@wazir-dev/cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +111 -0
- package/CHANGELOG.md +14 -0
- package/CONTRIBUTING.md +101 -0
- package/LICENSE +21 -0
- package/README.md +314 -0
- package/assets/composition-engine.mmd +34 -0
- package/assets/demo-script.sh +17 -0
- package/assets/logo-dark.svg +14 -0
- package/assets/logo.svg +14 -0
- package/assets/pipeline.mmd +39 -0
- package/assets/record-demo.sh +51 -0
- package/docs/README.md +51 -0
- package/docs/adapters/context-mode.md +60 -0
- package/docs/concepts/architecture.md +87 -0
- package/docs/concepts/artifact-model.md +60 -0
- package/docs/concepts/composition-engine.md +36 -0
- package/docs/concepts/indexing-and-recall.md +160 -0
- package/docs/concepts/observability.md +41 -0
- package/docs/concepts/roles-and-workflows.md +59 -0
- package/docs/concepts/terminology-policy.md +27 -0
- package/docs/getting-started/01-installation.md +78 -0
- package/docs/getting-started/02-first-run.md +102 -0
- package/docs/getting-started/03-adding-to-project.md +15 -0
- package/docs/getting-started/04-host-setup.md +15 -0
- package/docs/guides/ci-integration.md +15 -0
- package/docs/guides/creating-skills.md +15 -0
- package/docs/guides/expertise-module-authoring.md +15 -0
- package/docs/guides/hook-development.md +15 -0
- package/docs/guides/memory-and-learnings.md +34 -0
- package/docs/guides/multi-host-export.md +15 -0
- package/docs/guides/troubleshooting.md +101 -0
- package/docs/guides/writing-custom-roles.md +15 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-design.md +592 -0
- package/docs/plans/2026-03-15-cli-pipeline-integration-plan.md +598 -0
- package/docs/plans/2026-03-15-docs-enforcement-plan.md +238 -0
- package/docs/readmes/INDEX.md +99 -0
- package/docs/readmes/features/expertise/README.md +171 -0
- package/docs/readmes/features/exports/README.md +222 -0
- package/docs/readmes/features/hooks/README.md +103 -0
- package/docs/readmes/features/hooks/loop-cap-guard.md +133 -0
- package/docs/readmes/features/hooks/post-tool-capture.md +121 -0
- package/docs/readmes/features/hooks/post-tool-lint.md +130 -0
- package/docs/readmes/features/hooks/pre-compact-summary.md +122 -0
- package/docs/readmes/features/hooks/pre-tool-capture-route.md +100 -0
- package/docs/readmes/features/hooks/protected-path-write-guard.md +128 -0
- package/docs/readmes/features/hooks/session-start.md +119 -0
- package/docs/readmes/features/hooks/stop-handoff-harvest.md +125 -0
- package/docs/readmes/features/roles/README.md +157 -0
- package/docs/readmes/features/roles/clarifier.md +152 -0
- package/docs/readmes/features/roles/content-author.md +190 -0
- package/docs/readmes/features/roles/designer.md +193 -0
- package/docs/readmes/features/roles/executor.md +184 -0
- package/docs/readmes/features/roles/learner.md +210 -0
- package/docs/readmes/features/roles/planner.md +182 -0
- package/docs/readmes/features/roles/researcher.md +164 -0
- package/docs/readmes/features/roles/reviewer.md +184 -0
- package/docs/readmes/features/roles/specifier.md +162 -0
- package/docs/readmes/features/roles/verifier.md +215 -0
- package/docs/readmes/features/schemas/README.md +178 -0
- package/docs/readmes/features/skills/README.md +63 -0
- package/docs/readmes/features/skills/brainstorming.md +96 -0
- package/docs/readmes/features/skills/debugging.md +148 -0
- package/docs/readmes/features/skills/design.md +120 -0
- package/docs/readmes/features/skills/prepare-next.md +109 -0
- package/docs/readmes/features/skills/run-audit.md +159 -0
- package/docs/readmes/features/skills/scan-project.md +109 -0
- package/docs/readmes/features/skills/self-audit.md +176 -0
- package/docs/readmes/features/skills/tdd.md +137 -0
- package/docs/readmes/features/skills/using-skills.md +92 -0
- package/docs/readmes/features/skills/verification.md +120 -0
- package/docs/readmes/features/skills/writing-plans.md +104 -0
- package/docs/readmes/features/tooling/README.md +320 -0
- package/docs/readmes/features/workflows/README.md +186 -0
- package/docs/readmes/features/workflows/author.md +181 -0
- package/docs/readmes/features/workflows/clarify.md +154 -0
- package/docs/readmes/features/workflows/design-review.md +171 -0
- package/docs/readmes/features/workflows/design.md +169 -0
- package/docs/readmes/features/workflows/discover.md +162 -0
- package/docs/readmes/features/workflows/execute.md +173 -0
- package/docs/readmes/features/workflows/learn.md +167 -0
- package/docs/readmes/features/workflows/plan-review.md +165 -0
- package/docs/readmes/features/workflows/plan.md +170 -0
- package/docs/readmes/features/workflows/prepare-next.md +167 -0
- package/docs/readmes/features/workflows/review.md +169 -0
- package/docs/readmes/features/workflows/run-audit.md +191 -0
- package/docs/readmes/features/workflows/spec-challenge.md +159 -0
- package/docs/readmes/features/workflows/specify.md +160 -0
- package/docs/readmes/features/workflows/verify.md +177 -0
- package/docs/readmes/packages/README.md +50 -0
- package/docs/readmes/packages/ajv.md +117 -0
- package/docs/readmes/packages/context-mode.md +118 -0
- package/docs/readmes/packages/gray-matter.md +116 -0
- package/docs/readmes/packages/node-test.md +137 -0
- package/docs/readmes/packages/yaml.md +112 -0
- package/docs/reference/configuration-reference.md +159 -0
- package/docs/reference/expertise-index.md +52 -0
- package/docs/reference/git-flow.md +43 -0
- package/docs/reference/hooks.md +87 -0
- package/docs/reference/host-exports.md +50 -0
- package/docs/reference/launch-checklist.md +172 -0
- package/docs/reference/marketplace-listings.md +76 -0
- package/docs/reference/release-process.md +34 -0
- package/docs/reference/roles-reference.md +77 -0
- package/docs/reference/skills.md +33 -0
- package/docs/reference/templates.md +29 -0
- package/docs/reference/tooling-cli.md +94 -0
- package/docs/truth-claims.yaml +222 -0
- package/expertise/PROGRESS.md +63 -0
- package/expertise/README.md +18 -0
- package/expertise/antipatterns/PROGRESS.md +56 -0
- package/expertise/antipatterns/backend/api-design-antipatterns.md +1271 -0
- package/expertise/antipatterns/backend/auth-antipatterns.md +1195 -0
- package/expertise/antipatterns/backend/caching-antipatterns.md +622 -0
- package/expertise/antipatterns/backend/database-antipatterns.md +1038 -0
- package/expertise/antipatterns/backend/index.md +24 -0
- package/expertise/antipatterns/backend/microservices-antipatterns.md +850 -0
- package/expertise/antipatterns/code/architecture-antipatterns.md +919 -0
- package/expertise/antipatterns/code/async-antipatterns.md +622 -0
- package/expertise/antipatterns/code/code-smells.md +1186 -0
- package/expertise/antipatterns/code/dependency-antipatterns.md +1209 -0
- package/expertise/antipatterns/code/error-handling-antipatterns.md +1360 -0
- package/expertise/antipatterns/code/index.md +27 -0
- package/expertise/antipatterns/code/naming-and-abstraction.md +1118 -0
- package/expertise/antipatterns/code/state-management-antipatterns.md +1076 -0
- package/expertise/antipatterns/code/testing-antipatterns.md +1053 -0
- package/expertise/antipatterns/design/accessibility-antipatterns.md +1136 -0
- package/expertise/antipatterns/design/dark-patterns.md +1121 -0
- package/expertise/antipatterns/design/index.md +22 -0
- package/expertise/antipatterns/design/ui-antipatterns.md +1202 -0
- package/expertise/antipatterns/design/ux-antipatterns.md +680 -0
- package/expertise/antipatterns/frontend/css-layout-antipatterns.md +691 -0
- package/expertise/antipatterns/frontend/flutter-antipatterns.md +1827 -0
- package/expertise/antipatterns/frontend/index.md +23 -0
- package/expertise/antipatterns/frontend/mobile-antipatterns.md +573 -0
- package/expertise/antipatterns/frontend/react-antipatterns.md +1128 -0
- package/expertise/antipatterns/frontend/spa-antipatterns.md +1235 -0
- package/expertise/antipatterns/index.md +31 -0
- package/expertise/antipatterns/performance/index.md +20 -0
- package/expertise/antipatterns/performance/performance-antipatterns.md +1013 -0
- package/expertise/antipatterns/performance/premature-optimization.md +623 -0
- package/expertise/antipatterns/performance/scaling-antipatterns.md +785 -0
- package/expertise/antipatterns/process/ai-coding-antipatterns.md +853 -0
- package/expertise/antipatterns/process/code-review-antipatterns.md +656 -0
- package/expertise/antipatterns/process/deployment-antipatterns.md +920 -0
- package/expertise/antipatterns/process/index.md +23 -0
- package/expertise/antipatterns/process/technical-debt-antipatterns.md +647 -0
- package/expertise/antipatterns/security/index.md +20 -0
- package/expertise/antipatterns/security/secrets-antipatterns.md +849 -0
- package/expertise/antipatterns/security/security-theater.md +843 -0
- package/expertise/antipatterns/security/vulnerability-patterns.md +801 -0
- package/expertise/architecture/PROGRESS.md +70 -0
- package/expertise/architecture/data/caching-architecture.md +671 -0
- package/expertise/architecture/data/data-consistency.md +574 -0
- package/expertise/architecture/data/data-modeling.md +536 -0
- package/expertise/architecture/data/event-streams-and-queues.md +634 -0
- package/expertise/architecture/data/index.md +25 -0
- package/expertise/architecture/data/search-architecture.md +663 -0
- package/expertise/architecture/data/sql-vs-nosql.md +708 -0
- package/expertise/architecture/decisions/architecture-decision-records.md +640 -0
- package/expertise/architecture/decisions/build-vs-buy.md +616 -0
- package/expertise/architecture/decisions/index.md +23 -0
- package/expertise/architecture/decisions/monolith-to-microservices.md +790 -0
- package/expertise/architecture/decisions/technology-selection.md +616 -0
- package/expertise/architecture/distributed/cap-theorem-and-tradeoffs.md +800 -0
- package/expertise/architecture/distributed/circuit-breaker-bulkhead.md +741 -0
- package/expertise/architecture/distributed/consensus-and-coordination.md +796 -0
- package/expertise/architecture/distributed/distributed-systems-fundamentals.md +564 -0
- package/expertise/architecture/distributed/idempotency-and-retry.md +796 -0
- package/expertise/architecture/distributed/index.md +25 -0
- package/expertise/architecture/distributed/saga-pattern.md +797 -0
- package/expertise/architecture/foundations/architectural-thinking.md +460 -0
- package/expertise/architecture/foundations/coupling-and-cohesion.md +770 -0
- package/expertise/architecture/foundations/design-principles-solid.md +649 -0
- package/expertise/architecture/foundations/domain-driven-design.md +719 -0
- package/expertise/architecture/foundations/index.md +25 -0
- package/expertise/architecture/foundations/separation-of-concerns.md +472 -0
- package/expertise/architecture/foundations/twelve-factor-app.md +797 -0
- package/expertise/architecture/index.md +34 -0
- package/expertise/architecture/integration/api-design-graphql.md +638 -0
- package/expertise/architecture/integration/api-design-grpc.md +804 -0
- package/expertise/architecture/integration/api-design-rest.md +892 -0
- package/expertise/architecture/integration/index.md +25 -0
- package/expertise/architecture/integration/third-party-integration.md +795 -0
- package/expertise/architecture/integration/webhooks-and-callbacks.md +1152 -0
- package/expertise/architecture/integration/websockets-realtime.md +791 -0
- package/expertise/architecture/mobile-architecture/index.md +22 -0
- package/expertise/architecture/mobile-architecture/mobile-app-architecture.md +780 -0
- package/expertise/architecture/mobile-architecture/mobile-backend-for-frontend.md +670 -0
- package/expertise/architecture/mobile-architecture/offline-first.md +719 -0
- package/expertise/architecture/mobile-architecture/push-and-sync.md +782 -0
- package/expertise/architecture/patterns/cqrs-event-sourcing.md +717 -0
- package/expertise/architecture/patterns/event-driven.md +797 -0
- package/expertise/architecture/patterns/hexagonal-clean-architecture.md +870 -0
- package/expertise/architecture/patterns/index.md +27 -0
- package/expertise/architecture/patterns/layered-architecture.md +736 -0
- package/expertise/architecture/patterns/microservices.md +753 -0
- package/expertise/architecture/patterns/modular-monolith.md +692 -0
- package/expertise/architecture/patterns/monolith.md +626 -0
- package/expertise/architecture/patterns/plugin-architecture.md +735 -0
- package/expertise/architecture/patterns/serverless.md +780 -0
- package/expertise/architecture/scaling/database-scaling.md +615 -0
- package/expertise/architecture/scaling/feature-flags-and-rollouts.md +757 -0
- package/expertise/architecture/scaling/horizontal-vs-vertical.md +606 -0
- package/expertise/architecture/scaling/index.md +24 -0
- package/expertise/architecture/scaling/multi-tenancy.md +800 -0
- package/expertise/architecture/scaling/stateless-design.md +787 -0
- package/expertise/backend/embedded-firmware.md +625 -0
- package/expertise/backend/go.md +853 -0
- package/expertise/backend/index.md +24 -0
- package/expertise/backend/java-spring.md +448 -0
- package/expertise/backend/node-typescript.md +625 -0
- package/expertise/backend/python-fastapi.md +724 -0
- package/expertise/backend/rust.md +458 -0
- package/expertise/backend/solidity.md +711 -0
- package/expertise/composition-map.yaml +443 -0
- package/expertise/content/foundations/content-modeling.md +395 -0
- package/expertise/content/foundations/editorial-standards.md +449 -0
- package/expertise/content/foundations/index.md +24 -0
- package/expertise/content/foundations/microcopy.md +455 -0
- package/expertise/content/foundations/terminology-governance.md +509 -0
- package/expertise/content/index.md +34 -0
- package/expertise/content/patterns/accessibility-copy.md +518 -0
- package/expertise/content/patterns/index.md +24 -0
- package/expertise/content/patterns/notification-content.md +433 -0
- package/expertise/content/patterns/sample-content.md +486 -0
- package/expertise/content/patterns/state-copy.md +439 -0
- package/expertise/design/PROGRESS.md +58 -0
- package/expertise/design/disciplines/dark-mode-theming.md +577 -0
- package/expertise/design/disciplines/design-systems.md +595 -0
- package/expertise/design/disciplines/index.md +25 -0
- package/expertise/design/disciplines/information-architecture.md +800 -0
- package/expertise/design/disciplines/interaction-design.md +788 -0
- package/expertise/design/disciplines/responsive-design.md +552 -0
- package/expertise/design/disciplines/usability-testing.md +516 -0
- package/expertise/design/disciplines/user-research.md +792 -0
- package/expertise/design/foundations/accessibility-design.md +796 -0
- package/expertise/design/foundations/color-theory.md +797 -0
- package/expertise/design/foundations/iconography.md +795 -0
- package/expertise/design/foundations/index.md +26 -0
- package/expertise/design/foundations/motion-and-animation.md +653 -0
- package/expertise/design/foundations/rtl-design.md +585 -0
- package/expertise/design/foundations/spacing-and-layout.md +607 -0
- package/expertise/design/foundations/typography.md +800 -0
- package/expertise/design/foundations/visual-hierarchy.md +761 -0
- package/expertise/design/index.md +32 -0
- package/expertise/design/patterns/authentication-flows.md +474 -0
- package/expertise/design/patterns/content-consumption.md +789 -0
- package/expertise/design/patterns/data-display.md +618 -0
- package/expertise/design/patterns/e-commerce.md +1494 -0
- package/expertise/design/patterns/feedback-and-states.md +642 -0
- package/expertise/design/patterns/forms-and-input.md +819 -0
- package/expertise/design/patterns/gamification.md +801 -0
- package/expertise/design/patterns/index.md +31 -0
- package/expertise/design/patterns/microinteractions.md +449 -0
- package/expertise/design/patterns/navigation.md +800 -0
- package/expertise/design/patterns/notifications.md +705 -0
- package/expertise/design/patterns/onboarding.md +700 -0
- package/expertise/design/patterns/search-and-filter.md +601 -0
- package/expertise/design/patterns/settings-and-preferences.md +768 -0
- package/expertise/design/patterns/social-and-community.md +748 -0
- package/expertise/design/platforms/desktop-native.md +612 -0
- package/expertise/design/platforms/index.md +25 -0
- package/expertise/design/platforms/mobile-android.md +825 -0
- package/expertise/design/platforms/mobile-cross-platform.md +983 -0
- package/expertise/design/platforms/mobile-ios.md +699 -0
- package/expertise/design/platforms/tablet.md +794 -0
- package/expertise/design/platforms/web-dashboard.md +790 -0
- package/expertise/design/platforms/web-responsive.md +550 -0
- package/expertise/design/psychology/behavioral-nudges.md +449 -0
- package/expertise/design/psychology/cognitive-load.md +1191 -0
- package/expertise/design/psychology/error-psychology.md +778 -0
- package/expertise/design/psychology/index.md +22 -0
- package/expertise/design/psychology/persuasive-design.md +736 -0
- package/expertise/design/psychology/user-mental-models.md +623 -0
- package/expertise/design/tooling/open-pencil.md +266 -0
- package/expertise/frontend/angular.md +1073 -0
- package/expertise/frontend/desktop-electron.md +546 -0
- package/expertise/frontend/flutter.md +782 -0
- package/expertise/frontend/index.md +27 -0
- package/expertise/frontend/native-android.md +409 -0
- package/expertise/frontend/native-ios.md +490 -0
- package/expertise/frontend/react-native.md +1160 -0
- package/expertise/frontend/react.md +808 -0
- package/expertise/frontend/vue.md +1089 -0
- package/expertise/humanize/domain-rules-code.md +79 -0
- package/expertise/humanize/domain-rules-content.md +67 -0
- package/expertise/humanize/domain-rules-technical-docs.md +56 -0
- package/expertise/humanize/index.md +35 -0
- package/expertise/humanize/self-audit-checklist.md +87 -0
- package/expertise/humanize/sentence-patterns.md +218 -0
- package/expertise/humanize/vocabulary-blacklist.md +105 -0
- package/expertise/i18n/PROGRESS.md +65 -0
- package/expertise/i18n/advanced/accessibility-and-i18n.md +28 -0
- package/expertise/i18n/advanced/bidirectional-text-algorithm.md +38 -0
- package/expertise/i18n/advanced/complex-scripts.md +30 -0
- package/expertise/i18n/advanced/performance-and-i18n.md +27 -0
- package/expertise/i18n/advanced/testing-i18n.md +28 -0
- package/expertise/i18n/content/content-adaptation.md +23 -0
- package/expertise/i18n/content/locale-specific-formatting.md +23 -0
- package/expertise/i18n/content/machine-translation-integration.md +28 -0
- package/expertise/i18n/content/translation-management.md +29 -0
- package/expertise/i18n/foundations/date-time-calendars.md +67 -0
- package/expertise/i18n/foundations/i18n-architecture.md +272 -0
- package/expertise/i18n/foundations/locale-and-language-tags.md +79 -0
- package/expertise/i18n/foundations/numbers-currency-units.md +61 -0
- package/expertise/i18n/foundations/pluralization-and-gender.md +109 -0
- package/expertise/i18n/foundations/string-externalization.md +236 -0
- package/expertise/i18n/foundations/text-direction-bidi.md +241 -0
- package/expertise/i18n/foundations/unicode-and-encoding.md +86 -0
- package/expertise/i18n/index.md +38 -0
- package/expertise/i18n/platform/backend-i18n.md +31 -0
- package/expertise/i18n/platform/flutter-i18n.md +148 -0
- package/expertise/i18n/platform/native-android-i18n.md +36 -0
- package/expertise/i18n/platform/native-ios-i18n.md +36 -0
- package/expertise/i18n/platform/react-i18n.md +103 -0
- package/expertise/i18n/platform/web-css-i18n.md +81 -0
- package/expertise/i18n/rtl/arabic-specific.md +175 -0
- package/expertise/i18n/rtl/hebrew-specific.md +149 -0
- package/expertise/i18n/rtl/rtl-animations-and-transitions.md +111 -0
- package/expertise/i18n/rtl/rtl-forms-and-input.md +161 -0
- package/expertise/i18n/rtl/rtl-fundamentals.md +211 -0
- package/expertise/i18n/rtl/rtl-icons-and-images.md +181 -0
- package/expertise/i18n/rtl/rtl-layout-mirroring.md +252 -0
- package/expertise/i18n/rtl/rtl-navigation-and-gestures.md +107 -0
- package/expertise/i18n/rtl/rtl-testing-and-qa.md +147 -0
- package/expertise/i18n/rtl/rtl-typography.md +160 -0
- package/expertise/index.md +113 -0
- package/expertise/index.yaml +216 -0
- package/expertise/infrastructure/cloud-aws.md +597 -0
- package/expertise/infrastructure/cloud-gcp.md +599 -0
- package/expertise/infrastructure/cybersecurity.md +816 -0
- package/expertise/infrastructure/database-mongodb.md +447 -0
- package/expertise/infrastructure/database-postgres.md +400 -0
- package/expertise/infrastructure/devops-cicd.md +787 -0
- package/expertise/infrastructure/index.md +27 -0
- package/expertise/performance/PROGRESS.md +50 -0
- package/expertise/performance/backend/api-latency.md +1204 -0
- package/expertise/performance/backend/background-jobs.md +506 -0
- package/expertise/performance/backend/connection-pooling.md +1209 -0
- package/expertise/performance/backend/database-query-optimization.md +515 -0
- package/expertise/performance/backend/index.md +23 -0
- package/expertise/performance/backend/rate-limiting-and-throttling.md +971 -0
- package/expertise/performance/foundations/algorithmic-complexity.md +954 -0
- package/expertise/performance/foundations/caching-strategies.md +489 -0
- package/expertise/performance/foundations/concurrency-and-parallelism.md +847 -0
- package/expertise/performance/foundations/index.md +24 -0
- package/expertise/performance/foundations/measuring-and-profiling.md +440 -0
- package/expertise/performance/foundations/memory-management.md +964 -0
- package/expertise/performance/foundations/performance-budgets.md +1314 -0
- package/expertise/performance/index.md +31 -0
- package/expertise/performance/infrastructure/auto-scaling.md +1059 -0
- package/expertise/performance/infrastructure/cdn-and-edge.md +1081 -0
- package/expertise/performance/infrastructure/index.md +22 -0
- package/expertise/performance/infrastructure/load-balancing.md +1081 -0
- package/expertise/performance/infrastructure/observability.md +1079 -0
- package/expertise/performance/mobile/index.md +23 -0
- package/expertise/performance/mobile/mobile-animations.md +544 -0
- package/expertise/performance/mobile/mobile-memory-battery.md +416 -0
- package/expertise/performance/mobile/mobile-network.md +452 -0
- package/expertise/performance/mobile/mobile-rendering.md +599 -0
- package/expertise/performance/mobile/mobile-startup-time.md +505 -0
- package/expertise/performance/platform-specific/flutter-performance.md +647 -0
- package/expertise/performance/platform-specific/index.md +22 -0
- package/expertise/performance/platform-specific/node-performance.md +1307 -0
- package/expertise/performance/platform-specific/postgres-performance.md +1366 -0
- package/expertise/performance/platform-specific/react-performance.md +1403 -0
- package/expertise/performance/web/bundle-optimization.md +1239 -0
- package/expertise/performance/web/image-and-media.md +636 -0
- package/expertise/performance/web/index.md +24 -0
- package/expertise/performance/web/network-optimization.md +1133 -0
- package/expertise/performance/web/rendering-performance.md +1098 -0
- package/expertise/performance/web/ssr-and-hydration.md +918 -0
- package/expertise/performance/web/web-vitals.md +1374 -0
- package/expertise/quality/accessibility.md +985 -0
- package/expertise/quality/evidence-based-verification.md +499 -0
- package/expertise/quality/index.md +24 -0
- package/expertise/quality/ml-model-audit.md +614 -0
- package/expertise/quality/performance.md +600 -0
- package/expertise/quality/testing-api.md +891 -0
- package/expertise/quality/testing-mobile.md +496 -0
- package/expertise/quality/testing-web.md +849 -0
- package/expertise/security/PROGRESS.md +54 -0
- package/expertise/security/agentic-identity.md +540 -0
- package/expertise/security/compliance-frameworks.md +601 -0
- package/expertise/security/data/data-encryption.md +364 -0
- package/expertise/security/data/data-privacy-gdpr.md +692 -0
- package/expertise/security/data/database-security.md +1171 -0
- package/expertise/security/data/index.md +22 -0
- package/expertise/security/data/pii-handling.md +531 -0
- package/expertise/security/foundations/authentication.md +1041 -0
- package/expertise/security/foundations/authorization.md +603 -0
- package/expertise/security/foundations/cryptography.md +1001 -0
- package/expertise/security/foundations/index.md +25 -0
- package/expertise/security/foundations/owasp-top-10.md +1354 -0
- package/expertise/security/foundations/secrets-management.md +1217 -0
- package/expertise/security/foundations/secure-sdlc.md +700 -0
- package/expertise/security/foundations/supply-chain-security.md +698 -0
- package/expertise/security/index.md +31 -0
- package/expertise/security/infrastructure/cloud-security-aws.md +1296 -0
- package/expertise/security/infrastructure/cloud-security-gcp.md +1376 -0
- package/expertise/security/infrastructure/container-security.md +721 -0
- package/expertise/security/infrastructure/incident-response.md +1295 -0
- package/expertise/security/infrastructure/index.md +24 -0
- package/expertise/security/infrastructure/logging-and-monitoring.md +1618 -0
- package/expertise/security/infrastructure/network-security.md +1337 -0
- package/expertise/security/mobile/index.md +23 -0
- package/expertise/security/mobile/mobile-android-security.md +1218 -0
- package/expertise/security/mobile/mobile-binary-protection.md +1229 -0
- package/expertise/security/mobile/mobile-data-storage.md +1265 -0
- package/expertise/security/mobile/mobile-ios-security.md +1401 -0
- package/expertise/security/mobile/mobile-network-security.md +1520 -0
- package/expertise/security/smart-contract-security.md +594 -0
- package/expertise/security/testing/index.md +22 -0
- package/expertise/security/testing/penetration-testing.md +1258 -0
- package/expertise/security/testing/security-code-review.md +1765 -0
- package/expertise/security/testing/threat-modeling.md +1074 -0
- package/expertise/security/testing/vulnerability-scanning.md +1062 -0
- package/expertise/security/web/api-security.md +586 -0
- package/expertise/security/web/cors-and-headers.md +433 -0
- package/expertise/security/web/csrf.md +562 -0
- package/expertise/security/web/file-upload.md +1477 -0
- package/expertise/security/web/index.md +25 -0
- package/expertise/security/web/injection.md +1375 -0
- package/expertise/security/web/session-management.md +1101 -0
- package/expertise/security/web/xss.md +1158 -0
- package/exports/README.md +17 -0
- package/exports/hosts/claude/.claude/agents/clarifier.md +42 -0
- package/exports/hosts/claude/.claude/agents/content-author.md +63 -0
- package/exports/hosts/claude/.claude/agents/designer.md +55 -0
- package/exports/hosts/claude/.claude/agents/executor.md +55 -0
- package/exports/hosts/claude/.claude/agents/learner.md +51 -0
- package/exports/hosts/claude/.claude/agents/planner.md +53 -0
- package/exports/hosts/claude/.claude/agents/researcher.md +43 -0
- package/exports/hosts/claude/.claude/agents/reviewer.md +54 -0
- package/exports/hosts/claude/.claude/agents/specifier.md +47 -0
- package/exports/hosts/claude/.claude/agents/verifier.md +71 -0
- package/exports/hosts/claude/.claude/commands/author.md +42 -0
- package/exports/hosts/claude/.claude/commands/clarify.md +38 -0
- package/exports/hosts/claude/.claude/commands/design-review.md +46 -0
- package/exports/hosts/claude/.claude/commands/design.md +44 -0
- package/exports/hosts/claude/.claude/commands/discover.md +37 -0
- package/exports/hosts/claude/.claude/commands/execute.md +48 -0
- package/exports/hosts/claude/.claude/commands/learn.md +38 -0
- package/exports/hosts/claude/.claude/commands/plan-review.md +42 -0
- package/exports/hosts/claude/.claude/commands/plan.md +39 -0
- package/exports/hosts/claude/.claude/commands/prepare-next.md +37 -0
- package/exports/hosts/claude/.claude/commands/review.md +40 -0
- package/exports/hosts/claude/.claude/commands/run-audit.md +41 -0
- package/exports/hosts/claude/.claude/commands/spec-challenge.md +41 -0
- package/exports/hosts/claude/.claude/commands/specify.md +38 -0
- package/exports/hosts/claude/.claude/commands/verify.md +37 -0
- package/exports/hosts/claude/.claude/settings.json +34 -0
- package/exports/hosts/claude/CLAUDE.md +19 -0
- package/exports/hosts/claude/export.manifest.json +38 -0
- package/exports/hosts/claude/host-package.json +67 -0
- package/exports/hosts/codex/AGENTS.md +19 -0
- package/exports/hosts/codex/export.manifest.json +38 -0
- package/exports/hosts/codex/host-package.json +41 -0
- package/exports/hosts/cursor/.cursor/hooks.json +16 -0
- package/exports/hosts/cursor/.cursor/rules/wazir-core.mdc +19 -0
- package/exports/hosts/cursor/export.manifest.json +38 -0
- package/exports/hosts/cursor/host-package.json +42 -0
- package/exports/hosts/gemini/GEMINI.md +19 -0
- package/exports/hosts/gemini/export.manifest.json +38 -0
- package/exports/hosts/gemini/host-package.json +41 -0
- package/hooks/README.md +18 -0
- package/hooks/definitions/loop_cap_guard.yaml +21 -0
- package/hooks/definitions/post_tool_capture.yaml +24 -0
- package/hooks/definitions/pre_compact_summary.yaml +19 -0
- package/hooks/definitions/pre_tool_capture_route.yaml +19 -0
- package/hooks/definitions/protected_path_write_guard.yaml +19 -0
- package/hooks/definitions/session_start.yaml +19 -0
- package/hooks/definitions/stop_handoff_harvest.yaml +20 -0
- package/hooks/loop-cap-guard +17 -0
- package/hooks/post-tool-lint +36 -0
- package/hooks/protected-path-write-guard +17 -0
- package/hooks/session-start +41 -0
- package/llms-full.txt +2355 -0
- package/llms.txt +43 -0
- package/package.json +79 -0
- package/roles/README.md +20 -0
- package/roles/clarifier.md +42 -0
- package/roles/content-author.md +63 -0
- package/roles/designer.md +55 -0
- package/roles/executor.md +55 -0
- package/roles/learner.md +51 -0
- package/roles/planner.md +53 -0
- package/roles/researcher.md +43 -0
- package/roles/reviewer.md +54 -0
- package/roles/specifier.md +47 -0
- package/roles/verifier.md +71 -0
- package/schemas/README.md +24 -0
- package/schemas/accepted-learning.schema.json +20 -0
- package/schemas/author-artifact.schema.json +156 -0
- package/schemas/clarification.schema.json +19 -0
- package/schemas/design-artifact.schema.json +80 -0
- package/schemas/docs-claim.schema.json +18 -0
- package/schemas/export-manifest.schema.json +20 -0
- package/schemas/hook.schema.json +67 -0
- package/schemas/host-export-package.schema.json +18 -0
- package/schemas/implementation-plan.schema.json +19 -0
- package/schemas/proposed-learning.schema.json +19 -0
- package/schemas/research.schema.json +18 -0
- package/schemas/review.schema.json +29 -0
- package/schemas/run-manifest.schema.json +18 -0
- package/schemas/spec-challenge.schema.json +18 -0
- package/schemas/spec.schema.json +20 -0
- package/schemas/usage.schema.json +102 -0
- package/schemas/verification-proof.schema.json +29 -0
- package/schemas/wazir-manifest.schema.json +173 -0
- package/skills/README.md +40 -0
- package/skills/brainstorming/SKILL.md +77 -0
- package/skills/debugging/SKILL.md +50 -0
- package/skills/design/SKILL.md +61 -0
- package/skills/dispatching-parallel-agents/SKILL.md +128 -0
- package/skills/executing-plans/SKILL.md +70 -0
- package/skills/finishing-a-development-branch/SKILL.md +169 -0
- package/skills/humanize/SKILL.md +123 -0
- package/skills/init-pipeline/SKILL.md +124 -0
- package/skills/prepare-next/SKILL.md +20 -0
- package/skills/receiving-code-review/SKILL.md +123 -0
- package/skills/requesting-code-review/SKILL.md +105 -0
- package/skills/requesting-code-review/code-reviewer.md +108 -0
- package/skills/run-audit/SKILL.md +197 -0
- package/skills/scan-project/SKILL.md +41 -0
- package/skills/self-audit/SKILL.md +153 -0
- package/skills/subagent-driven-development/SKILL.md +154 -0
- package/skills/subagent-driven-development/code-quality-reviewer-prompt.md +26 -0
- package/skills/subagent-driven-development/implementer-prompt.md +102 -0
- package/skills/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/skills/tdd/SKILL.md +23 -0
- package/skills/using-git-worktrees/SKILL.md +163 -0
- package/skills/using-skills/SKILL.md +95 -0
- package/skills/verification/SKILL.md +22 -0
- package/skills/wazir/SKILL.md +463 -0
- package/skills/writing-plans/SKILL.md +30 -0
- package/skills/writing-skills/SKILL.md +157 -0
- package/skills/writing-skills/anthropic-best-practices.md +122 -0
- package/skills/writing-skills/persuasion-principles.md +50 -0
- package/templates/README.md +20 -0
- package/templates/artifacts/README.md +10 -0
- package/templates/artifacts/accepted-learning.md +19 -0
- package/templates/artifacts/accepted-learning.template.json +12 -0
- package/templates/artifacts/author.md +74 -0
- package/templates/artifacts/author.template.json +19 -0
- package/templates/artifacts/clarification.md +21 -0
- package/templates/artifacts/clarification.template.json +12 -0
- package/templates/artifacts/execute-notes.md +19 -0
- package/templates/artifacts/implementation-plan.md +21 -0
- package/templates/artifacts/implementation-plan.template.json +11 -0
- package/templates/artifacts/learning-proposal.md +19 -0
- package/templates/artifacts/next-run-handoff.md +21 -0
- package/templates/artifacts/plan-review.md +19 -0
- package/templates/artifacts/proposed-learning.template.json +12 -0
- package/templates/artifacts/research.md +21 -0
- package/templates/artifacts/research.template.json +12 -0
- package/templates/artifacts/review-findings.md +19 -0
- package/templates/artifacts/review.template.json +11 -0
- package/templates/artifacts/run-manifest.template.json +8 -0
- package/templates/artifacts/spec-challenge.md +19 -0
- package/templates/artifacts/spec-challenge.template.json +11 -0
- package/templates/artifacts/spec.md +21 -0
- package/templates/artifacts/spec.template.json +12 -0
- package/templates/artifacts/verification-proof.md +19 -0
- package/templates/artifacts/verification-proof.template.json +11 -0
- package/templates/examples/accepted-learning.example.json +14 -0
- package/templates/examples/author.example.json +152 -0
- package/templates/examples/clarification.example.json +15 -0
- package/templates/examples/docs-claim.example.json +8 -0
- package/templates/examples/export-manifest.example.json +7 -0
- package/templates/examples/host-export-package.example.json +11 -0
- package/templates/examples/implementation-plan.example.json +17 -0
- package/templates/examples/proposed-learning.example.json +13 -0
- package/templates/examples/research.example.json +15 -0
- package/templates/examples/research.example.md +6 -0
- package/templates/examples/review.example.json +17 -0
- package/templates/examples/run-manifest.example.json +9 -0
- package/templates/examples/spec-challenge.example.json +14 -0
- package/templates/examples/spec.example.json +21 -0
- package/templates/examples/verification-proof.example.json +21 -0
- package/templates/examples/wazir-manifest.example.yaml +65 -0
- package/templates/task-definition-schema.md +99 -0
- package/tooling/README.md +20 -0
- package/tooling/src/adapters/context-mode.js +50 -0
- package/tooling/src/capture/command.js +376 -0
- package/tooling/src/capture/store.js +99 -0
- package/tooling/src/capture/usage.js +270 -0
- package/tooling/src/checks/branches.js +50 -0
- package/tooling/src/checks/brand-truth.js +110 -0
- package/tooling/src/checks/changelog.js +231 -0
- package/tooling/src/checks/command-registry.js +36 -0
- package/tooling/src/checks/commits.js +102 -0
- package/tooling/src/checks/docs-drift.js +103 -0
- package/tooling/src/checks/docs-truth.js +201 -0
- package/tooling/src/checks/runtime-surface.js +156 -0
- package/tooling/src/cli.js +116 -0
- package/tooling/src/command-options.js +56 -0
- package/tooling/src/commands/validate.js +320 -0
- package/tooling/src/doctor/command.js +91 -0
- package/tooling/src/export/command.js +77 -0
- package/tooling/src/export/compiler.js +498 -0
- package/tooling/src/guards/loop-cap-guard.js +52 -0
- package/tooling/src/guards/protected-path-write-guard.js +67 -0
- package/tooling/src/index/command.js +152 -0
- package/tooling/src/index/storage.js +1061 -0
- package/tooling/src/index/summarizers.js +261 -0
- package/tooling/src/loaders.js +18 -0
- package/tooling/src/project-root.js +22 -0
- package/tooling/src/recall/command.js +225 -0
- package/tooling/src/schema-validator.js +30 -0
- package/tooling/src/state-root.js +40 -0
- package/tooling/src/status/command.js +71 -0
- package/wazir.manifest.yaml +135 -0
- package/workflows/README.md +19 -0
- package/workflows/author.md +42 -0
- package/workflows/clarify.md +38 -0
- package/workflows/design-review.md +46 -0
- package/workflows/design.md +44 -0
- package/workflows/discover.md +37 -0
- package/workflows/execute.md +48 -0
- package/workflows/learn.md +38 -0
- package/workflows/plan-review.md +42 -0
- package/workflows/plan.md +39 -0
- package/workflows/prepare-next.md +37 -0
- package/workflows/review.md +40 -0
- package/workflows/run-audit.md +41 -0
- package/workflows/spec-challenge.md +41 -0
- package/workflows/specify.md +38 -0
- package/workflows/verify.md +37 -0
|
@@ -0,0 +1,1073 @@
|
|
|
1
|
+
# Angular — Expertise Module
|
|
2
|
+
|
|
3
|
+
> An Angular specialist designs, builds, and maintains web applications using Angular (v17+),
|
|
4
|
+
> leveraging standalone components, signal-based reactivity, and the modern Angular toolchain.
|
|
5
|
+
> Scope spans component architecture, state management, performance optimization, SSR, security, and DevOps.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Core Patterns & Conventions
|
|
10
|
+
|
|
11
|
+
### Project Structure
|
|
12
|
+
|
|
13
|
+
**Standalone-first (Angular 19+ default):**
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
src/
|
|
17
|
+
app/
|
|
18
|
+
app.component.ts # Root standalone component
|
|
19
|
+
app.config.ts # provideRouter, provideHttpClient, etc.
|
|
20
|
+
app.routes.ts # Top-level route config
|
|
21
|
+
core/ # Singletons: auth, error handler, interceptors
|
|
22
|
+
interceptors/
|
|
23
|
+
guards/
|
|
24
|
+
services/
|
|
25
|
+
shared/ # Reusable pipes, directives, UI primitives
|
|
26
|
+
components/
|
|
27
|
+
directives/
|
|
28
|
+
pipes/
|
|
29
|
+
features/
|
|
30
|
+
dashboard/
|
|
31
|
+
dashboard.component.ts
|
|
32
|
+
dashboard.routes.ts
|
|
33
|
+
components/ # Dumb/presentational components
|
|
34
|
+
services/ # Feature-scoped services
|
|
35
|
+
models/
|
|
36
|
+
users/
|
|
37
|
+
...
|
|
38
|
+
layouts/ # Shell components (sidebar, header)
|
|
39
|
+
environments/
|
|
40
|
+
environment.ts
|
|
41
|
+
environment.prod.ts
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Nx Monorepo (enterprise):**
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
apps/
|
|
48
|
+
web-app/
|
|
49
|
+
admin-app/
|
|
50
|
+
libs/
|
|
51
|
+
shared/ui/ # Reusable UI components
|
|
52
|
+
shared/data-access/ # HTTP services, stores
|
|
53
|
+
shared/util/ # Pure utility functions
|
|
54
|
+
feature-dashboard/ # Feature library
|
|
55
|
+
feature-auth/
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Nx enforces module boundaries via `@nx/enforce-module-boundaries` lint rule. Library categories: `feature`, `data-access`, `ui`, `util`, `shell`.
|
|
59
|
+
|
|
60
|
+
### Naming Conventions (Angular Style Guide)
|
|
61
|
+
|
|
62
|
+
| Artifact | Pattern | Example |
|
|
63
|
+
|-------------------|------------------------------------|--------------------------------|
|
|
64
|
+
| Component | `feature-name.component.ts` | `user-list.component.ts` |
|
|
65
|
+
| Service | `feature-name.service.ts` | `auth.service.ts` |
|
|
66
|
+
| Directive | `feature-name.directive.ts` | `highlight.directive.ts` |
|
|
67
|
+
| Pipe | `feature-name.pipe.ts` | `truncate.pipe.ts` |
|
|
68
|
+
| Guard | `feature-name.guard.ts` | `auth.guard.ts` |
|
|
69
|
+
| Interceptor | `feature-name.interceptor.ts` | `error.interceptor.ts` |
|
|
70
|
+
| Interface/Model | `feature-name.model.ts` | `user.model.ts` |
|
|
71
|
+
| Route config | `feature-name.routes.ts` | `dashboard.routes.ts` |
|
|
72
|
+
|
|
73
|
+
- Use `kebab-case` for file names, `PascalCase` for classes, `camelCase` for methods/properties.
|
|
74
|
+
- Prefix selectors: `app-` for app components, `shared-` for shared library components.
|
|
75
|
+
- One class per file. Max 400 lines per component file — split if larger.
|
|
76
|
+
|
|
77
|
+
### Architecture Patterns
|
|
78
|
+
|
|
79
|
+
**Smart (Container) vs Dumb (Presentational) Components:**
|
|
80
|
+
|
|
81
|
+
- **Smart components**: inject services, manage state, dispatch actions, handle routing. Located in `features/<name>/`.
|
|
82
|
+
- **Dumb components**: receive data via `input()`, emit events via `output()`. Located in `features/<name>/components/` or `shared/components/`.
|
|
83
|
+
- Dumb components must have zero service dependencies and `changeDetection: ChangeDetectionStrategy.OnPush`.
|
|
84
|
+
|
|
85
|
+
**Feature-based architecture**: group all related code (components, services, routes, models) inside a single feature directory. Each feature owns its own routes file and lazy-loads independently.
|
|
86
|
+
|
|
87
|
+
### Signal-Based Reactivity (Angular 17+)
|
|
88
|
+
|
|
89
|
+
Signals are Angular's synchronous, fine-grained reactivity primitive. As of Angular 19-20, signals, `computed()`, `effect()`, `linkedSignal()`, `input()`, `output()`, `viewChild()`, and `contentChild()` are all stable.
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// Signal basics
|
|
93
|
+
count = signal(0);
|
|
94
|
+
doubleCount = computed(() => this.count() * 2);
|
|
95
|
+
|
|
96
|
+
// Signal inputs (replacing @Input)
|
|
97
|
+
name = input<string>(); // optional
|
|
98
|
+
name = input.required<string>(); // required
|
|
99
|
+
name = input('default'); // with default
|
|
100
|
+
|
|
101
|
+
// Signal outputs (replacing @Output)
|
|
102
|
+
clicked = output<MouseEvent>();
|
|
103
|
+
|
|
104
|
+
// Model inputs (two-way binding)
|
|
105
|
+
value = model<string>(''); // parent binds with [(value)]
|
|
106
|
+
|
|
107
|
+
// Signal queries (replacing @ViewChild/@ContentChild)
|
|
108
|
+
myRef = viewChild<ElementRef>('myRef');
|
|
109
|
+
items = contentChildren(ItemComponent);
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**`effect()`** runs side effects when signals change. Use sparingly — prefer `computed()` for derived state:
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
effect(() => {
|
|
116
|
+
console.log(`Count changed: ${this.count()}`);
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
**`linkedSignal()`** (Angular 19+): a writable signal that resets when a source changes:
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
selectedIndex = linkedSignal({
|
|
124
|
+
source: this.items,
|
|
125
|
+
computation: () => 0 // reset to 0 when items change
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**`resource()` / `rxResource()`** (Angular 19+): declarative async data fetching tied to signals:
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
userData = rxResource({
|
|
133
|
+
request: () => ({ id: this.userId() }),
|
|
134
|
+
loader: ({ request }) => this.http.get<User>(`/api/users/${request.id}`)
|
|
135
|
+
});
|
|
136
|
+
// userData.value(), userData.isLoading(), userData.error()
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### State Management
|
|
140
|
+
|
|
141
|
+
**Tier 1 — Local component state**: plain `signal()` + `computed()`. Sufficient for most components.
|
|
142
|
+
|
|
143
|
+
**Tier 2 — Shared feature state**: injectable service with signals:
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
@Injectable({ providedIn: 'root' })
|
|
147
|
+
export class CartService {
|
|
148
|
+
private readonly _items = signal<CartItem[]>([]);
|
|
149
|
+
readonly items = this._items.asReadonly();
|
|
150
|
+
readonly total = computed(() =>
|
|
151
|
+
this._items().reduce((sum, i) => sum + i.price * i.qty, 0)
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
addItem(item: CartItem) {
|
|
155
|
+
this._items.update(items => [...items, item]);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Tier 3 — NgRx SignalStore** (for complex shared state with plugins):
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
export const ProductStore = signalStore(
|
|
164
|
+
{ providedIn: 'root' },
|
|
165
|
+
withState({ products: [] as Product[], loading: false }),
|
|
166
|
+
withComputed(({ products }) => ({
|
|
167
|
+
count: computed(() => products().length),
|
|
168
|
+
})),
|
|
169
|
+
withMethods((store, http = inject(HttpClient)) => ({
|
|
170
|
+
loadProducts: rxMethod<void>(
|
|
171
|
+
pipe(
|
|
172
|
+
tap(() => patchState(store, { loading: true })),
|
|
173
|
+
switchMap(() => http.get<Product[]>('/api/products')),
|
|
174
|
+
tap(products => patchState(store, { products, loading: false }))
|
|
175
|
+
)
|
|
176
|
+
),
|
|
177
|
+
}))
|
|
178
|
+
);
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Tier 4 — NgRx Store** (global Redux pattern): use only when you need DevTools time-travel, action logging across the entire app, or complex action orchestration with Effects. High boilerplate cost (actions, reducers, selectors, effects).
|
|
182
|
+
|
|
183
|
+
### Routing
|
|
184
|
+
|
|
185
|
+
**Standalone route config (Angular 17+):**
|
|
186
|
+
|
|
187
|
+
```typescript
|
|
188
|
+
// app.routes.ts
|
|
189
|
+
export const routes: Routes = [
|
|
190
|
+
{ path: '', component: HomeComponent },
|
|
191
|
+
{
|
|
192
|
+
path: 'dashboard',
|
|
193
|
+
loadComponent: () => import('./features/dashboard/dashboard.component')
|
|
194
|
+
.then(m => m.DashboardComponent),
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
path: 'admin',
|
|
198
|
+
loadChildren: () => import('./features/admin/admin.routes')
|
|
199
|
+
.then(m => m.ADMIN_ROUTES),
|
|
200
|
+
canActivate: [authGuard],
|
|
201
|
+
},
|
|
202
|
+
];
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
**Functional guards (Angular 15+):**
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
export const authGuard: CanActivateFn = (route, state) => {
|
|
209
|
+
const auth = inject(AuthService);
|
|
210
|
+
const router = inject(Router);
|
|
211
|
+
return auth.isAuthenticated() ? true : router.createUrlTree(['/login']);
|
|
212
|
+
};
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
- Prefer `loadComponent` for single-component routes and `loadChildren` for feature route groups.
|
|
216
|
+
- Use `resolve` for route data pre-fetching. Use `canMatch` to conditionally load different components for the same route path.
|
|
217
|
+
|
|
218
|
+
### Data Flow Patterns
|
|
219
|
+
|
|
220
|
+
- **Template binding**: prefer signals over observables. Convert observables with `toSignal()` before template binding.
|
|
221
|
+
- **Service-to-component**: return signals from services; use `computed()` for derived state.
|
|
222
|
+
- **Component-to-component**: use `input()` / `output()` for parent-child; use a shared signal service for sibling communication.
|
|
223
|
+
- **RxJS operators** remain essential for: `debounceTime`, `switchMap`, `retry`, `combineLatest`, `merge`, `takeUntilDestroyed`.
|
|
224
|
+
|
|
225
|
+
### Error Handling
|
|
226
|
+
|
|
227
|
+
**Global ErrorHandler:**
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
@Injectable()
|
|
231
|
+
export class GlobalErrorHandler implements ErrorHandler {
|
|
232
|
+
handleError(error: unknown): void {
|
|
233
|
+
// Log to Sentry / external service
|
|
234
|
+
console.error('Unhandled error:', error);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Provide in app.config.ts
|
|
239
|
+
{ provide: ErrorHandler, useClass: GlobalErrorHandler }
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
**HTTP error interceptor (functional):**
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
export const errorInterceptor: HttpInterceptorFn = (req, next) =>
|
|
246
|
+
next(req).pipe(
|
|
247
|
+
retry({ count: 2, delay: 1000 }),
|
|
248
|
+
catchError((error: HttpErrorResponse) => {
|
|
249
|
+
if (error.status === 401) inject(Router).navigate(['/login']);
|
|
250
|
+
return throwError(() => error);
|
|
251
|
+
})
|
|
252
|
+
);
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Logging and Observability
|
|
256
|
+
|
|
257
|
+
- Use a centralized `LoggerService` that wraps `console.*` and can route to external providers (Sentry, Datadog, Application Insights).
|
|
258
|
+
- Instrument HTTP calls via interceptor: log request/response timings, status codes, error rates.
|
|
259
|
+
- Use Angular's `ErrorHandler` to funnel all unhandled errors to the logging service.
|
|
260
|
+
- In production, integrate Sentry Angular SDK (`@sentry/angular`) for error tracking with source maps.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Anti-Patterns & Pitfalls
|
|
265
|
+
|
|
266
|
+
### 1. Not using `OnPush` change detection
|
|
267
|
+
**Why**: Default change detection checks the entire component subtree on every event. This causes thousands of unnecessary re-renders in large apps, degrading performance. With signals and standalone components, always pair with `OnPush`.
|
|
268
|
+
|
|
269
|
+
### 2. Calling methods in templates
|
|
270
|
+
**Why**: Angular re-evaluates template expressions on every change detection cycle. A method call like `{{ getTotal() }}` runs repeatedly, even when inputs have not changed. Use `computed()` signals or pure pipes instead.
|
|
271
|
+
|
|
272
|
+
### 3. Forgetting to unsubscribe from observables
|
|
273
|
+
**Why**: Leaked subscriptions cause memory leaks, stale callbacks, and erratic behavior. Use `takeUntilDestroyed()` (Angular 16+), `DestroyRef`, or convert to signals with `toSignal()` (which auto-unsubscribes).
|
|
274
|
+
|
|
275
|
+
### 4. Nested subscriptions (subscribe-inside-subscribe)
|
|
276
|
+
**Why**: Creates tightly coupled, hard-to-test code. Misses error propagation and cancellation semantics. Use `switchMap`, `mergeMap`, or `concatMap` instead.
|
|
277
|
+
|
|
278
|
+
### 5. "God components" with too many responsibilities
|
|
279
|
+
**Why**: Components exceeding 300+ lines with mixed template logic, HTTP calls, and business rules are untestable and unmaintainable. Extract business logic to services and split into smart/dumb component pairs.
|
|
280
|
+
|
|
281
|
+
### 6. Overusing `any` type
|
|
282
|
+
**Why**: Defeats TypeScript's compile-time safety. Bugs slip to runtime that the compiler would catch. Use strict typing; enable `strict: true` in `tsconfig.json`. Use `unknown` if the type is truly uncertain.
|
|
283
|
+
|
|
284
|
+
### 7. Direct DOM manipulation (ElementRef.nativeElement, document.querySelector)
|
|
285
|
+
**Why**: Bypasses Angular's sanitization (XSS risk), breaks SSR compatibility, and fights the framework's rendering pipeline. Use `Renderer2`, template variables, or signal-based `viewChild()`.
|
|
286
|
+
|
|
287
|
+
### 8. Putting everything in a single shared module
|
|
288
|
+
**Why**: Shared modules that re-export dozens of components become import-everything bundles, killing tree-shaking. With standalone components, import only what each component needs directly.
|
|
289
|
+
|
|
290
|
+
### 9. Storing sensitive tokens in localStorage
|
|
291
|
+
**Why**: localStorage is accessible to any script on the page, making it vulnerable to XSS exfiltration. Store tokens in HttpOnly cookies set by the server.
|
|
292
|
+
|
|
293
|
+
### 10. Importing entire RxJS or lodash
|
|
294
|
+
**Why**: `import * as rxjs from 'rxjs'` or `import _ from 'lodash'` prevents tree-shaking, inflating bundle size by 50-200 KB. Import specific operators: `import { map } from 'rxjs/operators'` or use `lodash-es` with named imports.
|
|
295
|
+
|
|
296
|
+
### 11. Using `async` pipe AND `toSignal()` for the same stream
|
|
297
|
+
**Why**: Double subscription causes duplicate HTTP calls and inconsistent state. Choose one: `toSignal()` for signal-based templates, `async` pipe for legacy observable templates.
|
|
298
|
+
|
|
299
|
+
### 12. Mutating signal values in place
|
|
300
|
+
**Why**: `this.items().push(newItem)` mutates the array without notifying the signal. Signals detect changes by reference. Always use `update()` with a new reference: `this.items.update(arr => [...arr, newItem])`.
|
|
301
|
+
|
|
302
|
+
### 13. Overusing `effect()` for state derivation
|
|
303
|
+
**Why**: `effect()` is for side effects (logging, localStorage sync), not computed state. Using `effect()` to set other signals creates hidden dependencies and glitch potential. Use `computed()` for derived state.
|
|
304
|
+
|
|
305
|
+
### 14. Not lazy-loading feature routes
|
|
306
|
+
**Why**: Eager loading all features means users download the entire app upfront. Route-level lazy loading with `loadComponent` / `loadChildren` can cut initial bundle size by 40-70%.
|
|
307
|
+
|
|
308
|
+
### 15. Ignoring `trackBy` in `@for` loops
|
|
309
|
+
**Why**: Without `track`, Angular destroys and recreates all DOM nodes on every change. The `track` expression (required in `@for`) tells Angular which items are the same across renders, enabling efficient DOM recycling.
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## Testing Strategy
|
|
314
|
+
|
|
315
|
+
### Component Testing (TestBed)
|
|
316
|
+
|
|
317
|
+
```typescript
|
|
318
|
+
describe('UserListComponent', () => {
|
|
319
|
+
let component: UserListComponent;
|
|
320
|
+
let fixture: ComponentFixture<UserListComponent>;
|
|
321
|
+
|
|
322
|
+
beforeEach(async () => {
|
|
323
|
+
await TestBed.configureTestingModule({
|
|
324
|
+
imports: [UserListComponent], // standalone components go in imports
|
|
325
|
+
providers: [
|
|
326
|
+
{ provide: UserService, useValue: { getUsers: () => signal([]) } }
|
|
327
|
+
],
|
|
328
|
+
}).compileComponents();
|
|
329
|
+
|
|
330
|
+
fixture = TestBed.createComponent(UserListComponent);
|
|
331
|
+
component = fixture.componentInstance;
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('should render user names', () => {
|
|
335
|
+
fixture.componentRef.setInput('users', [{ name: 'Alice' }]);
|
|
336
|
+
fixture.detectChanges();
|
|
337
|
+
expect(fixture.nativeElement.textContent).toContain('Alice');
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
**Component Harnesses** (Angular CDK): provide a stable, abstraction-layer API for interacting with Angular Material components in tests, decoupling test code from DOM structure.
|
|
343
|
+
|
|
344
|
+
### Service Testing
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
describe('AuthService', () => {
|
|
348
|
+
let service: AuthService;
|
|
349
|
+
let httpMock: HttpTestingController;
|
|
350
|
+
|
|
351
|
+
beforeEach(() => {
|
|
352
|
+
TestBed.configureTestingModule({
|
|
353
|
+
providers: [
|
|
354
|
+
AuthService,
|
|
355
|
+
provideHttpClient(),
|
|
356
|
+
provideHttpClientTesting(),
|
|
357
|
+
],
|
|
358
|
+
});
|
|
359
|
+
service = TestBed.inject(AuthService);
|
|
360
|
+
httpMock = TestBed.inject(HttpTestingController);
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('should login and store token', () => {
|
|
364
|
+
service.login('user', 'pass').subscribe(res => {
|
|
365
|
+
expect(res.token).toBe('abc');
|
|
366
|
+
});
|
|
367
|
+
httpMock.expectOne('/api/login').flush({ token: 'abc' });
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
### E2E Testing (Playwright)
|
|
373
|
+
|
|
374
|
+
Playwright has fully replaced Protractor (deprecated since Angular 15). Use `@ngx-playwright/test` or Playwright directly.
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
// e2e/dashboard.spec.ts
|
|
378
|
+
import { test, expect } from '@playwright/test';
|
|
379
|
+
|
|
380
|
+
test('should display dashboard after login', async ({ page }) => {
|
|
381
|
+
await page.goto('/login');
|
|
382
|
+
await page.fill('[data-testid="email"]', 'admin@test.com');
|
|
383
|
+
await page.fill('[data-testid="password"]', 'password');
|
|
384
|
+
await page.click('[data-testid="submit"]');
|
|
385
|
+
await expect(page).toHaveURL('/dashboard');
|
|
386
|
+
await expect(page.locator('h1')).toHaveText('Dashboard');
|
|
387
|
+
});
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
- Use `data-testid` attributes for stable selectors.
|
|
391
|
+
- Run in CI with `--reporter=html` for visual reports.
|
|
392
|
+
|
|
393
|
+
### Signal Testing Patterns
|
|
394
|
+
|
|
395
|
+
Test signals by reading their current value after triggering changes:
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
it('should compute doubled count', () => {
|
|
399
|
+
const component = TestBed.createComponent(CounterComponent).componentInstance;
|
|
400
|
+
component.count.set(5);
|
|
401
|
+
expect(component.doubleCount()).toBe(10);
|
|
402
|
+
});
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
For `effect()`, use `TestBed.flushEffects()` (Angular 18+) to synchronously flush pending effects in tests.
|
|
406
|
+
|
|
407
|
+
### Marble Testing (RxJS)
|
|
408
|
+
|
|
409
|
+
Use `TestScheduler` from `rxjs/testing` for deterministic observable testing:
|
|
410
|
+
|
|
411
|
+
```typescript
|
|
412
|
+
it('should debounce search input', () => {
|
|
413
|
+
const scheduler = new TestScheduler((actual, expected) => {
|
|
414
|
+
expect(actual).toEqual(expected);
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
scheduler.run(({ cold, expectObservable }) => {
|
|
418
|
+
const input$ = cold('--a--b----c|');
|
|
419
|
+
const expected = '--------b-------c|';
|
|
420
|
+
expectObservable(input$.pipe(debounceTime(5, scheduler))).toBe(expected);
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Angular 21+ Testing Revolution
|
|
426
|
+
|
|
427
|
+
- **Vitest** is becoming the default unit test runner (replacing Karma/Jest).
|
|
428
|
+
- **Testronaut** provides Playwright-based component testing that uses the Angular CLI build pipeline natively.
|
|
429
|
+
|
|
430
|
+
---
|
|
431
|
+
|
|
432
|
+
## Performance Considerations
|
|
433
|
+
|
|
434
|
+
### Change Detection
|
|
435
|
+
|
|
436
|
+
**OnPush + Signals = Optimal**: OnPush components only re-render when their inputs change or signals they read are updated. Signals provide fine-grained tracking so Angular knows exactly which components to check.
|
|
437
|
+
|
|
438
|
+
**Zoneless Angular (v18+ experimental, v20.2+ stable, v21+ default):**
|
|
439
|
+
|
|
440
|
+
```typescript
|
|
441
|
+
// app.config.ts
|
|
442
|
+
export const appConfig: ApplicationConfig = {
|
|
443
|
+
providers: [
|
|
444
|
+
provideExperimentalZonelessChangeDetection(), // v18-19
|
|
445
|
+
// provideZonelessChangeDetection(), // v20.2+ stable
|
|
446
|
+
],
|
|
447
|
+
};
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
Benefits: ~30-50 KB bundle reduction, 40-50% LCP improvement, cleaner stack traces, no Zone.js monkey-patching of async APIs.
|
|
451
|
+
|
|
452
|
+
**Requirements for zoneless**: all components must use `OnPush` or signals. No reliance on Zone.js-triggered change detection.
|
|
453
|
+
|
|
454
|
+
### Lazy Loading and Deferred Views
|
|
455
|
+
|
|
456
|
+
**Route-level lazy loading**: `loadComponent` / `loadChildren` in route config.
|
|
457
|
+
|
|
458
|
+
**`@defer` blocks (Angular 17+):**
|
|
459
|
+
|
|
460
|
+
```html
|
|
461
|
+
@defer (on viewport) {
|
|
462
|
+
<app-heavy-chart [data]="chartData()" />
|
|
463
|
+
} @placeholder {
|
|
464
|
+
<div class="skeleton-chart"></div>
|
|
465
|
+
} @loading (minimum 300ms) {
|
|
466
|
+
<app-spinner />
|
|
467
|
+
} @error {
|
|
468
|
+
<p>Failed to load chart</p>
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
Triggers: `on idle`, `on viewport`, `on interaction`, `on hover`, `on timer(ms)`, `when condition`.
|
|
473
|
+
|
|
474
|
+
`@defer` reduces initial bundle size by code-splitting deferred components and their dependencies into separate chunks automatically.
|
|
475
|
+
|
|
476
|
+
### SSR (Angular SSR, formerly Angular Universal)
|
|
477
|
+
|
|
478
|
+
- Built-in since Angular 17 via `@angular/ssr` package (replaces `@nguniversal/*`).
|
|
479
|
+
- **Hydration** (stable v17+): server-rendered HTML is reused by the client, avoiding DOM destruction/recreation.
|
|
480
|
+
- **Incremental Hydration** (v19.2+): combines `@defer` with SSR — server renders placeholder HTML, client hydrates deferred blocks only when triggered, loading less JavaScript upfront.
|
|
481
|
+
- **Hybrid Rendering** (v19+): per-route SSR/SSG/CSR configuration in `server.ts`.
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
// server.ts — route-level rendering mode
|
|
485
|
+
serverRoutes: [
|
|
486
|
+
{ path: '', renderMode: RenderMode.Prerender }, // SSG
|
|
487
|
+
{ path: 'dashboard', renderMode: RenderMode.Server }, // SSR
|
|
488
|
+
{ path: 'admin/**', renderMode: RenderMode.Client }, // CSR
|
|
489
|
+
]
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
### Bundle Size Optimization
|
|
493
|
+
|
|
494
|
+
- **Standalone components** enable better tree-shaking — no NgModule barrel imports.
|
|
495
|
+
- Use `@defer` for below-the-fold components and heavy third-party libraries (chart libraries, rich text editors).
|
|
496
|
+
- Analyze bundles with `npx ng build --stats-json` + `webpack-bundle-analyzer` or `source-map-explorer`.
|
|
497
|
+
- Enable production optimizations: `ng build --configuration production` applies minification, tree-shaking, and dead-code elimination.
|
|
498
|
+
|
|
499
|
+
### Image Optimization (NgOptimizedImage)
|
|
500
|
+
|
|
501
|
+
```html
|
|
502
|
+
<img ngSrc="hero.jpg" width="1200" height="600" priority />
|
|
503
|
+
<img ngSrc="thumb.jpg" width="200" height="200" />
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
- `priority` attribute sets `fetchpriority="high"` and `loading="eager"` for LCP images.
|
|
507
|
+
- Non-priority images automatically get `loading="lazy"`.
|
|
508
|
+
- Requires explicit `width` and `height` to prevent layout shift, or use `fill` for fluid images.
|
|
509
|
+
- Connect an image CDN loader (Cloudinary, Imgix, etc.) for automatic `srcset` generation.
|
|
510
|
+
|
|
511
|
+
---
|
|
512
|
+
|
|
513
|
+
## Security Considerations
|
|
514
|
+
|
|
515
|
+
### Built-in XSS Protection
|
|
516
|
+
|
|
517
|
+
Angular treats all template-bound values as untrusted by default and sanitizes them. This covers:
|
|
518
|
+
- HTML interpolation (`{{ }}` and `[innerHTML]`)
|
|
519
|
+
- URL bindings (`[href]`, `[src]`)
|
|
520
|
+
- Style bindings
|
|
521
|
+
|
|
522
|
+
**Do NOT bypass with**: `bypassSecurityTrustHtml()` unless absolutely necessary and the content is verified safe. Document every usage with a security justification comment.
|
|
523
|
+
|
|
524
|
+
**Avoid**: `ElementRef.nativeElement.innerHTML`, `document.createElement`, and dynamic code execution — these bypass Angular's sanitizer entirely.
|
|
525
|
+
|
|
526
|
+
### CSRF Protection
|
|
527
|
+
|
|
528
|
+
```typescript
|
|
529
|
+
// app.config.ts
|
|
530
|
+
provideHttpClient(
|
|
531
|
+
withXsrfConfiguration({
|
|
532
|
+
cookieName: 'XSRF-TOKEN', // cookie name from server
|
|
533
|
+
headerName: 'X-XSRF-TOKEN', // header sent with requests
|
|
534
|
+
})
|
|
535
|
+
)
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
Server must set the XSRF cookie and validate the header. Angular's `HttpClient` reads the cookie and attaches the header automatically on mutating requests (POST, PUT, DELETE).
|
|
539
|
+
|
|
540
|
+
### Content Security Policy (CSP)
|
|
541
|
+
|
|
542
|
+
Minimum CSP for Angular apps:
|
|
543
|
+
|
|
544
|
+
```
|
|
545
|
+
Content-Security-Policy:
|
|
546
|
+
default-src 'self';
|
|
547
|
+
script-src 'self' 'nonce-{RANDOM}';
|
|
548
|
+
style-src 'self' 'nonce-{RANDOM}';
|
|
549
|
+
img-src 'self' data: https:;
|
|
550
|
+
connect-src 'self' https://api.example.com;
|
|
551
|
+
```
|
|
552
|
+
|
|
553
|
+
- Angular 17+ supports `nonce` for inline styles via `ngCspNonce` attribute or `CSP_NONCE` injection token.
|
|
554
|
+
- Avoid `unsafe-inline` for scripts. Use nonces or hashes.
|
|
555
|
+
|
|
556
|
+
### Authentication
|
|
557
|
+
|
|
558
|
+
- Use `angular-auth-oidc-client` for OpenID Connect / OAuth 2.0 flows.
|
|
559
|
+
- Store tokens in HttpOnly, Secure, SameSite cookies — never in `localStorage` or `sessionStorage`.
|
|
560
|
+
- Implement token refresh via HTTP interceptor before expiry.
|
|
561
|
+
- Use route guards (`canActivate`, `canMatch`) to protect routes client-side; always enforce authorization server-side.
|
|
562
|
+
|
|
563
|
+
---
|
|
564
|
+
|
|
565
|
+
## Integration Patterns
|
|
566
|
+
|
|
567
|
+
### HTTP (HttpClient)
|
|
568
|
+
|
|
569
|
+
**Functional interceptors (Angular 15+, recommended):**
|
|
570
|
+
|
|
571
|
+
```typescript
|
|
572
|
+
// app.config.ts
|
|
573
|
+
provideHttpClient(
|
|
574
|
+
withInterceptors([authInterceptor, loggingInterceptor, retryInterceptor])
|
|
575
|
+
)
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
**Retry with exponential backoff:**
|
|
579
|
+
|
|
580
|
+
```typescript
|
|
581
|
+
export const retryInterceptor: HttpInterceptorFn = (req, next) =>
|
|
582
|
+
next(req).pipe(
|
|
583
|
+
retry({
|
|
584
|
+
count: 3,
|
|
585
|
+
delay: (error, retryCount) => {
|
|
586
|
+
if (error.status >= 400 && error.status < 500) return throwError(() => error);
|
|
587
|
+
return timer(Math.pow(2, retryCount) * 1000); // 2s, 4s, 8s
|
|
588
|
+
},
|
|
589
|
+
})
|
|
590
|
+
);
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
**Request context tokens** for per-request configuration:
|
|
594
|
+
|
|
595
|
+
```typescript
|
|
596
|
+
const CACHE_ENABLED = new HttpContextToken<boolean>(() => false);
|
|
597
|
+
|
|
598
|
+
// In service
|
|
599
|
+
this.http.get('/api/data', {
|
|
600
|
+
context: new HttpContext().set(CACHE_ENABLED, true),
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
// In interceptor
|
|
604
|
+
if (req.context.get(CACHE_ENABLED)) { /* serve from cache */ }
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
### Forms (Reactive, Typed)
|
|
608
|
+
|
|
609
|
+
**Typed FormGroup (Angular 14+):**
|
|
610
|
+
|
|
611
|
+
```typescript
|
|
612
|
+
export class ProfileFormComponent {
|
|
613
|
+
private fb = inject(NonNullableFormBuilder);
|
|
614
|
+
|
|
615
|
+
form = this.fb.group({
|
|
616
|
+
name: ['', [Validators.required, Validators.minLength(2)]],
|
|
617
|
+
email: ['', [Validators.required, Validators.email]],
|
|
618
|
+
address: this.fb.group({
|
|
619
|
+
street: [''],
|
|
620
|
+
city: [''],
|
|
621
|
+
zip: ['', Validators.pattern(/^\d{5}$/)],
|
|
622
|
+
}),
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
onSubmit() {
|
|
626
|
+
if (this.form.invalid) return;
|
|
627
|
+
const value = this.form.getRawValue(); // fully typed
|
|
628
|
+
// value.name is string, value.address.zip is string
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
- Use `NonNullableFormBuilder` so `.reset()` returns to initial values, not null.
|
|
634
|
+
- Access controls via `this.form.controls.name` (typed) rather than `this.form.get('name')` (untyped).
|
|
635
|
+
- For dynamic forms, use `FormArray` with typed element types.
|
|
636
|
+
|
|
637
|
+
### Angular Material / CDK
|
|
638
|
+
|
|
639
|
+
- Import only needed components: `import { MatButtonModule } from '@angular/material/button'`.
|
|
640
|
+
- Use CDK primitives (`Overlay`, `DragDrop`, `A11y`, `Clipboard`) for custom UI without Material styling.
|
|
641
|
+
- Use component harnesses in tests: `const button = await loader.getHarness(MatButtonHarness)`.
|
|
642
|
+
|
|
643
|
+
### Real-time (WebSocket, SSE)
|
|
644
|
+
|
|
645
|
+
```typescript
|
|
646
|
+
@Injectable({ providedIn: 'root' })
|
|
647
|
+
export class WebSocketService {
|
|
648
|
+
private socket$ = webSocket<Message>('wss://api.example.com/ws');
|
|
649
|
+
|
|
650
|
+
messages$ = this.socket$.pipe(
|
|
651
|
+
retry({ delay: 3000 }), // auto-reconnect
|
|
652
|
+
share(), // share among subscribers
|
|
653
|
+
);
|
|
654
|
+
|
|
655
|
+
send(msg: Message) {
|
|
656
|
+
this.socket$.next(msg);
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
For SSE (Server-Sent Events), use native `EventSource` API wrapped in an Observable or signal via `rxResource`.
|
|
662
|
+
|
|
663
|
+
---
|
|
664
|
+
|
|
665
|
+
## DevOps & Deployment
|
|
666
|
+
|
|
667
|
+
### Angular CLI and Build System
|
|
668
|
+
|
|
669
|
+
**esbuild + Vite (Angular 17+ default):**
|
|
670
|
+
|
|
671
|
+
- Production builds via esbuild: `ng build` — ~56% faster than Webpack (8 min to 3.5 min in large projects).
|
|
672
|
+
- Dev server via Vite: `ng serve` — cold start ~12s (down from ~45s with Webpack).
|
|
673
|
+
- Output is ESM (`.mjs` files) by default. Ensure Node.js 20+ in CI and Docker.
|
|
674
|
+
|
|
675
|
+
```json
|
|
676
|
+
// angular.json
|
|
677
|
+
{
|
|
678
|
+
"architect": {
|
|
679
|
+
"build": {
|
|
680
|
+
"builder": "@angular/build:application",
|
|
681
|
+
"options": {
|
|
682
|
+
"outputPath": "dist/my-app",
|
|
683
|
+
"index": "src/index.html",
|
|
684
|
+
"browser": "src/main.ts",
|
|
685
|
+
"server": "src/main.server.ts"
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
### Docker Patterns
|
|
693
|
+
|
|
694
|
+
**Multi-stage Dockerfile:**
|
|
695
|
+
|
|
696
|
+
```dockerfile
|
|
697
|
+
# Build stage
|
|
698
|
+
FROM node:20-alpine AS build
|
|
699
|
+
WORKDIR /app
|
|
700
|
+
COPY package*.json ./
|
|
701
|
+
RUN npm ci
|
|
702
|
+
COPY . .
|
|
703
|
+
RUN npx ng build --configuration production
|
|
704
|
+
|
|
705
|
+
# Production stage (static SPA)
|
|
706
|
+
FROM nginx:alpine
|
|
707
|
+
COPY --from=build /app/dist/my-app/browser /usr/share/nginx/html
|
|
708
|
+
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
|
709
|
+
EXPOSE 80
|
|
710
|
+
|
|
711
|
+
# Production stage (SSR)
|
|
712
|
+
FROM node:20-alpine AS ssr
|
|
713
|
+
WORKDIR /app
|
|
714
|
+
COPY --from=build /app/dist/my-app .
|
|
715
|
+
EXPOSE 4000
|
|
716
|
+
CMD ["node", "server/server.mjs"]
|
|
717
|
+
```
|
|
718
|
+
|
|
719
|
+
**nginx.conf for SPA routing:**
|
|
720
|
+
|
|
721
|
+
```nginx
|
|
722
|
+
server {
|
|
723
|
+
listen 80;
|
|
724
|
+
root /usr/share/nginx/html;
|
|
725
|
+
index index.html;
|
|
726
|
+
|
|
727
|
+
location / {
|
|
728
|
+
try_files $uri $uri/ /index.html;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
location /assets/ {
|
|
732
|
+
expires 1y;
|
|
733
|
+
add_header Cache-Control "public, immutable";
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
### CI/CD
|
|
739
|
+
|
|
740
|
+
```yaml
|
|
741
|
+
# GitHub Actions example
|
|
742
|
+
jobs:
|
|
743
|
+
build-and-test:
|
|
744
|
+
runs-on: ubuntu-latest
|
|
745
|
+
steps:
|
|
746
|
+
- uses: actions/checkout@v4
|
|
747
|
+
- uses: actions/setup-node@v4
|
|
748
|
+
with:
|
|
749
|
+
node-version: 20
|
|
750
|
+
cache: 'npm'
|
|
751
|
+
- run: npm ci
|
|
752
|
+
- run: npx ng lint
|
|
753
|
+
- run: npx ng test --watch=false --browsers=ChromeHeadless --code-coverage
|
|
754
|
+
- run: npx ng build --configuration production
|
|
755
|
+
- run: npx playwright install --with-deps
|
|
756
|
+
- run: npx playwright test
|
|
757
|
+
- uses: actions/upload-artifact@v4
|
|
758
|
+
with:
|
|
759
|
+
name: dist
|
|
760
|
+
path: dist/
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
- Cache `node_modules` and `.angular/cache` for faster CI.
|
|
764
|
+
- Run lint, unit tests, build, and E2E in parallel where possible.
|
|
765
|
+
- Use `--affected` with Nx to only build/test changed projects.
|
|
766
|
+
|
|
767
|
+
### Environment Files
|
|
768
|
+
|
|
769
|
+
```typescript
|
|
770
|
+
// environments/environment.ts (dev)
|
|
771
|
+
export const environment = {
|
|
772
|
+
production: false,
|
|
773
|
+
apiUrl: 'http://localhost:3000/api',
|
|
774
|
+
};
|
|
775
|
+
|
|
776
|
+
// environments/environment.prod.ts
|
|
777
|
+
export const environment = {
|
|
778
|
+
production: true,
|
|
779
|
+
apiUrl: 'https://api.example.com',
|
|
780
|
+
};
|
|
781
|
+
```
|
|
782
|
+
|
|
783
|
+
For runtime configuration (Docker/K8s), load config from `/assets/config.json` at app startup via `APP_INITIALIZER` — avoids rebuilding for each environment.
|
|
784
|
+
|
|
785
|
+
### Monitoring (Sentry)
|
|
786
|
+
|
|
787
|
+
```typescript
|
|
788
|
+
// app.config.ts
|
|
789
|
+
import * as Sentry from '@sentry/angular';
|
|
790
|
+
|
|
791
|
+
Sentry.init({
|
|
792
|
+
dsn: environment.sentryDsn,
|
|
793
|
+
integrations: [Sentry.browserTracingIntegration()],
|
|
794
|
+
tracesSampleRate: 0.2,
|
|
795
|
+
});
|
|
796
|
+
|
|
797
|
+
export const appConfig: ApplicationConfig = {
|
|
798
|
+
providers: [
|
|
799
|
+
{ provide: ErrorHandler, useValue: Sentry.createErrorHandler() },
|
|
800
|
+
{ provide: Sentry.TraceService, deps: [Router] },
|
|
801
|
+
{
|
|
802
|
+
provide: APP_INITIALIZER,
|
|
803
|
+
useFactory: () => () => {},
|
|
804
|
+
deps: [Sentry.TraceService],
|
|
805
|
+
multi: true,
|
|
806
|
+
},
|
|
807
|
+
],
|
|
808
|
+
};
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
Upload source maps in CI: `npx sentry-cli sourcemaps upload dist/`.
|
|
812
|
+
|
|
813
|
+
---
|
|
814
|
+
|
|
815
|
+
## Decision Trees
|
|
816
|
+
|
|
817
|
+
### Signals vs RxJS
|
|
818
|
+
|
|
819
|
+
```
|
|
820
|
+
Need to manage UI state or component state?
|
|
821
|
+
-> YES: Use signal() + computed()
|
|
822
|
+
Need derived/computed values from existing state?
|
|
823
|
+
-> YES: Use computed()
|
|
824
|
+
Need to react to user events with debounce, throttle, switchMap?
|
|
825
|
+
-> YES: Use RxJS operators
|
|
826
|
+
Need to combine multiple async streams (merge, combineLatest, race)?
|
|
827
|
+
-> YES: Use RxJS
|
|
828
|
+
Working with HttpClient responses?
|
|
829
|
+
-> Simple request: Use rxResource() or toSignal(this.http.get(...))
|
|
830
|
+
-> Complex retry/polling/cancellation: Use RxJS pipe
|
|
831
|
+
Need to bridge signal <-> observable?
|
|
832
|
+
-> Observable to Signal: toSignal()
|
|
833
|
+
-> Signal to Observable: toObservable()
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
**Rule of thumb**: Signals for state (synchronous, value-based), RxJS for events and streams (asynchronous, time-based).
|
|
837
|
+
|
|
838
|
+
### NgRx vs Service-Based State
|
|
839
|
+
|
|
840
|
+
```
|
|
841
|
+
Is state local to one component or feature?
|
|
842
|
+
-> YES: signal() in component or feature service
|
|
843
|
+
Is state shared across multiple features?
|
|
844
|
+
-> Simple (2-5 properties): Injectable service with signals
|
|
845
|
+
-> Medium complexity: NgRx SignalStore
|
|
846
|
+
-> Complex (many entities, optimistic updates, undo/redo): NgRx Store
|
|
847
|
+
Do you need Redux DevTools time-travel debugging?
|
|
848
|
+
-> YES: NgRx Store
|
|
849
|
+
Do you need action logging / audit trail?
|
|
850
|
+
-> YES: NgRx Store
|
|
851
|
+
Is the team unfamiliar with Redux concepts?
|
|
852
|
+
-> YES: Start with services + signals, adopt NgRx SignalStore if needed
|
|
853
|
+
```
|
|
854
|
+
|
|
855
|
+
### Standalone vs Module-Based Architecture
|
|
856
|
+
|
|
857
|
+
```
|
|
858
|
+
Starting a new project (Angular 17+)?
|
|
859
|
+
-> YES: Standalone components (default since Angular 19)
|
|
860
|
+
Existing project with NgModules?
|
|
861
|
+
-> Small/medium: Migrate to standalone incrementally
|
|
862
|
+
-> Large enterprise: Migrate feature-by-feature; use importProvidersFrom() for legacy module compat
|
|
863
|
+
Using third-party libraries that require NgModules?
|
|
864
|
+
-> Wrap with importProvidersFrom() in providers array
|
|
865
|
+
Need to share declarations across many components?
|
|
866
|
+
-> Create barrel export file or shared component library
|
|
867
|
+
-> Do NOT create a giant SharedModule
|
|
868
|
+
```
|
|
869
|
+
|
|
870
|
+
**Angular team guidance**: Standalone is the future. NgModules are in maintenance mode. All new Angular APIs are standalone-first.
|
|
871
|
+
|
|
872
|
+
---
|
|
873
|
+
|
|
874
|
+
## Code Examples
|
|
875
|
+
|
|
876
|
+
### 1. Standalone Component with Signals and OnPush
|
|
877
|
+
|
|
878
|
+
```typescript
|
|
879
|
+
// user-card.component.ts
|
|
880
|
+
import { Component, input, output, ChangeDetectionStrategy } from '@angular/core';
|
|
881
|
+
import { DatePipe } from '@angular/common';
|
|
882
|
+
|
|
883
|
+
@Component({
|
|
884
|
+
selector: 'app-user-card',
|
|
885
|
+
standalone: true,
|
|
886
|
+
imports: [DatePipe],
|
|
887
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
888
|
+
template: `
|
|
889
|
+
<div class="card">
|
|
890
|
+
<h3>{{ user().name }}</h3>
|
|
891
|
+
<p>{{ user().email }}</p>
|
|
892
|
+
<p>Joined: {{ user().joinedAt | date:'mediumDate' }}</p>
|
|
893
|
+
<button (click)="selected.emit(user())">Select</button>
|
|
894
|
+
</div>
|
|
895
|
+
`,
|
|
896
|
+
})
|
|
897
|
+
export class UserCardComponent {
|
|
898
|
+
user = input.required<User>();
|
|
899
|
+
selected = output<User>();
|
|
900
|
+
}
|
|
901
|
+
```
|
|
902
|
+
|
|
903
|
+
### 2. Data Fetching with rxResource and Signals
|
|
904
|
+
|
|
905
|
+
```typescript
|
|
906
|
+
// product-list.component.ts
|
|
907
|
+
import { Component, signal, inject } from '@angular/core';
|
|
908
|
+
import { rxResource } from '@angular/core/rxjs-interop';
|
|
909
|
+
import { HttpClient } from '@angular/common/http';
|
|
910
|
+
import { FormsModule } from '@angular/forms';
|
|
911
|
+
|
|
912
|
+
@Component({
|
|
913
|
+
selector: 'app-product-list',
|
|
914
|
+
standalone: true,
|
|
915
|
+
imports: [FormsModule],
|
|
916
|
+
template: `
|
|
917
|
+
<input [(ngModel)]="searchTerm" placeholder="Search..." />
|
|
918
|
+
|
|
919
|
+
@if (products.isLoading()) {
|
|
920
|
+
<app-spinner />
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
@if (products.error()) {
|
|
924
|
+
<p class="error">Failed to load products</p>
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
@for (product of products.value(); track product.id) {
|
|
928
|
+
<app-product-card [product]="product" />
|
|
929
|
+
} @empty {
|
|
930
|
+
<p>No products found</p>
|
|
931
|
+
}
|
|
932
|
+
`,
|
|
933
|
+
})
|
|
934
|
+
export class ProductListComponent {
|
|
935
|
+
private http = inject(HttpClient);
|
|
936
|
+
searchTerm = signal('');
|
|
937
|
+
|
|
938
|
+
products = rxResource({
|
|
939
|
+
request: () => ({ q: this.searchTerm() }),
|
|
940
|
+
loader: ({ request }) =>
|
|
941
|
+
this.http.get<Product[]>('/api/products', { params: { q: request.q } }),
|
|
942
|
+
});
|
|
943
|
+
}
|
|
944
|
+
```
|
|
945
|
+
|
|
946
|
+
### 3. Functional Interceptor Chain
|
|
947
|
+
|
|
948
|
+
```typescript
|
|
949
|
+
// interceptors/auth.interceptor.ts
|
|
950
|
+
import { HttpInterceptorFn } from '@angular/common/http';
|
|
951
|
+
import { inject } from '@angular/core';
|
|
952
|
+
|
|
953
|
+
export const authInterceptor: HttpInterceptorFn = (req, next) => {
|
|
954
|
+
const token = inject(AuthService).accessToken();
|
|
955
|
+
if (!token) return next(req);
|
|
956
|
+
|
|
957
|
+
const authReq = req.clone({
|
|
958
|
+
setHeaders: { Authorization: `Bearer ${token}` },
|
|
959
|
+
});
|
|
960
|
+
return next(authReq);
|
|
961
|
+
};
|
|
962
|
+
|
|
963
|
+
// interceptors/logging.interceptor.ts
|
|
964
|
+
export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
|
|
965
|
+
const start = performance.now();
|
|
966
|
+
return next(req).pipe(
|
|
967
|
+
tap({
|
|
968
|
+
next: () => {
|
|
969
|
+
const duration = Math.round(performance.now() - start);
|
|
970
|
+
console.log(`[HTTP] ${req.method} ${req.url} -- ${duration}ms`);
|
|
971
|
+
},
|
|
972
|
+
error: (err) => {
|
|
973
|
+
console.error(`[HTTP ERROR] ${req.method} ${req.url}`, err.status);
|
|
974
|
+
},
|
|
975
|
+
})
|
|
976
|
+
);
|
|
977
|
+
};
|
|
978
|
+
|
|
979
|
+
// app.config.ts
|
|
980
|
+
provideHttpClient(
|
|
981
|
+
withInterceptors([authInterceptor, loggingInterceptor, retryInterceptor]),
|
|
982
|
+
withXsrfConfiguration({ cookieName: 'XSRF-TOKEN', headerName: 'X-XSRF-TOKEN' })
|
|
983
|
+
)
|
|
984
|
+
```
|
|
985
|
+
|
|
986
|
+
### 4. NgRx SignalStore with Entities
|
|
987
|
+
|
|
988
|
+
```typescript
|
|
989
|
+
// stores/todo.store.ts
|
|
990
|
+
import { signalStore, withState, withMethods, withComputed, patchState } from '@ngrx/signals';
|
|
991
|
+
import { withEntities, setAllEntities, addEntity, removeEntity } from '@ngrx/signals/entities';
|
|
992
|
+
import { rxMethod } from '@ngrx/signals/rxjs-interop';
|
|
993
|
+
import { pipe, switchMap, tap } from 'rxjs';
|
|
994
|
+
|
|
995
|
+
export const TodoStore = signalStore(
|
|
996
|
+
{ providedIn: 'root' },
|
|
997
|
+
withEntities<Todo>(),
|
|
998
|
+
withState({ loading: false, filter: 'all' as 'all' | 'active' | 'done' }),
|
|
999
|
+
withComputed(({ entities, filter }) => ({
|
|
1000
|
+
filteredTodos: computed(() => {
|
|
1001
|
+
const f = filter();
|
|
1002
|
+
if (f === 'all') return entities();
|
|
1003
|
+
return entities().filter(t => (f === 'done') === t.completed);
|
|
1004
|
+
}),
|
|
1005
|
+
remaining: computed(() => entities().filter(t => !t.completed).length),
|
|
1006
|
+
})),
|
|
1007
|
+
withMethods((store, http = inject(HttpClient)) => ({
|
|
1008
|
+
loadTodos: rxMethod<void>(
|
|
1009
|
+
pipe(
|
|
1010
|
+
tap(() => patchState(store, { loading: true })),
|
|
1011
|
+
switchMap(() => http.get<Todo[]>('/api/todos')),
|
|
1012
|
+
tap(todos => {
|
|
1013
|
+
patchState(store, setAllEntities(todos));
|
|
1014
|
+
patchState(store, { loading: false });
|
|
1015
|
+
})
|
|
1016
|
+
)
|
|
1017
|
+
),
|
|
1018
|
+
addTodo(title: string) {
|
|
1019
|
+
const todo: Todo = { id: crypto.randomUUID(), title, completed: false };
|
|
1020
|
+
patchState(store, addEntity(todo));
|
|
1021
|
+
},
|
|
1022
|
+
removeTodo(id: string) {
|
|
1023
|
+
patchState(store, removeEntity(id));
|
|
1024
|
+
},
|
|
1025
|
+
setFilter(filter: 'all' | 'active' | 'done') {
|
|
1026
|
+
patchState(store, { filter });
|
|
1027
|
+
},
|
|
1028
|
+
}))
|
|
1029
|
+
);
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
### 5. Deferred Loading with @defer
|
|
1033
|
+
|
|
1034
|
+
```typescript
|
|
1035
|
+
@Component({
|
|
1036
|
+
selector: 'app-analytics-page',
|
|
1037
|
+
standalone: true,
|
|
1038
|
+
imports: [HeroSectionComponent],
|
|
1039
|
+
template: `
|
|
1040
|
+
<app-hero-section [title]="pageTitle()" />
|
|
1041
|
+
|
|
1042
|
+
@defer (on viewport) {
|
|
1043
|
+
<app-heavy-chart [data]="chartData()" />
|
|
1044
|
+
} @placeholder {
|
|
1045
|
+
<div class="chart-skeleton" style="height: 400px"></div>
|
|
1046
|
+
} @loading (minimum 200ms) {
|
|
1047
|
+
<app-spinner />
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
@defer (on idle) {
|
|
1051
|
+
<app-recommendations [userId]="userId()" />
|
|
1052
|
+
} @placeholder {
|
|
1053
|
+
<p>Loading recommendations...</p>
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
@defer (on interaction(loadComments)) {
|
|
1057
|
+
<app-comments [postId]="postId()" />
|
|
1058
|
+
} @placeholder {
|
|
1059
|
+
<button #loadComments>Load Comments</button>
|
|
1060
|
+
}
|
|
1061
|
+
`,
|
|
1062
|
+
})
|
|
1063
|
+
export class AnalyticsPageComponent {
|
|
1064
|
+
pageTitle = input.required<string>();
|
|
1065
|
+
chartData = input.required<ChartData[]>();
|
|
1066
|
+
userId = input.required<string>();
|
|
1067
|
+
postId = input.required<string>();
|
|
1068
|
+
}
|
|
1069
|
+
```
|
|
1070
|
+
|
|
1071
|
+
---
|
|
1072
|
+
|
|
1073
|
+
*Researched: 2026-03-07 | Sources: [Angular Official Docs](https://angular.dev), [Angular Blog 2025 Strategy](https://blog.angular.dev/angular-2025-strategy-9ca333dfc334), [Angular Signals Guide](https://angular.dev/guide/signals), [RxJS Interop](https://angular.dev/ecosystem/rxjs-interop), [Nx Angular Architecture Guide](https://nx.dev/blog/architecting-angular-applications), [Nx State Management 2025](https://nx.dev/blog/angular-state-management-2025), [Angular Security](https://angular.dev/best-practices/security), [NgRx SignalStore](https://ngrx.io/guide/signals), [Angular SSR Guide](https://angular.dev/guide/ssr), [Angular Zoneless](https://angular.dev/guide/zoneless), [NgOptimizedImage](https://angular.dev/guide/image-optimization), [AngularArchitects SSR](https://www.angulararchitects.io/blog/guide-for-ssr/), [Angular Interceptors](https://angular.dev/guide/http/interceptors), [Angular Typed Forms](https://angular.dev/guide/forms/typed-forms), [InfoQ Angular 21](https://www.infoq.com/news/2025/11/angular-21-released/), [Signals vs RxJS](https://angularexperts.io/blog/signals-vs-rxjs/), [Chrome NgOptimizedImage](https://developer.chrome.com/blog/angular_ngoptimizedimage/), [Angular Zoneless Performance](https://javascript-conference.com/blog/angular-20-zoneless-mode-performance-migration-guide/)*
|