devendra-skill-package 1.0.2
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/.github/skills/boiler-project-generator/SKILL.md +103 -0
- package/.github/skills/boiler-project-generator/assets/templates/android/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/angular/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/flutter/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/ios/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/react/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/api-setup.ts +76 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/component-primary-button.tsx +29 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/env-config.env +4 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-auth-screen.tsx +45 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-dashboard-screen.tsx +45 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-profile-screen.tsx +45 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/hook-use-app-boot.ts +11 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/navigation-setup.tsx +25 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/project-structure.md +24 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-hooks.ts +5 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-session-slice.ts +29 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-store.ts +15 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/theme-index.ts +23 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/utils-helpers.ts +7 -0
- package/.github/skills/boiler-project-generator/metadata.yaml +29 -0
- package/.github/skills/boiler-project-generator/references/android.md +19 -0
- package/.github/skills/boiler-project-generator/references/angular.md +14 -0
- package/.github/skills/boiler-project-generator/references/flutter.md +17 -0
- package/.github/skills/boiler-project-generator/references/ios.md +17 -0
- package/.github/skills/boiler-project-generator/references/react-native.md +103 -0
- package/.github/skills/boiler-project-generator/references/react.md +17 -0
- package/.github/skills/boiler-project-generator/scripts/angular.js +43 -0
- package/.github/skills/boiler-project-generator/scripts/generate-project.mjs +107 -0
- package/.github/skills/boiler-project-generator/scripts/init.js +105 -0
- package/.github/skills/boiler-project-generator/scripts/react-native.js +1041 -0
- package/.github/skills/boiler-project-generator/scripts/react.js +32 -0
- package/.github/skills/clean-architecture-generator/SKILL.md +235 -0
- package/.github/skills/clean-architecture-generator/references/architecture-core.md +126 -0
- package/.github/skills/clean-architecture-generator/references/platform-angular.md +374 -0
- package/.github/skills/clean-architecture-generator/references/platform-flutter.md +366 -0
- package/.github/skills/clean-architecture-generator/references/platform-react-native.md +590 -0
- package/.github/skills/clean-architecture-generator/references/platform-react.md +261 -0
- package/.github/skills/code-test-coverage-analyzer/SKILL.md +1358 -0
- package/.github/skills/code-test-coverage-analyzer/assets/config-questionnaire.md +118 -0
- package/.github/skills/code-test-coverage-analyzer/assets/conflict-resolution-protocol.md +99 -0
- package/.github/skills/code-test-coverage-analyzer/assets/coverage-calculation-rules.md +294 -0
- package/.github/skills/code-test-coverage-analyzer/assets/multi-strategy-search.md +145 -0
- package/.github/skills/code-test-coverage-analyzer/assets/output-file-contracts.md +651 -0
- package/.github/skills/code-test-coverage-analyzer/assets/status-definitions.md +273 -0
- package/.github/skills/code-test-coverage-analyzer/examples/example-invocation.md +208 -0
- package/.github/skills/code-test-coverage-analyzer/references/android-patterns.md +229 -0
- package/.github/skills/code-test-coverage-analyzer/references/angular-patterns.md +177 -0
- package/.github/skills/code-test-coverage-analyzer/references/backend-patterns.md +383 -0
- package/.github/skills/code-test-coverage-analyzer/references/flutter-patterns.md +254 -0
- package/.github/skills/code-test-coverage-analyzer/references/ios-patterns.md +228 -0
- package/.github/skills/code-test-coverage-analyzer/references/react-native-patterns.md +431 -0
- package/.github/skills/code-test-coverage-analyzer/references/react-patterns.md +206 -0
- package/.github/skills/code-test-coverage-analyzer/scripts/detect-platform.ps1 +261 -0
- package/.github/skills/code-test-coverage-analyzer/scripts/scan-repository.ps1 +242 -0
- package/.github/skills/code-test-coverage-analyzer/templates/brd-compliance-row.md +107 -0
- package/.github/skills/code-test-coverage-analyzer/templates/test-case-row.md +130 -0
- package/.github/skills/code-test-coverage-analyzer/templates/user-story-compliance-row.md +170 -0
- package/.github/skills/custom-font-integrator/SKILL.md +373 -0
- package/.github/skills/custom-font-integrator/assets/FontTestScreen.js +150 -0
- package/.github/skills/custom-font-integrator/references/example-info-plist.xml +21 -0
- package/.github/skills/custom-font-integrator/scripts/setup-fonts.sh +99 -0
- package/.github/skills/encryption-decryption-utility/Instructions.md +282 -0
- package/.github/skills/encryption-decryption-utility/README.md +222 -0
- package/.github/skills/encryption-decryption-utility/SKILL.md +645 -0
- package/.github/skills/encryption-decryption-utility/assets/encryption-config-template.json +13 -0
- package/.github/skills/encryption-decryption-utility/assets/platform-matrix.json +180 -0
- package/.github/skills/encryption-decryption-utility/metadata.yaml +51 -0
- package/.github/skills/encryption-decryption-utility/references/android-crypto.md +157 -0
- package/.github/skills/encryption-decryption-utility/references/angular-crypto.md +130 -0
- package/.github/skills/encryption-decryption-utility/references/approach-1-webcrypto.md +95 -0
- package/.github/skills/encryption-decryption-utility/references/approach-2-rsa-aes-cbc.md +111 -0
- package/.github/skills/encryption-decryption-utility/references/approach-3-aes-sharedkey.md +114 -0
- package/.github/skills/encryption-decryption-utility/references/architecture.md +233 -0
- package/.github/skills/encryption-decryption-utility/references/backend-alignment.md +506 -0
- package/.github/skills/encryption-decryption-utility/references/dependency-guidance.md +234 -0
- package/.github/skills/encryption-decryption-utility/references/ec-implementation.md +832 -0
- package/.github/skills/encryption-decryption-utility/references/error-handling-playbook.md +234 -0
- package/.github/skills/encryption-decryption-utility/references/flutter-crypto.md +105 -0
- package/.github/skills/encryption-decryption-utility/references/ios-crypto.md +338 -0
- package/.github/skills/encryption-decryption-utility/references/native-rsa-implementation.md +373 -0
- package/.github/skills/encryption-decryption-utility/references/node-crypto.md +86 -0
- package/.github/skills/encryption-decryption-utility/references/react-crypto.md +105 -0
- package/.github/skills/encryption-decryption-utility/references/react-native-crypto.md +1001 -0
- package/.github/skills/encryption-decryption-utility/references/react-web-crypto.md +668 -0
- package/.github/skills/encryption-decryption-utility/references/security-checklist.md +176 -0
- package/.github/skills/encryption-decryption-utility/references/web-crypto.md +74 -0
- package/.github/skills/encryption-decryption-utility/scripts/detect.js +128 -0
- package/.github/skills/encryption-decryption-utility/scripts/install.js +1713 -0
- package/.github/skills/encryption-decryption-utility/scripts/setup.js +800 -0
- package/.github/skills/encryption-decryption-utility/template.md +241 -0
- package/.github/skills/encryption-decryption-utility/templates/config-questionnaire.md +467 -0
- package/.github/skills/encryption-decryption-utility/templates/delivery-checklist.md +206 -0
- package/.github/skills/encryption-decryption-utility/templates/implementation-plan.md +304 -0
- package/.github/skills/feature-generator/SKILL.MD +2741 -0
- package/.github/skills/feature-generator/assets/fingerprint-schema.json +385 -0
- package/.github/skills/feature-generator/assets/spec-template.md +172 -0
- package/.github/skills/feature-generator/assets/transform-rules.json +82 -0
- package/.github/skills/feature-generator/references/api-envelope-patterns.md +327 -0
- package/.github/skills/feature-generator/references/screen-logic-patterns.md +399 -0
- package/.github/skills/feature-generator/references/state-library-patterns.md +464 -0
- package/.github/skills/figma-ui-mapper/README.md +34 -0
- package/.github/skills/figma-ui-mapper/SKILL.md +101 -0
- package/.github/skills/figma-ui-mapper/assets/templates/component-map-template.md +30 -0
- package/.github/skills/figma-ui-mapper/assets/templates/generation-report-template.md +49 -0
- package/.github/skills/figma-ui-mapper/metadata.yaml +22 -0
- package/.github/skills/figma-ui-mapper/references/android.md +11 -0
- package/.github/skills/figma-ui-mapper/references/angular.md +18 -0
- package/.github/skills/figma-ui-mapper/references/capacitor.md +18 -0
- package/.github/skills/figma-ui-mapper/references/common.md +23 -0
- package/.github/skills/figma-ui-mapper/references/expo.md +18 -0
- package/.github/skills/figma-ui-mapper/references/flutter.md +18 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/angular/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/capacitor/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/expo/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/flutter/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/ionic/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/kotlin-multiplatform/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/native-android/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/native-ios/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/next/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/nuxt/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/react/rules.md +19 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/react-native/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/svelte/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/vue/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/ionic.md +18 -0
- package/.github/skills/figma-ui-mapper/references/ios.md +11 -0
- package/.github/skills/figma-ui-mapper/references/kotlin-multiplatform.md +18 -0
- package/.github/skills/figma-ui-mapper/references/native-android.md +18 -0
- package/.github/skills/figma-ui-mapper/references/native-ios.md +18 -0
- package/.github/skills/figma-ui-mapper/references/next.md +18 -0
- package/.github/skills/figma-ui-mapper/references/nuxt.md +18 -0
- package/.github/skills/figma-ui-mapper/references/react-figma-guideline.md +21 -0
- package/.github/skills/figma-ui-mapper/references/react-native.md +18 -0
- package/.github/skills/figma-ui-mapper/references/react.md +18 -0
- package/.github/skills/figma-ui-mapper/references/script-usage.md +46 -0
- package/.github/skills/figma-ui-mapper/references/svelte.md +18 -0
- package/.github/skills/figma-ui-mapper/references/vue.md +18 -0
- package/.github/skills/figma-ui-mapper/references/workflow.md +74 -0
- package/.github/skills/figma-ui-mapper/scripts/generate-page-through-figma.js +635 -0
- package/.github/skills/figma-ui-mapper/scripts/generate-react-page-through-figma.js +4 -0
- package/.github/skills/frontend-task-breakdown/SKILL.md +734 -0
- package/.github/skills/md-file-converter/SKILL.md +291 -0
- package/.github/skills/md-file-converter/config/formats.json +114 -0
- package/.github/skills/md-file-converter/extractors/extract-docx.sh +120 -0
- package/.github/skills/md-file-converter/extractors/extract-eml.py +108 -0
- package/.github/skills/md-file-converter/extractors/extract-pdf.sh +97 -0
- package/.github/skills/md-file-converter/extractors/extract-pptx.py +143 -0
- package/.github/skills/md-file-converter/extractors/extract-xlsx.py +175 -0
- package/.github/skills/md-file-converter/scripts/check-tools.sh +47 -0
- package/.github/skills/md-file-converter/scripts/extract-archive.sh +83 -0
- package/.github/skills/md-file-converter/scripts/extract-ocr.sh +77 -0
- package/.github/skills/md-file-converter/scripts/get-metadata.sh +57 -0
- package/.github/skills/md-file-converter/templates/output.md +73 -0
- package/.github/skills/md-file-converter/templates/unsupported-object.md +6 -0
- package/.github/skills/mockAPI-contract-generator/SKILL.md +607 -0
- package/.github/skills/test-case-validator/CHANGELOG.md +155 -0
- package/.github/skills/test-case-validator/CONFIGURATION.md +130 -0
- package/.github/skills/test-case-validator/EXAMPLES.md +50 -0
- package/.github/skills/test-case-validator/QUALITY_SCORING.md +98 -0
- package/.github/skills/test-case-validator/README.md +111 -0
- package/.github/skills/test-case-validator/REPORT_TEMPLATE.md +116 -0
- package/.github/skills/test-case-validator/SKILL.md +209 -0
- package/.github/skills/test-case-validator/SYSTEM_PROMPT.md +127 -0
- package/.github/skills/test-case-validator/TRACEABILITY_ENGINE.md +152 -0
- package/.github/skills/test-case-validator/VALIDATION_RULES.md +411 -0
- package/.github/skills/test-case-validator/config/default.config.yaml +62 -0
- package/.github/skills/test-case-validator/docs/ARCHITECTURE.md +66 -0
- package/.github/skills/test-case-validator/docs/EXTENDING.md +85 -0
- package/.github/skills/test-case-validator/docs/FAQ.md +88 -0
- package/.github/skills/test-case-validator/docs/VERSIONING.md +55 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-brd.md +43 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-test-cases.csv +9 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-user-story.md +29 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/output-report.md +303 -0
- package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/input-spec.md +43 -0
- package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/input-test-files-index.md +56 -0
- package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/output-report.md +274 -0
- package/.github/skills/test-case-validator/prompts/00-applicability-resolution.md +61 -0
- package/.github/skills/test-case-validator/prompts/01-extraction-user-story.md +58 -0
- package/.github/skills/test-case-validator/prompts/02-extraction-brd.md +64 -0
- package/.github/skills/test-case-validator/prompts/03-extraction-test-cases.md +92 -0
- package/.github/skills/test-case-validator/prompts/04-traceability-matrix.md +41 -0
- package/.github/skills/test-case-validator/prompts/05-coverage-analysis.md +46 -0
- package/.github/skills/test-case-validator/prompts/06-missing-scenario-detection.md +32 -0
- package/.github/skills/test-case-validator/prompts/07-duplicate-detection.md +36 -0
- package/.github/skills/test-case-validator/prompts/08-risk-analysis.md +39 -0
- package/.github/skills/test-case-validator/prompts/09-gated-wiring-validation.md +53 -0
- package/.github/skills/test-case-validator/prompts/10-observation-report.md +56 -0
- package/.github/skills/test-case-validator/prompts/11-final-report-assembly.md +57 -0
- package/.github/skills/test-case-validator/templates/coverage-table.md +25 -0
- package/.github/skills/test-case-validator/templates/executive-summary.md +38 -0
- package/.github/skills/test-case-validator/templates/final-summary-block.md +87 -0
- package/.github/skills/test-case-validator/templates/observation-entry.md +41 -0
- package/.github/skills/test-case-validator/templates/rtm-row.md +25 -0
- package/.github/skills/validation-engine-builder/README.md +50 -0
- package/.github/skills/validation-engine-builder/SKILL.md +241 -0
- package/.github/skills/validation-engine-builder/references/core.md +158 -0
- package/.github/skills/validation-engine-builder/references/platform-angular.md +166 -0
- package/.github/skills/validation-engine-builder/references/platform-flutter.md +118 -0
- package/.github/skills/validation-engine-builder/references/platform-react-native.md +170 -0
- package/.github/skills/validation-engine-builder/references/platform-react.md +94 -0
- package/.github/skills/validation-engine-builder/scripts/apply-to-project.js +80 -0
- package/.github/skills/validation-engine-builder/scripts/generate-angular.js +271 -0
- package/.github/skills/validation-engine-builder/scripts/generate-flutter.js +298 -0
- package/.github/skills/validation-engine-builder/scripts/generate-react-native.js +217 -0
- package/.github/skills/validation-engine-builder/scripts/generate-react.js +163 -0
- package/.github/skills/validation-engine-builder/scripts/install-deps.js +53 -0
- package/.github/skills/validation-engine-builder/scripts/shared/detect-platform.js +68 -0
- package/.github/skills/validation-engine-builder/scripts/shared/npm-deps.js +103 -0
- package/.github/skills/validation-engine-builder/scripts/shared/rhf-hook.js +43 -0
- package/.github/skills/validation-engine-builder/scripts/shared/ts-messages-regex.js +62 -0
- package/.github/skills/validation-engine-builder/scripts/shared/util.js +44 -0
- package/.github/skills/validation-engine-builder/scripts/shared/yup-schemas.js +80 -0
- package/.github/skills/validation-engine-builder/template.md +73 -0
- package/.github/skills/validation-engine-builder/templates/messages.json +22 -0
- package/.github/skills/validation-engine-builder/templates/regex.json +16 -0
- package/.github/skills/validation-engine-builder/templates/schemas.json +48 -0
- package/bin/index.js +145 -0
- package/package.json +22 -0
- package/readme.md +0 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
# Platform File — Angular
|
|
2
|
+
|
|
3
|
+
Read `architecture-core.md` first. Angular differs from the React-family platforms in one
|
|
4
|
+
structural way that changes how DI is implemented: **Angular has a built-in, framework-native
|
|
5
|
+
dependency injection system.** The manual "DI Pattern A / Pattern B" containers used for React
|
|
6
|
+
Native and React do not apply here — do not port them. Use Angular's own `@Injectable` /
|
|
7
|
+
`providers` mechanism instead (detailed below).
|
|
8
|
+
|
|
9
|
+
Everything under "Stack Fingerprint Defaults" applies **only** when Phase 1 detection found nothing
|
|
10
|
+
in the target repo.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Sub-Variant Detection (run before anything else)
|
|
15
|
+
|
|
16
|
+
| Signal | Sub-variant | Affects |
|
|
17
|
+
|---|---|---|
|
|
18
|
+
| `src/app/app.config.ts` exists, no `app.module.ts` | Standalone components (Angular 14+) | Feature wiring goes in each component's `providers` array or `app.config.ts`; no `NgModule` is created |
|
|
19
|
+
| `src/app/app.module.ts` exists | NgModule-based | Feature wiring goes in a `[Feature]Module` with a `providers` array |
|
|
20
|
+
|
|
21
|
+
State the detected sub-variant before generating any file — it changes where the repository→use
|
|
22
|
+
case provider mapping is registered.
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Stack Fingerprint Defaults (greenfield only)
|
|
27
|
+
|
|
28
|
+
| Concern | Detect first (search for) | Default if nothing detected |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| Language | — | TypeScript, strict mode |
|
|
31
|
+
| State management | `@ngrx/store` imports (`createReducer`, `createFeature`), `@ngrx/signals`, `@datorama/akita`, `@ngxs/store`, `signal(`/`computed(` from `@angular/core` used as app state | Plain RxJS: an injectable `[Feature]Store` service holding a `BehaviorSubject` per concern, exposed as `Observable` |
|
|
32
|
+
| HTTP client | `HttpClient` from `@angular/common/http` (this is Angular's standard client — still verify it's actually imported, not a wrapped Axios instance) | `HttpClient` with a functional `HttpInterceptorFn` |
|
|
33
|
+
| Storage | existing `StorageService`/`localStorage`/`sessionStorage` wrapper | injectable `StorageService` wrapping `sessionStorage` for tokens, `localStorage` for non-sensitive UI state |
|
|
34
|
+
| Routing | `app.routes.ts` / `RouterModule.forRoot(` | Angular Router with lazy-loaded feature routes |
|
|
35
|
+
| DI mechanism | — (Angular always has DI) | `@Injectable({ providedIn: 'root' })` for app-wide singletons, or feature-scoped providers array for feature-scoped instances |
|
|
36
|
+
| Test runner | `karma.conf.js`, `jest.config.*` | Whatever the project already runs (Angular CLI defaults to Karma+Jasmine; many teams migrate to Jest) — do not silently switch an existing project's test runner |
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## Folder Tree
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
src/app/
|
|
44
|
+
├── app.component.ts
|
|
45
|
+
├── app.config.ts # standalone bootstrap providers (or app.module.ts if NgModule-based)
|
|
46
|
+
├── app.routes.ts
|
|
47
|
+
│
|
|
48
|
+
├── core/ # Infrastructure — shared by all features
|
|
49
|
+
│ ├── api/
|
|
50
|
+
│ │ ├── api-error-handler.ts
|
|
51
|
+
│ │ ├── app-error.ts
|
|
52
|
+
│ │ └── token.provider.ts # Token getter/setter (prevents circular deps)
|
|
53
|
+
│ ├── interceptors/
|
|
54
|
+
│ │ ├── auth.interceptor.ts # attaches Authorization header
|
|
55
|
+
│ │ └── error.interceptor.ts # maps HTTP errors to AppError
|
|
56
|
+
│ ├── guards/
|
|
57
|
+
│ │ └── auth.guard.ts # CanActivateFn
|
|
58
|
+
│ ├── storage/
|
|
59
|
+
│ │ └── storage.service.ts
|
|
60
|
+
│ └── logging/
|
|
61
|
+
│ └── logger.service.ts
|
|
62
|
+
│
|
|
63
|
+
├── features/
|
|
64
|
+
│ └── [feature-name]/
|
|
65
|
+
│ ├── domain/ # Pure TypeScript — ZERO Angular imports
|
|
66
|
+
│ │ ├── entities/
|
|
67
|
+
│ │ │ └── [item].entity.ts
|
|
68
|
+
│ │ ├── repositories/
|
|
69
|
+
│ │ │ └── [feature].repository.ts # abstract class — see DI section
|
|
70
|
+
│ │ └── use-cases/
|
|
71
|
+
│ │ └── get-[item].use-case.ts
|
|
72
|
+
│ ├── data/
|
|
73
|
+
│ │ ├── dtos/
|
|
74
|
+
│ │ │ └── [item].dto.ts
|
|
75
|
+
│ │ ├── mappers/
|
|
76
|
+
│ │ │ └── [feature].mapper.ts
|
|
77
|
+
│ │ └── repositories/
|
|
78
|
+
│ │ └── [feature].repository.impl.ts
|
|
79
|
+
│ ├── presentation/
|
|
80
|
+
│ │ ├── pages/
|
|
81
|
+
│ │ │ └── [feature]-page/
|
|
82
|
+
│ │ │ ├── [feature]-page.component.ts
|
|
83
|
+
│ │ │ ├── [feature]-page.component.html
|
|
84
|
+
│ │ │ └── [feature]-page.component.scss
|
|
85
|
+
│ │ ├── components/
|
|
86
|
+
│ │ └── state/
|
|
87
|
+
│ │ └── [feature].store.ts # RxJS store service, or NgRx feature slice if detected
|
|
88
|
+
│ ├── [feature].routes.ts
|
|
89
|
+
│ └── [feature].providers.ts # provider array wiring [Feature]Repository → [Feature]RepositoryImpl
|
|
90
|
+
│
|
|
91
|
+
└── shared/
|
|
92
|
+
└── ui/ # Reusable presentational components with no feature dependency
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Naming Extensions (Angular-specific — official Angular style guide, not invented)
|
|
98
|
+
|
|
99
|
+
Angular's own style guide uses **kebab-case file names with a dot-separated type suffix**, and
|
|
100
|
+
explicitly recommends **against** an `I`-prefix on interface/abstraction names (no Hungarian
|
|
101
|
+
notation). This is different from the RN/React convention in this skill and must not be mixed in:
|
|
102
|
+
|
|
103
|
+
| Artifact | Convention | Example |
|
|
104
|
+
|----------|------------|---------|
|
|
105
|
+
| Feature folder | kebab-case | `portfolio-summary`, `user-profile` |
|
|
106
|
+
| Entity file | kebab-case + `.entity.ts` | `holding.entity.ts` (class `HoldingEntity`) |
|
|
107
|
+
| Repository abstraction | kebab-case + `.repository.ts`, **no `I` prefix** | `holding.repository.ts` (abstract class `HoldingRepository`) |
|
|
108
|
+
| Repository implementation | kebab-case + `.repository.impl.ts` | `holding.repository.impl.ts` (class `HoldingRepositoryImpl`) |
|
|
109
|
+
| Use case file | kebab-case + `.use-case.ts` | `get-holding-summary.use-case.ts` (class `GetHoldingSummaryUseCase`) |
|
|
110
|
+
| DTO file | kebab-case + `.dto.ts` | `holding-summary.dto.ts` |
|
|
111
|
+
| Mapper file | kebab-case + `.mapper.ts`, function named `mapTo…` | `holding.mapper.ts` → `mapHoldingDtoToDomain` |
|
|
112
|
+
| Component (page) | kebab-case + `.component.ts` | `holding-detail-page.component.ts` (class `HoldingDetailPageComponent`) |
|
|
113
|
+
| Store/state service | kebab-case + `.store.ts` | `holding.store.ts` (class `HoldingStore`) |
|
|
114
|
+
| Route guard | kebab-case + `.guard.ts` | `auth.guard.ts` |
|
|
115
|
+
| Interceptor | kebab-case + `.interceptor.ts` | `auth.interceptor.ts` |
|
|
116
|
+
| Providers file | kebab-case + `.providers.ts` | `holding.providers.ts` |
|
|
117
|
+
| Test file | same base name + `.spec.ts` | `holding.repository.impl.spec.ts` |
|
|
118
|
+
| Loading state | `'idle' \| 'loading' \| 'succeeded' \| 'failed'` string union | never a `boolean` |
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## DI — Angular's Framework-Native Mechanism (do not use RN/React manual containers)
|
|
123
|
+
|
|
124
|
+
**Why this is different:** a TypeScript `interface` has no runtime representation, so Angular's DI
|
|
125
|
+
cannot inject one directly. The correct Angular idiom is to make the repository contract an
|
|
126
|
+
**abstract class** (which does have a runtime token) and bind the concrete implementation to it via
|
|
127
|
+
a `providers` array — this is how Angular's own documentation and the wider community express
|
|
128
|
+
"interface-based DI."
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
// domain/repositories/[feature].repository.ts
|
|
132
|
+
import { Observable } from 'rxjs';
|
|
133
|
+
import type { [Item]Entity } from '../entities/[item].entity';
|
|
134
|
+
|
|
135
|
+
export abstract class [Feature]Repository {
|
|
136
|
+
abstract get[Item](args: [RequestArgs]): Observable<[Item]Entity>;
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
// domain/use-cases/get-[item].use-case.ts
|
|
142
|
+
import { Injectable } from '@angular/core';
|
|
143
|
+
import { Observable } from 'rxjs';
|
|
144
|
+
import { [Feature]Repository } from '../repositories/[feature].repository';
|
|
145
|
+
import type { [Item]Entity } from '../entities/[item].entity';
|
|
146
|
+
|
|
147
|
+
@Injectable()
|
|
148
|
+
export class Get[Item]UseCase {
|
|
149
|
+
constructor(private readonly repository: [Feature]Repository) {}
|
|
150
|
+
|
|
151
|
+
execute(args: [RequestArgs]): Observable<[Item]Entity> {
|
|
152
|
+
return this.repository.get[Item](args);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
// data/repositories/[feature].repository.impl.ts
|
|
159
|
+
import { Injectable } from '@angular/core';
|
|
160
|
+
import { HttpClient } from '@angular/common/http';
|
|
161
|
+
import { Observable, map } from 'rxjs';
|
|
162
|
+
import { [Feature]Repository } from '../../domain/repositories/[feature].repository';
|
|
163
|
+
import type { [Item]Entity } from '../../domain/entities/[item].entity';
|
|
164
|
+
import type { [Item]Dto } from '../dtos/[item].dto';
|
|
165
|
+
import { map[Item]DtoToDomain } from '../mappers/[feature].mapper';
|
|
166
|
+
|
|
167
|
+
@Injectable()
|
|
168
|
+
export class [Feature]RepositoryImpl extends [Feature]Repository {
|
|
169
|
+
constructor(private readonly http: HttpClient) { super(); }
|
|
170
|
+
|
|
171
|
+
get[Item](args: [RequestArgs]): Observable<[Item]Entity> {
|
|
172
|
+
return this.http.post<[Item]Dto>('/[endpoint]', args).pipe(map(map[Item]DtoToDomain));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
```ts
|
|
178
|
+
// features/[feature-name]/[feature].providers.ts
|
|
179
|
+
import type { Provider } from '@angular/core';
|
|
180
|
+
import { [Feature]Repository } from './domain/repositories/[feature].repository';
|
|
181
|
+
import { [Feature]RepositoryImpl } from './data/repositories/[feature].repository.impl';
|
|
182
|
+
import { Get[Item]UseCase } from './domain/use-cases/get-[item].use-case';
|
|
183
|
+
|
|
184
|
+
export const [FEATURE]_PROVIDERS: Provider[] = [
|
|
185
|
+
{ provide: [Feature]Repository, useClass: [Feature]RepositoryImpl },
|
|
186
|
+
Get[Item]UseCase,
|
|
187
|
+
];
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Register `[FEATURE]_PROVIDERS` in exactly one place:
|
|
191
|
+
- **Standalone**: the feature's routed component's `providers` array, or on the lazy route in
|
|
192
|
+
`app.routes.ts` (`{ path: '[feature]', providers: [FEATURE]_PROVIDERS], loadComponent: … }`) so
|
|
193
|
+
the providers are scoped to the feature, not app-wide.
|
|
194
|
+
- **NgModule**: the feature's `[Feature]Module`'s `providers` array.
|
|
195
|
+
|
|
196
|
+
**Never** add `providedIn: 'root'` to a feature-specific repository/use-case unless the feature is
|
|
197
|
+
genuinely meant to be a global singleton (auth is a legitimate exception, same as in RN/React) —
|
|
198
|
+
feature-scoped providers keep one feature from leaking its DI graph into another.
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## State Pattern — Plain RxJS Store (default when no NgRx/Akita/Signals detected)
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
// presentation/state/[feature].store.ts
|
|
206
|
+
import { Injectable } from '@angular/core';
|
|
207
|
+
import { BehaviorSubject, Observable } from 'rxjs';
|
|
208
|
+
import type { [Item]Entity } from '../../domain/entities/[item].entity';
|
|
209
|
+
|
|
210
|
+
export type LoadingState = 'idle' | 'loading' | 'succeeded' | 'failed';
|
|
211
|
+
|
|
212
|
+
interface [Feature]State {
|
|
213
|
+
data: [Item]Entity | null;
|
|
214
|
+
loading: LoadingState;
|
|
215
|
+
error: string | null;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const initialState: [Feature]State = { data: null, loading: 'idle', error: null };
|
|
219
|
+
|
|
220
|
+
@Injectable()
|
|
221
|
+
export class [Feature]Store {
|
|
222
|
+
private readonly state$ = new BehaviorSubject<[Feature]State>(initialState);
|
|
223
|
+
|
|
224
|
+
readonly data$: Observable<[Item]Entity | null> = this.state$.pipe(map(s => s.data));
|
|
225
|
+
readonly loading$: Observable<LoadingState> = this.state$.pipe(map(s => s.loading));
|
|
226
|
+
readonly error$: Observable<string | null> = this.state$.pipe(map(s => s.error));
|
|
227
|
+
|
|
228
|
+
constructor(private readonly get[Item]UseCase: Get[Item]UseCase) {}
|
|
229
|
+
|
|
230
|
+
load(args: [RequestArgs]): void {
|
|
231
|
+
this.patch({ loading: 'loading', error: null });
|
|
232
|
+
this.get[Item]UseCase.execute(args).subscribe({
|
|
233
|
+
next: (data) => this.patch({ data, loading: 'succeeded' }),
|
|
234
|
+
error: (err) => this.patch({ loading: 'failed', error: err?.message ?? 'Unknown error' }),
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
private patch(partial: Partial<[Feature]State>): void {
|
|
239
|
+
this.state$.next({ ...this.state$.value, ...partial });
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Register `[Feature]Store` in the same `providers` array as the repository/use case. Component
|
|
245
|
+
subscribes via the `async` pipe in the template (`data$ | async`) — never a manual `.subscribe()`
|
|
246
|
+
in a component without unsubscribing (see Anti-Patterns).
|
|
247
|
+
|
|
248
|
+
**If NgRx (or another state library) is detected** in the target repo, use that instead — a feature
|
|
249
|
+
slice/store keyed to the feature, following the same file location (`presentation/state/`), not
|
|
250
|
+
this BehaviorSubject pattern. Do not introduce a second state-management library alongside an
|
|
251
|
+
existing one.
|
|
252
|
+
|
|
253
|
+
## HTTP Interceptor Pattern
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
// core/interceptors/auth.interceptor.ts
|
|
257
|
+
import type { HttpInterceptorFn } from '@angular/common/http';
|
|
258
|
+
import { getToken } from '../api/token.provider';
|
|
259
|
+
|
|
260
|
+
export const authInterceptor: HttpInterceptorFn = (req, next) => {
|
|
261
|
+
const token = getToken();
|
|
262
|
+
if (!token) return next(req);
|
|
263
|
+
return next(req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }));
|
|
264
|
+
};
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Register in `app.config.ts`: `provideHttpClient(withInterceptors([authInterceptor, errorInterceptor]))`.
|
|
268
|
+
|
|
269
|
+
## Route Guard Pattern
|
|
270
|
+
|
|
271
|
+
```ts
|
|
272
|
+
// core/guards/auth.guard.ts
|
|
273
|
+
import type { CanActivateFn } from '@angular/router';
|
|
274
|
+
import { inject } from '@angular/core';
|
|
275
|
+
import { Router } from '@angular/router';
|
|
276
|
+
import { AuthTokenStore } from '../../features/auth/presentation/state/auth-token.store';
|
|
277
|
+
|
|
278
|
+
export const authGuard: CanActivateFn = () => {
|
|
279
|
+
const hasToken = !!inject(AuthTokenStore).getToken();
|
|
280
|
+
if (hasToken) return true;
|
|
281
|
+
inject(Router).navigate(['/login']);
|
|
282
|
+
return false;
|
|
283
|
+
};
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
## Route Definition Pattern
|
|
287
|
+
|
|
288
|
+
```ts
|
|
289
|
+
// features/[feature-name]/[feature].routes.ts
|
|
290
|
+
import type { Routes } from '@angular/router';
|
|
291
|
+
import { [FEATURE]_PROVIDERS } from './[feature].providers';
|
|
292
|
+
|
|
293
|
+
export const [FEATURE]_ROUTES: Routes = [
|
|
294
|
+
{
|
|
295
|
+
path: '',
|
|
296
|
+
providers: [FEATURE]_PROVIDERS,
|
|
297
|
+
children: [
|
|
298
|
+
{ path: '', loadComponent: () => import('./presentation/pages/[feature]-page/[feature]-page.component').then(m => m.[Feature]PageComponent) },
|
|
299
|
+
{ path: ':id', loadComponent: () => import('./presentation/pages/[feature]-detail-page/[feature]-detail-page.component').then(m => m.[Feature]DetailPageComponent) },
|
|
300
|
+
],
|
|
301
|
+
},
|
|
302
|
+
];
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Mount with `{ path: '[feature]', loadChildren: () => import('./features/[feature-name]/[feature].routes').then(m => m.[FEATURE]_ROUTES) }` in the parent `app.routes.ts`.
|
|
306
|
+
|
|
307
|
+
## Domain / Data Layer Patterns
|
|
308
|
+
|
|
309
|
+
See the DI section above — the abstract-class repository and its implementation together constitute
|
|
310
|
+
both the domain contract and the data-layer implementation pattern for Angular.
|
|
311
|
+
|
|
312
|
+
---
|
|
313
|
+
|
|
314
|
+
## Security Rules (Angular/browser-specific — on top of architecture-core.md)
|
|
315
|
+
|
|
316
|
+
1. There is no OS Keychain in a browser. Prefer an httpOnly cookie set by the server for tokens; if
|
|
317
|
+
client-side storage is required, prefer in-memory (a service field) with refresh-token rotation
|
|
318
|
+
over `localStorage`.
|
|
319
|
+
2. **Never** use `localStorage` for tokens without explicitly flagging the XSS-persistence tradeoff
|
|
320
|
+
to the user — same rule as `platform-react.md`.
|
|
321
|
+
3. Angular's built-in sanitization (`DomSanitizer`) protects template bindings from XSS by default —
|
|
322
|
+
**never** call `bypassSecurityTrustHtml`/`bypassSecurityTrustScript` on user-controlled content.
|
|
323
|
+
|
|
324
|
+
---
|
|
325
|
+
|
|
326
|
+
## Anti-Patterns (Angular-specific, on top of architecture-core.md)
|
|
327
|
+
|
|
328
|
+
- **Never** try to inject a plain TypeScript `interface` — Angular's DI has no runtime token for it.
|
|
329
|
+
Use an abstract class (see DI section) or an `InjectionToken<T>` with a factory provider.
|
|
330
|
+
- **Never** call `HttpClient` directly from a component — always go through the feature's
|
|
331
|
+
`[Feature]Repository` via constructor injection.
|
|
332
|
+
- **Never** put business logic in a component class — it belongs in a use case.
|
|
333
|
+
- **Never** subscribe to an `Observable` in a component without unsubscribing — use the `async`
|
|
334
|
+
pipe, or `takeUntilDestroyed()` (Angular 16+) if a manual subscription is unavoidable. An
|
|
335
|
+
un-unsubscribed subscription is a memory leak on every navigation away from the component.
|
|
336
|
+
- **Never** register a feature's providers with `providedIn: 'root'` unless the feature is
|
|
337
|
+
genuinely meant to be a global singleton — keep feature DI scoped to the feature's route/component.
|
|
338
|
+
- **Never** mix the `I`-prefix convention from `platform-react-native.md`/`platform-react.md` into
|
|
339
|
+
Angular code — Angular naming has no `I` prefix (see Naming Extensions).
|
|
340
|
+
- **Never** use `any` — use a specific type or `unknown` with a type guard.
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
## Worked Example
|
|
345
|
+
|
|
346
|
+
**Request**: "Add a watchlist feature with ability to fetch and display watchlist items."
|
|
347
|
+
|
|
348
|
+
**Artifacts**:
|
|
349
|
+
- Domain: `holding.entity.ts` → `WatchlistItemEntity`; `watchlist.repository.ts` (abstract class) → `WatchlistRepository`; `get-watchlist.use-case.ts` → `GetWatchlistUseCase`
|
|
350
|
+
- Data: `watchlist-item.dto.ts`, `watchlist.mapper.ts` → `mapWatchlistItemDtoToDomain`, `watchlist.repository.impl.ts` → `WatchlistRepositoryImpl`
|
|
351
|
+
- Presentation: `watchlist.store.ts` → `WatchlistStore`, `watchlist-page.component.ts`/`.html`/`.scss`
|
|
352
|
+
- Wiring: `watchlist.providers.ts`, `watchlist.routes.ts`
|
|
353
|
+
|
|
354
|
+
**Output**:
|
|
355
|
+
```
|
|
356
|
+
Created:
|
|
357
|
+
src/app/features/watchlist/domain/entities/watchlist-item.entity.ts
|
|
358
|
+
src/app/features/watchlist/domain/repositories/watchlist.repository.ts
|
|
359
|
+
src/app/features/watchlist/domain/use-cases/get-watchlist.use-case.ts
|
|
360
|
+
src/app/features/watchlist/data/dtos/watchlist-item.dto.ts
|
|
361
|
+
src/app/features/watchlist/data/mappers/watchlist.mapper.ts
|
|
362
|
+
src/app/features/watchlist/data/repositories/watchlist.repository.impl.ts
|
|
363
|
+
src/app/features/watchlist/presentation/state/watchlist.store.ts
|
|
364
|
+
src/app/features/watchlist/presentation/pages/watchlist-page/watchlist-page.component.ts
|
|
365
|
+
src/app/features/watchlist/presentation/pages/watchlist-page/watchlist-page.component.html
|
|
366
|
+
src/app/features/watchlist/presentation/pages/watchlist-page/watchlist-page.component.scss
|
|
367
|
+
src/app/features/watchlist/watchlist.providers.ts
|
|
368
|
+
src/app/features/watchlist/watchlist.routes.ts
|
|
369
|
+
|
|
370
|
+
Updated:
|
|
371
|
+
src/app/app.routes.ts (added lazy-loaded watchlist route)
|
|
372
|
+
|
|
373
|
+
Dependency check: PASS — no layer boundary violations detected
|
|
374
|
+
```
|