@tungvivas/angular-vibe-kit 0.1.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.
@@ -0,0 +1,210 @@
1
+ # Angular Best Practices — v20+ (Signal-Native Era)
2
+
3
+ > Framework-level patterns only. This file describes HOW to write idiomatic
4
+ > Angular 20+ — syntax, DI, components, Signals. It does NOT dictate folder
5
+ > structure, state-management choice, or naming — those are project decisions
6
+ > recorded in `.claude/rules/project-rules.md` (inferred from the codebase).
7
+ >
8
+ > When the project already follows a correct pattern, follow the project.
9
+ > When it is below standard, apply this standard to NEW code only.
10
+
11
+ ---
12
+
13
+ ## Module System
14
+ - **Standalone is implicit** — `standalone: true` no longer needs to be written.
15
+ Components, directives, and pipes are standalone by default.
16
+ - Bootstrap: `bootstrapApplication(AppComponent, appConfig)`.
17
+ - Providers in `app.config.ts` (`provideRouter`, `provideHttpClient`, etc.).
18
+ - NgModules are fully legacy — do NOT add new ones.
19
+
20
+ ## Component Pattern
21
+ - `ChangeDetectionStrategy.OnPush` everywhere — or **zoneless** (see below).
22
+ - **Signal-based APIs are the default** for all inputs, outputs, queries:
23
+
24
+ ```typescript
25
+ @Component({
26
+ selector: 'app-user-card',
27
+ imports: [DatePipe],
28
+ templateUrl: './user-card.component.html',
29
+ changeDetection: ChangeDetectionStrategy.OnPush,
30
+ })
31
+ export class UserCardComponent {
32
+ // Signal inputs (replaces @Input)
33
+ user = input.required<User>();
34
+ showActions = input(true); // with default
35
+
36
+ // Two-way binding (replaces @Input + @Output pair)
37
+ selectedId = model<number | null>(null);
38
+
39
+ // Signal output (replaces @Output + EventEmitter)
40
+ delete = output<number>();
41
+
42
+ // Signal queries (replaces @ViewChild / @ContentChild)
43
+ tableRef = viewChild<ElementRef>('table');
44
+
45
+ // Derived state
46
+ displayName = computed(() => `${this.user().name} (${this.user().email})`);
47
+
48
+ onDeleteClick(): void {
49
+ this.delete.emit(this.user().id);
50
+ }
51
+ }
52
+ ```
53
+
54
+ - `@Input()` / `@Output()` / `@ViewChild()` still work but are legacy — use signal APIs for all new code.
55
+
56
+ ## Template Syntax
57
+ - `@if`, `@else`, `@for` (required `track`), `@switch`, `@defer`, `@empty`, `@placeholder`.
58
+ - **`@let`** — declare template-local variables (stable since v18.1):
59
+
60
+ ```html
61
+ @let user = currentUser();
62
+ @let greeting = 'Hello, ' + user.name;
63
+
64
+ <h1>{{ greeting }}</h1>
65
+
66
+ @if (isLoading()) {
67
+ <app-spinner />
68
+ } @else {
69
+ @for (item of items(); track item.id) {
70
+ <app-item-card [item]="item" />
71
+ } @empty {
72
+ <app-empty-state />
73
+ }
74
+ }
75
+
76
+ @defer (on viewport) {
77
+ <app-heavy-chart [data]="chartData()" />
78
+ } @placeholder {
79
+ <div class="chart-skeleton" />
80
+ }
81
+ ```
82
+
83
+ ## Services & Dependency Injection
84
+ - `inject()` everywhere — no constructor injection.
85
+ - `@Injectable({ providedIn: 'root' })` for singletons.
86
+ - HttpClient ONLY in services; services return `Observable<T>` or use `resource()`.
87
+
88
+ ## HTTP
89
+ - `provideHttpClient(withInterceptors([...]))` with functional interceptors.
90
+ - **`httpResource()`** for declarative GET requests tied to signals — ⚠️ **experimental in v20** (ready to try, API may change). Use only if the team accepts experimental APIs; otherwise stick to plain HttpClient:
91
+
92
+ ```typescript
93
+ // Declarative: auto-refetches when userId signal changes (experimental API)
94
+ readonly userResource = httpResource<User>(
95
+ () => `/api/users/${this.userId()}`
96
+ );
97
+ // userResource.value() → User | undefined
98
+ // userResource.isLoading() → boolean
99
+ // userResource.error() → unknown
100
+ ```
101
+
102
+ - Plain `HttpClient` + `Observable` for mutations (POST/PUT/DELETE) and complex flows.
103
+ - Functional interceptors:
104
+
105
+ ```typescript
106
+ export const authInterceptor: HttpInterceptorFn = (req, next) => {
107
+ const token = inject(AuthService).accessToken();
108
+ return token
109
+ ? next(req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }))
110
+ : next(req);
111
+ };
112
+ ```
113
+
114
+ ## Routing
115
+ - `loadComponent()` / `loadChildren()` for lazy loading.
116
+ - Functional guards (`CanActivateFn`) and resolvers (`ResolveFn`).
117
+ - **Route-level render mode** (SSR): configure per-route in `app.routes.ts` when using SSR.
118
+
119
+ ## Reactive / Signals (signal-native)
120
+
121
+ Signal primitives:
122
+ | API | Purpose | Status (v20) |
123
+ |-----|---------|--------------|
124
+ | `signal(val)` | Writable state | Stable |
125
+ | `computed(() => ...)` | Derived state (memoized) | Stable |
126
+ | `effect(() => ...)` | Side effects when signals change | Stable |
127
+ | `linkedSignal(() => ...)` | Writable signal derived from another | Stable (graduated in v20) |
128
+ | `resource()` / `rxResource()` | Async state (loading/error/value) | ⚠️ Experimental |
129
+ | `httpResource()` | HTTP GET tied to signals | ⚠️ Experimental |
130
+ | `toSignal(obs$)` | Bridge Observable → Signal | Stable |
131
+ | `toObservable(sig)` | Bridge Signal → Observable | Stable |
132
+
133
+ Rules:
134
+ - `computed()` for all derived state — never store redundant computed data in a separate signal.
135
+ - `effect()` only for side effects (logging, DOM, third-party libs) — NOT for updating other signals (use `computed` or `linkedSignal` instead). In v20, signal writes inside `effect()` are allowed by default (no `allowSignalWrites`).
136
+ - `resource()` / `rxResource()` / `httpResource()` are **experimental** — use for async data only if the team accepts experimental APIs; otherwise use plain HttpClient + Observable. Use plain HttpClient for all mutations regardless.
137
+ - Subscriptions (when still needed): `takeUntilDestroyed(inject(DestroyRef))`.
138
+
139
+ ```typescript
140
+ export class UserListComponent {
141
+ private userService = inject(UserService);
142
+ private destroyRef = inject(DestroyRef);
143
+
144
+ // Filter signal drives the resource
145
+ filter = signal('');
146
+ page = signal(1);
147
+
148
+ usersResource = httpResource<PaginationResponse<User>>(
149
+ () => `/api/users?page=${this.page()}&search=${this.filter()}`
150
+ );
151
+
152
+ userCount = computed(() => this.usersResource.value()?.meta.total ?? 0);
153
+ }
154
+ ```
155
+
156
+ ## Zoneless Change Detection (stable in v20)
157
+ - New projects: configure with `provideZonelessChangeDetection()` in `app.config.ts`.
158
+ - Existing projects migrating: replace `provideZoneChangeDetection` carefully — test thoroughly.
159
+ - In zoneless mode: CD is driven entirely by signals and `markForCheck()`. NEVER rely on `setTimeout`, `Promise.then`, or other macrotask-based CD triggers.
160
+ - `async` pipe still works in zoneless mode.
161
+
162
+ ```typescript
163
+ // app.config.ts — zoneless
164
+ export const appConfig: ApplicationConfig = {
165
+ providers: [
166
+ provideZonelessChangeDetection(),
167
+ provideRouter(routes),
168
+ provideHttpClient(withInterceptors([authInterceptor])),
169
+ ],
170
+ };
171
+ ```
172
+
173
+ ## Forms
174
+ - Reactive Forms (`NonNullableFormBuilder`) for all validated forms.
175
+ - **Signal-based forms** (developer preview in v20) — use only if the team opts in explicitly.
176
+ - Show validation messages when control is `touched` and `invalid`.
177
+
178
+ ## TypeScript
179
+ - No `any`. Signal types are inferred — do not add redundant annotations.
180
+ - `interface` for object shapes, `type` for unions/mapped types.
181
+ - `readonly` on arrays and objects that must not be mutated.
182
+
183
+ ## Testing
184
+ - Unit (service): `provideHttpClientTesting()` + `HttpTestingController`.
185
+ - Component: `TestBed` + mock services; assert signal values with `TestBed.flushEffects()` where needed.
186
+ - E2E: Playwright.
187
+ - Runner: Jest or Web Test Runner / Vitest (Karma is legacy — check what the project uses).
188
+
189
+ ## Security
190
+ - Tokens in memory (signal in service) or httpOnly cookie — NOT localStorage.
191
+ - No hardcoded backend URL — use `environment`.
192
+ - Route guards for all auth-required routes.
193
+
194
+ ---
195
+
196
+ ## What's New vs v18-19
197
+
198
+ | Feature | v18-19 | v20+ |
199
+ |---------|--------|------|
200
+ | Signal inputs/outputs | Production-ready (v19) | Default; `@Input`/`@Output` legacy |
201
+ | Signals core (`signal`/`effect`/`linkedSignal`) | Stable/preview mix | **All graduated to stable** |
202
+ | `linkedSignal()` | Experimental (v19) | **Stable** |
203
+ | `httpResource()` / `resource()` / `rxResource()` | Experimental (v19.2+) | ⚠️ **Still experimental** |
204
+ | Zoneless CD | Experimental (`provideExperimentalZonelessChangeDetection`) | **Stable** (`provideZonelessChangeDetection`) |
205
+ | `effect()` signal writes | Needed `allowSignalWrites` | **Allowed by default** |
206
+ | Incremental hydration (`@defer`-powered) | Developer preview (since v17) | **Stable** |
207
+ | Signal-based forms | — | Experimental / developer preview |
208
+ | `@let` template variables | Stable (since v18.1) | Stable |
209
+ | `standalone: true` | Implicit default since **v19** | Implicit default (carried over) |
210
+ | Route-level render mode (SSR) | Preview | **Stable** |
@@ -0,0 +1,55 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code when working with code in this repository.
4
+
5
+ > Generated by `angular-vibe-kit`. Run `/init` to populate this file from your codebase.
6
+ > Keep under 200 lines — link out to docs/ for details.
7
+
8
+ ## Read first
9
+ - [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) — layers, routing, state, request flow
10
+ - [docs/API_CONTRACT.md](docs/API_CONTRACT.md) — endpoints the frontend calls + payload shapes
11
+ - [docs/DESIGN_SYSTEM.md](docs/DESIGN_SYSTEM.md) — UI library, theme, shared components
12
+ - [docs/PROJECT-STATUS.md](docs/PROJECT-STATUS.md) — current progress, next tasks, warnings
13
+ - `.claude/angular-practices/` — Angular version idioms for this project (auto-detected)
14
+ - Feature `CONTEXT.md` files when modifying a specific feature
15
+
16
+ ## Common commands
17
+ > Run `/init` — this section will be populated with commands from your package.json.
18
+
19
+ ```bash
20
+ # Run dev server
21
+ ng serve
22
+
23
+ # Build production
24
+ ng build --configuration production
25
+
26
+ # Unit tests
27
+ ng test # or: jest
28
+
29
+ # E2E tests
30
+ npx playwright test
31
+
32
+ # Lint + type check
33
+ ng lint
34
+ npx tsc --noEmit
35
+ ```
36
+
37
+ ## Runtime and tooling
38
+ > Run `/init` — this section will be populated with your detected stack.
39
+
40
+ - Angular version: {{detected by /init}}
41
+ - State management: {{detected by /init}}
42
+ - UI library: {{detected by /init}}
43
+ - Test runner: {{detected by /init}}
44
+
45
+ ## High-level architecture
46
+ > Run `/init` — this section will summarize your detected architecture.
47
+ > Full details in [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
48
+
49
+ ## When updating docs
50
+ If behavior changes, update the related docs in the same commit:
51
+ - `docs/PROJECT-STATUS.md` for session progress
52
+ - `docs/API_CONTRACT.md` for endpoint changes
53
+ - `docs/DESIGN_SYSTEM.md` for UI/token changes
54
+ - Feature `CONTEXT.md` for non-obvious module behavior
55
+ - `docs/decisions/` for meaningful architectural decisions
@@ -0,0 +1,46 @@
1
+ # API Contract
2
+
3
+ > Endpoints the frontend calls, mapped to Angular services.
4
+ > Run `/init` to populate from existing service files.
5
+ > Update whenever a service starts calling a new/changed endpoint.
6
+
7
+ ## Base URL
8
+ ```
9
+ Development: {{from environment.ts}}
10
+ Production: {{from environment.prod.ts}}
11
+ ```
12
+
13
+ ## Response Envelope
14
+ ```typescript
15
+ interface ApiResponse<T> {
16
+ statusCode: number;
17
+ data: T;
18
+ message: string;
19
+ }
20
+
21
+ interface PaginationResponse<T> {
22
+ meta: { page: number; pageSize: number; pages: number; total: number };
23
+ result: T[];
24
+ }
25
+ ```
26
+
27
+ ## Auth
28
+ All endpoints require `Authorization: Bearer <accessToken>` except those marked **Public**.
29
+
30
+ ---
31
+
32
+ ## {{Feature}} — `{{Feature}}Service`
33
+
34
+ | Method | Endpoint | Service method |
35
+ |--------|----------|----------------|
36
+ | GET | `/{{path}}?page=&size=` | `getAll(params)` |
37
+ | GET | `/{{path}}/:id` | `getById(id)` |
38
+ | POST | `/{{path}}` | `create(payload)` |
39
+ | PUT | `/{{path}}/:id` | `update(id, payload)` |
40
+ | DELETE | `/{{path}}/:id` | `delete(id)` |
41
+
42
+ ```typescript
43
+ interface {{Feature}} { /* mirror backend DTO */ }
44
+ interface Create{{Feature}}Request { /* ... */ }
45
+ interface Update{{Feature}}Request { /* ... */ }
46
+ ```
@@ -0,0 +1,27 @@
1
+ # Architecture
2
+
3
+ > Run `/init` to populate this file from your codebase.
4
+
5
+ ## Overview
6
+ {{What this app is, who uses it, which backend it talks to.}}
7
+
8
+ ## Layers
9
+ - **core/** — singleton services, HTTP interceptors (auth, error), guards, layout
10
+ - **shared/** — reusable presentational components, pipes, directives
11
+ - **features/** — business modules (model, service, pages, components)
12
+
13
+ ## Routing & Lazy Loading
14
+ {{Route tree, public vs protected routes, lazy loading strategy.}}
15
+
16
+ ## State Management
17
+ {{Which approach (Signals / NgRx / BehaviorSubject) and the decision rule.}}
18
+
19
+ ## HTTP & Error Flow
20
+ 1. Smart component calls a feature service
21
+ 2. Service issues `HttpClient` request → `Observable<ApiResponse<T>>`
22
+ 3. Auth interceptor attaches `Authorization: Bearer <token>`
23
+ 4. Error interceptor handles 401 (refresh/redirect) and maps errors
24
+ 5. Component renders loading / error / empty / data
25
+
26
+ ## Auth
27
+ {{Token storage strategy (memory / httpOnly cookie), refresh flow.}}
@@ -0,0 +1,27 @@
1
+ # Design System
2
+
3
+ > Run `/init` to populate from your project's styles and UI library config.
4
+
5
+ ## UI Library
6
+ {{Angular Material / PrimeNG / Tailwind / custom}}
7
+
8
+ ## Design Tokens
9
+ | Token | Value |
10
+ |-------|-------|
11
+ | Primary | {{e.g. #2563eb}} |
12
+ | Danger | {{...}} |
13
+ | Spacing scale | {{4 / 8 / 12 / 16 / 24 / 32}} |
14
+ | Breakpoints | {{mobile 375 / tablet 768 / desktop 1280}} |
15
+
16
+ ## Shared Components
17
+ | Component | Role |
18
+ |-----------|------|
19
+ | Spinner | Loading indicator |
20
+ | EmptyState | No data message |
21
+ | ErrorMessage | Error display |
22
+ | ConfirmDialog | Confirmation modal |
23
+
24
+ ## Rules
25
+ - Every page must handle loading / error / empty states
26
+ - Use design tokens, not hardcoded values
27
+ - New shared UI goes in `shared/` only if used by 2+ features
@@ -0,0 +1,19 @@
1
+ # Project Status
2
+
3
+ > Date: {{YYYY-MM-DD}} | Session: 1
4
+ > Run `/init` to populate from the existing codebase.
5
+
6
+ ## Completed
7
+ - {{Features/modules already in the codebase}}
8
+
9
+ ## In Progress
10
+ - {{none}}
11
+
12
+ ## Next Tasks
13
+ 1. {{...}}
14
+
15
+ ## Deferred Issues
16
+ - [P2] {{...}}
17
+
18
+ ## Warnings
19
+ - ⚠️ {{Known gotchas future sessions must know}}
@@ -0,0 +1,18 @@
1
+ # ADR 001: State Management Strategy
2
+
3
+ > Status: Accepted | Date: {{YYYY-MM-DD}}
4
+
5
+ ## Context
6
+ {{Why a state decision is needed; what kinds of state the app has.}}
7
+
8
+ ## Options Considered
9
+ - **Signals + services** — Angular-native, minimal boilerplate (v16+)
10
+ - **NgRx Store** — Redux, DevTools, more boilerplate
11
+ - **Service + BehaviorSubject** — traditional RxJS
12
+
13
+ ## Decision
14
+ {{Which option. Rule for when to use what.}}
15
+
16
+ ## Consequences
17
+ - Positive: {{...}}
18
+ - Trade-offs: {{...}}
@@ -0,0 +1,19 @@
1
+ # ADR 002: Auth Token Storage & Refresh
2
+
3
+ > Status: Accepted | Date: {{YYYY-MM-DD}}
4
+
5
+ ## Context
6
+ {{JWT auth. Token storage has security implications (XSS vs CSRF).}}
7
+
8
+ ## Options Considered
9
+ - **In-memory** — safest vs XSS; lost on page refresh
10
+ - **httpOnly cookie (refresh token)** — not JS-readable, pairs with in-memory access token
11
+ - **localStorage** — vulnerable to XSS; avoid for tokens
12
+
13
+ ## Decision
14
+ {{Access token in memory. Refresh token in httpOnly cookie.
15
+ 401 → interceptor calls /auth/refresh → retry original request → on failure logout.}}
16
+
17
+ ## Implementation Pointers
18
+ - Auth interceptor: `core/.../auth.interceptor.ts`
19
+ - Token state: `core/auth/auth.service.ts`
@@ -0,0 +1,72 @@
1
+ # Project Rules — Angular
2
+
3
+ > Auto-loaded by Claude Code every session (this file lives in .claude/rules/).
4
+ > Generated by `angular-vibe-kit /init`. Keep this file focused on RULES — not explanations.
5
+ > Full architecture context is in docs/ARCHITECTURE.md.
6
+
7
+ > Run `/init` to populate this file with rules inferred from your codebase.
8
+
9
+ ---
10
+
11
+ ## ⚖️ Precedence — which rule wins (read this first)
12
+
13
+ When guidance conflicts, follow this order top to bottom:
14
+
15
+ 1. **This project's conventions (this file + the codebase)** — HIGHEST.
16
+ For legitimate project choices, the codebase wins even if it differs from the
17
+ Angular best-practice profile. This covers: folder layout & naming, state
18
+ management choice, API/response shape, auth model, forms library, HTTP client.
19
+ *If the project does it consistently and it works, match it.*
20
+
21
+ 2. **Angular best-practice profile** (`.claude/angular-practices/<version>.md`) —
22
+ applies when the project has NO established convention, OR when the existing code
23
+ is objectively below standard (e.g. `any`, HTTP in components, logic in templates,
24
+ missing subscription teardown). In those cases the standard applies to **NEW CODE ONLY**.
25
+
26
+ 3. **Legacy modules** (listed in Coexistence Strategy below) — **never refactored**
27
+ to satisfy 1 or 2, regardless. Touch only when a task explicitly targets them.
28
+
29
+ > "Below standard" vs "valid project choice": a *valid choice* is a coherent decision
30
+ > with no objective defect (naming, structure, library). *Below standard* is an
31
+ > objective defect the best-practice profile flags. When unsure which one applies, ASK.
32
+
33
+ ---
34
+
35
+ ## Tech Stack
36
+ > /init will fill this in from package.json + src/ scan.
37
+
38
+ ## Folder Structure
39
+ > /init will describe the actual layout found in src/.
40
+
41
+ ## Naming Conventions
42
+ | Type | Pattern | Example |
43
+ |------|---------|---------|
44
+ | Component | `kebab-case.component.ts` | `user-list.component.ts` |
45
+ | Service | `kebab-case.service.ts` | `user.service.ts` |
46
+ | Model/Interface | `PascalCase` | `User`, `ApiResponse<T>` |
47
+ | Routes file | `kebab-case.routes.ts` | `user.routes.ts` |
48
+
49
+ ## Coding Rules
50
+ > /init will generate concrete rules based on Angular version + what the project does.
51
+
52
+ ## DO NOT
53
+ - Do NOT use `any`
54
+ - Do NOT call HttpClient from components
55
+ - Do NOT store tokens in localStorage
56
+ - Do NOT hardcode the backend URL
57
+
58
+ ## Reference Examples
59
+ > /init fills this with the best existing files to copy patterns from.
60
+ > `/new-feature` opens these and mirrors their style.
61
+
62
+ | Kind | File | Why it's the reference |
63
+ |------|------|------------------------|
64
+ | Service | {{path}} | {{reason}} |
65
+ | Smart / page component | {{path}} | {{reason}} |
66
+ | Dumb / presentational component | {{path}} | {{reason}} |
67
+ | Test spec | {{path}} | {{reason}} |
68
+
69
+ ## Coexistence Strategy
70
+ > /init will classify existing code as standard / below-standard and generate:
71
+ > - Rules for new code
72
+ > - List of legacy modules to leave untouched