@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.
- package/LICENSE +21 -0
- package/README.md +129 -0
- package/bin/cli.js +245 -0
- package/commands/dev-cycle.md +139 -0
- package/commands/init.md +103 -0
- package/commands/new-feature.md +69 -0
- package/commands/review-pr.md +63 -0
- package/commands/start.md +21 -0
- package/commands/update-status.md +19 -0
- package/commands/write-context.md +71 -0
- package/commands/write-tests.md +110 -0
- package/package.json +40 -0
- package/practices/v12-13.md +193 -0
- package/practices/v14-15.md +133 -0
- package/practices/v16.md +127 -0
- package/practices/v17.md +166 -0
- package/practices/v18-19.md +121 -0
- package/practices/v20plus.md +210 -0
- package/templates/CLAUDE.md +55 -0
- package/templates/docs/API_CONTRACT.md +46 -0
- package/templates/docs/ARCHITECTURE.md +27 -0
- package/templates/docs/DESIGN_SYSTEM.md +27 -0
- package/templates/docs/PROJECT-STATUS.md +19 -0
- package/templates/docs/decisions/001-state-management.md +18 -0
- package/templates/docs/decisions/002-auth-token-storage.md +19 -0
- package/templates/rules/project-rules.md +72 -0
|
@@ -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
|