agent-directives 0.3.0 → 0.4.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,287 @@
1
+ ---
2
+ name: angular-patterns
3
+ description: Concrete Angular application patterns — smart/dumb component split, service-layer ownership, routing/guards/resolvers, HTTP interceptors, and reactive state with signals or RxJS.
4
+ version: 1.0.0
5
+ required: false
6
+ category: angular
7
+ tools:
8
+ - claude
9
+ - copilot
10
+ - codex
11
+ - cursor
12
+ source_urls:
13
+ - https://angular.dev/guide/components
14
+ - https://angular.dev/guide/routing
15
+ - https://angular.dev/guide/http
16
+ - https://angular.dev/guide/di
17
+ - https://angular.dev/guide/signals
18
+ applies_to:
19
+ - src/app/**/*.component.ts
20
+ - src/app/**/*.component.html
21
+ - src/app/**/*.service.ts
22
+ - src/app/**/*.store.ts
23
+ - src/app/**/*.routes.ts
24
+ - src/app/**/*.guard.ts
25
+ - src/app/**/*.resolver.ts
26
+ - src/app/**/*.interceptor.ts
27
+ ---
28
+
29
+ # Angular Patterns Rules
30
+
31
+ **Load when:** Building or reviewing Angular feature code that crosses components and services — data flow, routing, HTTP, forms, or state.
32
+
33
+ ## Version Awareness
34
+
35
+ Functional guards/resolvers/interceptors, `inject()`, signal inputs, `resource()`, `linkedSignal`, and `withComponentInputBinding()` are version-gated. Match the project's Angular version before introducing a pattern, and keep an existing file consistent with the style already in it.
36
+
37
+ ## Sources
38
+
39
+ - Angular Components Guide — https://angular.dev/guide/components
40
+ - Angular Routing Guide — https://angular.dev/guide/routing
41
+ - Angular HTTP Guide — https://angular.dev/guide/http
42
+ - Angular Dependency Injection Guide — https://angular.dev/guide/di
43
+ - Angular Signals Guide — https://angular.dev/guide/signals
44
+
45
+ ## Rules
46
+
47
+ ### Smart / dumb component split
48
+
49
+ Smart (container) components own data fetching and state. Dumb (presentational) components receive inputs and emit outputs only — no service injection, no router access.
50
+
51
+ ```typescript
52
+ // Smart — owns data
53
+ @Component({
54
+ selector: 'app-user-page',
55
+ standalone: true,
56
+ imports: [UserCardComponent],
57
+ template: `
58
+ @if (userResource.isLoading()) { <app-spinner /> }
59
+ @else if (userResource.error()) { <app-error /> }
60
+ @else { <app-user-card [user]="userResource.value()!" (select)="onSelect($event)" /> }
61
+ `,
62
+ changeDetection: ChangeDetectionStrategy.OnPush,
63
+ })
64
+ export class UserPageComponent {
65
+ private userService = inject(UserService);
66
+ userId = input.required<string>();
67
+
68
+ userResource = resource({
69
+ request: () => ({ id: this.userId() }),
70
+ loader: ({ request }) => firstValueFrom(this.userService.getUser(request.id)),
71
+ });
72
+
73
+ onSelect(id: string) { /* navigate or update state */ }
74
+ }
75
+ ```
76
+
77
+ ```typescript
78
+ // Dumb — pure presentation, no inject(), no router
79
+ @Component({
80
+ selector: 'app-user-card',
81
+ standalone: true,
82
+ template: `<button (click)="select.emit(user().id)">{{ user().name }}</button>`,
83
+ changeDetection: ChangeDetectionStrategy.OnPush,
84
+ })
85
+ export class UserCardComponent {
86
+ user = input.required<User>();
87
+ select = output<string>();
88
+ }
89
+ ```
90
+
91
+ ### Service layer
92
+
93
+ Services own all data access and business logic. Components delegate — no `HttpClient` in components, no `fetch()` in components.
94
+
95
+ ```typescript
96
+ @Injectable({ providedIn: 'root' })
97
+ export class UserService {
98
+ private http = inject(HttpClient);
99
+
100
+ getUsers(): Observable<User[]> {
101
+ return this.http.get<User[]>('/api/users');
102
+ }
103
+
104
+ getUser(id: string): Observable<User> {
105
+ return this.http.get<User>(`/api/users/${id}`);
106
+ }
107
+ }
108
+ ```
109
+
110
+ - One service per cohesive domain concept. Do not bury unrelated APIs in a god service.
111
+ - Scope a service to a component/route subtree (component `providers: [...]`) only when its lifecycle must follow that subtree. Otherwise prefer `providedIn: 'root'`.
112
+
113
+ ### Reactive state
114
+
115
+ ```typescript
116
+ // Local state
117
+ count = signal(0);
118
+ doubled = computed(() => this.count() * 2);
119
+ selectedItem = linkedSignal(() => this.items()[0]);
120
+
121
+ // Bridge an Observable into a signal
122
+ users = toSignal(this.userService.getUsers(), { initialValue: [] });
123
+ ```
124
+
125
+ - Derived values live in `computed` / `linkedSignal` — never in a separate writable signal updated by `effect`.
126
+ - Use `takeUntilDestroyed(this.destroyRef)` for manual subscriptions:
127
+
128
+ ```typescript
129
+ export class UserComponent {
130
+ private destroyRef = inject(DestroyRef);
131
+ private userService = inject(UserService);
132
+
133
+ ngOnInit() {
134
+ this.userService.updates$
135
+ .pipe(takeUntilDestroyed(this.destroyRef))
136
+ .subscribe(update => this.handleUpdate(update));
137
+ }
138
+ }
139
+ ```
140
+
141
+ ### Routing
142
+
143
+ ```typescript
144
+ // app.routes.ts (or feature.routes.ts)
145
+ export const routes: Routes = [
146
+ { path: '', component: HomeComponent },
147
+ {
148
+ path: 'admin',
149
+ canMatch: [authGuard], // canMatch prevents the chunk from loading at all
150
+ loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES),
151
+ },
152
+ {
153
+ path: 'users/:id',
154
+ resolve: { user: userResolver },
155
+ loadComponent: () => import('./users/user-detail.component').then(m => m.UserDetailComponent),
156
+ },
157
+ ];
158
+ ```
159
+
160
+ - Use `canMatch` (not `canActivate`) for sensitive routes — the route module never loads for unauthorized users, so the code isn't shipped to them.
161
+ - Lazy-load every feature with `loadChildren` (or `loadComponent` for a single route).
162
+ - Pre-fetch with `resolve` when the destination cannot meaningfully render without the data.
163
+ - Enable component input binding once at app level (`withComponentInputBinding()`), then bind route params via `input()`:
164
+
165
+ ```typescript
166
+ export class UserDetailComponent {
167
+ id = input.required<string>(); // route param :id binds automatically
168
+ }
169
+ ```
170
+
171
+ ### Functional guards and resolvers
172
+
173
+ ```typescript
174
+ export const authGuard: CanActivateFn = () => {
175
+ const auth = inject(AuthService);
176
+ return auth.isAuthenticated()
177
+ ? true
178
+ : inject(Router).createUrlTree(['/login']);
179
+ };
180
+
181
+ export const userResolver: ResolveFn<User> = (route) =>
182
+ inject(UserService).getUser(route.paramMap.get('id')!);
183
+ ```
184
+
185
+ - Prefer functional `CanActivateFn` / `CanMatchFn` / `ResolveFn` / `HttpInterceptorFn` over class-based equivalents on standalone-first projects.
186
+
187
+ ### HTTP interceptors
188
+
189
+ ```typescript
190
+ export const authInterceptor: HttpInterceptorFn = (req, next) => {
191
+ const token = inject(AuthService).token();
192
+ if (!token) return next(req);
193
+ return next(req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }));
194
+ };
195
+
196
+ // app.config.ts
197
+ provideHttpClient(withInterceptors([authInterceptor, errorInterceptor, retryInterceptor]))
198
+ ```
199
+
200
+ - Register cross-cutting concerns (auth, error mapping, retries, logging) as interceptors. Do not duplicate header logic across services.
201
+ - Keep interceptors small and composable; one concern per interceptor.
202
+
203
+ ### Forms
204
+
205
+ Match the project's existing form strategy. For complex validation, prefer Reactive Forms. For v21+ projects starting fresh, Signal Forms are appropriate.
206
+
207
+ ```typescript
208
+ // Reactive Forms — standard approach for most apps
209
+ export class LoginComponent {
210
+ private fb = inject(FormBuilder);
211
+
212
+ form = this.fb.nonNullable.group({
213
+ email: ['', [Validators.required, Validators.email]],
214
+ password: ['', [Validators.required, Validators.minLength(8)]],
215
+ });
216
+
217
+ submit() {
218
+ if (this.form.invalid) return;
219
+ const { email, password } = this.form.getRawValue();
220
+ // ...
221
+ }
222
+ }
223
+ ```
224
+
225
+ - Use `fb.nonNullable` / `NonNullableFormBuilder` so controls are typed as `T` rather than `T | null`.
226
+ - Avoid mixing reactive and template-driven forms in the same form.
227
+
228
+ ### Async data with `resource()`
229
+
230
+ Use `resource()` for reactive async fetching that depends on signals — it manages loading/error/value automatically and reloads when the request signal changes.
231
+
232
+ ```typescript
233
+ export class UserDetailComponent {
234
+ userId = input.required<string>();
235
+
236
+ userResource = resource({
237
+ request: () => ({ id: this.userId() }),
238
+ loader: ({ request }) =>
239
+ firstValueFrom(inject(UserService).getUser(request.id)),
240
+ });
241
+ }
242
+ ```
243
+
244
+ State: `userResource.value()`, `userResource.isLoading()`, `userResource.error()`, `userResource.reload()`.
245
+
246
+ ### RxJS operator choice
247
+
248
+ | Operator | Use for |
249
+ | --- | --- |
250
+ | `switchMap` | Search, navigation, latest-wins |
251
+ | `mergeMap` | Independent parallel requests |
252
+ | `exhaustMap` | Form submits — ignore re-clicks until current completes |
253
+ | `concatMap` | Ordered queueing |
254
+
255
+ Always end an HTTP-backed inner stream with `catchError` so the outer stream survives failures.
256
+
257
+ ### Rendering strategies
258
+
259
+ - **CSR** (default) — standard SPA.
260
+ - **SSR + Hydration** — `ng add @angular/ssr`. Improves first paint and SEO.
261
+ - **SSG / Prerender** — for content-heavy public routes.
262
+
263
+ Under SSR, never touch `window`, `document`, `localStorage`, or `navigator` directly — gate with `isPlatformBrowser(this.platformId)` or inject via the `DOCUMENT` token.
264
+
265
+ ### Accessibility
266
+
267
+ - Reuse Angular CDK primitives (Listbox, Menu, Dialog, Overlay, Tree, A11y `LiveAnnouncer`/`FocusTrap`) rather than re-implementing ARIA wiring.
268
+ - Style ARIA state attributes rather than toggling presentation classes:
269
+
270
+ ```css
271
+ [aria-selected="true"] { background: var(--color-selected); }
272
+ [aria-disabled="true"] { opacity: 0.5; cursor: not-allowed; }
273
+ ```
274
+
275
+ ## Anti-patterns to refuse
276
+
277
+ - `HttpClient` injected into a presentational component.
278
+ - Subscriptions in components without `takeUntilDestroyed` (or a documented manual `ngOnDestroy` cleanup).
279
+ - `canActivate` on a route whose entire chunk should be withheld from unauthorized users — use `canMatch`.
280
+ - Class-based `HTTP_INTERCEPTORS` providers on a standalone-first project.
281
+ - Manual `subscribe()` + assign-to-property to flow Observable data into the view when `async` pipe or `toSignal` covers the use case.
282
+
283
+ ## Evidence
284
+
285
+ - Targeted tests for the changed behavior (see `rules/angular/testing.md`).
286
+ - `ng lint` (or project equivalent) on touched files.
287
+ - `ng build` to verify typed templates and route configuration compile.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: angular-project-structure
3
- description: Angular workspace and project-structure standards for agents working in Angular applications.
4
- version: 1.0.0
3
+ description: Concrete Angular workspace, file-naming, feature-folder, and provider-bootstrapping standards for agents working in Angular applications.
4
+ version: 1.1.0
5
5
  required: false
6
6
  category: angular
7
7
  tools:
@@ -13,33 +13,125 @@ source_urls:
13
13
  - https://angular.dev/style-guide
14
14
  - https://angular.dev/tools/cli
15
15
  - https://angular.dev/reference/configs/workspace-config
16
+ - https://angular.dev/guide/ngmodules/standalone
16
17
  applies_to:
17
18
  - angular.json
18
19
  - package.json
20
+ - src/main.ts
19
21
  - src/app/**/*.ts
20
22
  - src/app/**/*.html
21
23
  ---
22
24
 
23
25
  # Angular Project Structure Rules
24
26
 
25
- **Load when:** The project contains `angular.json` or `@angular/core`, or the task touches Angular app structure, routes, components, services, templates, or tests.
27
+ **Load when:** The project contains `angular.json` or `@angular/core`, or the task touches Angular app structure, routes, file layout, providers, or bootstrap configuration.
26
28
 
27
- ## Sources
29
+ ## Version Awareness
30
+
31
+ Confirm the Angular version (`ng version` or `package.json`) before suggesting structure. Standalone APIs and the v17+ application builder are now defaults; do not invent NgModules on a standalone-first project, and do not migrate a working NgModule layout as a side effect of an unrelated change.
28
32
 
29
- Track source material explicitly so future updates can verify whether the rule is stale:
33
+ ## Sources
30
34
 
31
35
  - Angular Style Guide — https://angular.dev/style-guide
32
36
  - Angular CLI Overview — https://angular.dev/tools/cli
33
37
  - Angular Workspace Configuration — https://angular.dev/reference/configs/workspace-config
38
+ - Standalone APIs — https://angular.dev/guide/ngmodules/standalone
34
39
 
35
40
  ## Rules
36
41
 
37
- - Prefer Angular CLI-generated structure and names unless the project already has a stronger local convention.
38
- - Keep related files together by feature or component. Do not create broad catch-all folders for unrelated utilities.
39
- - Keep Angular-specific code under the existing app/source root; do not introduce a parallel structure unless project evidence requires it.
40
- - Follow existing project routing, state-management, and shared-library boundaries before adding new locations.
41
- - Treat `angular.json`, builder targets, and TypeScript config edits as project-wide changes. Keep them narrow and verify with the relevant configured Angular command.
42
- - Do not add framework rules from another ecosystem, such as React or Vue, to Angular-only work.
42
+ ### File and folder naming
43
+
44
+ Follow Angular CLI conventions one artifact per file. Use kebab-case file names and PascalCase class names:
45
+
46
+ - `user-profile.component.ts` + `user-profile.component.html` + `user-profile.component.scss` + `user-profile.component.spec.ts`
47
+ - `user.service.ts`, `auth.guard.ts`, `date-format.pipe.ts`, `logging.interceptor.ts`, `user.resolver.ts`
48
+ - Test files live next to the unit they test, named `<name>.spec.ts`.
49
+ - Generate scaffolding with the CLI rather than handwriting it: `ng generate component features/users/user-card --change-detection=OnPush --inline-style=false`.
50
+
51
+ ### Feature folders
52
+
53
+ Group by feature, then by artifact. Do not create catch-all `shared/` or `utils/` buckets for unrelated code.
54
+
55
+ ```
56
+ src/app/
57
+ core/ # app-wide singletons: auth, http interceptors, app-level guards
58
+ shared/ # reusable presentational components, pipes, directives
59
+ features/
60
+ users/
61
+ users.routes.ts
62
+ user-list/
63
+ user-list.component.{ts,html,scss,spec.ts}
64
+ user-detail/
65
+ user-detail.component.{ts,html,scss,spec.ts}
66
+ user.service.ts
67
+ user.model.ts
68
+ auth/
69
+ auth.routes.ts
70
+ ...
71
+ app.config.ts # application providers (router, http, etc.)
72
+ app.routes.ts # top-level routes
73
+ app.component.ts # root component
74
+ ```
75
+
76
+ - Keep route configuration co-located with the feature it serves (`users.routes.ts`), and lazy-load via `loadChildren`.
77
+ - Place app-wide providers (`provideRouter`, `provideHttpClient`, interceptors, error handlers) in `app.config.ts`. Bootstrap from `main.ts` with `bootstrapApplication(AppComponent, appConfig)`.
78
+ - A new directory or top-level folder must be justified by existing project evidence. Follow project routing, state-management, and shared-library boundaries before adding new locations.
79
+
80
+ ### Standalone bootstrap
81
+
82
+ ```typescript
83
+ // main.ts
84
+ bootstrapApplication(AppComponent, appConfig).catch(console.error);
85
+
86
+ // app.config.ts
87
+ export const appConfig: ApplicationConfig = {
88
+ providers: [
89
+ provideRouter(routes, withComponentInputBinding(), withViewTransitions()),
90
+ provideHttpClient(withInterceptors([authInterceptor, errorInterceptor])),
91
+ provideAnimationsAsync(),
92
+ ],
93
+ };
94
+ ```
95
+
96
+ - Use functional providers (`provideRouter`, `provideHttpClient`, `provideAnimationsAsync`) over the legacy module forms on standalone-first projects.
97
+ - Register HTTP interceptors functionally via `withInterceptors([...])`. Class-based `HTTP_INTERCEPTORS` multi-providers are reserved for NgModule-centered projects.
98
+
99
+ ### Routing layout
100
+
101
+ ```typescript
102
+ // app.routes.ts
103
+ export const routes: Routes = [
104
+ { path: '', pathMatch: 'full', redirectTo: 'home' },
105
+ { path: 'home', loadComponent: () => import('./features/home/home.component').then(m => m.HomeComponent) },
106
+ {
107
+ path: 'users',
108
+ loadChildren: () => import('./features/users/users.routes').then(m => m.USERS_ROUTES),
109
+ },
110
+ { path: '**', loadComponent: () => import('./shared/not-found/not-found.component').then(m => m.NotFoundComponent) },
111
+ ];
112
+ ```
113
+
114
+ - Lazy-load every feature area with `loadChildren` (or `loadComponent` for a single route). Eagerly loading feature trees inflates the initial bundle.
115
+ - Use `withComponentInputBinding()` so route params and query params bind to component `input()` signals automatically.
116
+ - Put guards/resolvers next to the route file (`users.guards.ts`, `user.resolver.ts`), not in a global `guards/` bucket.
117
+
118
+ ### Workspace and config files
119
+
120
+ - Treat `angular.json`, `tsconfig*.json`, builder targets, and `package.json` script changes as project-wide changes. Keep edits narrow and verify with the configured command (`ng build`, `ng test`, `ng lint`).
121
+ - Do not switch the application builder, change `target`/`module` settings, or rename top-level paths casually — those are coordinated migrations.
122
+ - `environments/environment*.ts` files describe configuration shape, not real secrets. Inject production secrets via CI/CD, not source.
123
+
124
+ ### Cross-ecosystem hygiene
125
+
126
+ - Do not add React, Vue, or other-framework conventions to an Angular project. If shared code must straddle frameworks, isolate it behind a framework-neutral library.
127
+ - Match the project's existing module system (ESM vs CommonJS) and import style. Use path aliases from `tsconfig.json` where the project already defines them.
128
+
129
+ ## Anti-patterns to refuse
130
+
131
+ - A single `shared/` directory accumulating unrelated services, pipes, components, and constants.
132
+ - New NgModules created on a standalone-first project just to host providers — use `app.config.ts` or route-level `providers`.
133
+ - Eagerly importing feature components into `app.routes.ts` instead of `loadChildren` / `loadComponent`.
134
+ - Editing `angular.json` builders, `tsconfig` paths, or `package.json` scripts as part of an unrelated component or service change.
43
135
 
44
136
  ## Evidence
45
137
 
@@ -0,0 +1,152 @@
1
+ ---
2
+ name: angular-security
3
+ description: Concrete Angular security rules — XSS prevention, HttpClient discipline, secret handling, route guards, and SSR safety.
4
+ version: 1.0.0
5
+ required: false
6
+ category: angular
7
+ tools:
8
+ - claude
9
+ - copilot
10
+ - codex
11
+ - cursor
12
+ source_urls:
13
+ - https://angular.dev/best-practices/security
14
+ - https://angular.dev/api/platform-browser/DomSanitizer
15
+ - https://angular.dev/guide/http/security
16
+ - https://angular.dev/guide/ssr
17
+ applies_to:
18
+ - src/app/**/*.component.ts
19
+ - src/app/**/*.component.html
20
+ - src/app/**/*.service.ts
21
+ - src/app/**/*.interceptor.ts
22
+ - src/app/**/*.guard.ts
23
+ - src/app/**/*.routes.ts
24
+ - src/environments/**/*.ts
25
+ - src/**/*.server.ts
26
+ - server.ts
27
+ ---
28
+
29
+ # Angular Security Rules
30
+
31
+ **Load when:** Touching code that handles user input, renders untrusted HTML, calls external APIs, manages auth/tokens, configures route guards, or runs under SSR.
32
+
33
+ ## Version Awareness
34
+
35
+ Sanitization, route guard, and SSR APIs evolve between versions — verify the version with `ng version` before suggesting an API. Do not introduce APIs that don't exist in the project's Angular version, and do not rip out a guard or sanitization step as a side effect of an unrelated change.
36
+
37
+ ## Sources
38
+
39
+ - Angular Security Best Practices — https://angular.dev/best-practices/security
40
+ - DomSanitizer API — https://angular.dev/api/platform-browser/DomSanitizer
41
+ - HttpClient Security — https://angular.dev/guide/http/security
42
+ - Angular SSR Guide — https://angular.dev/guide/ssr
43
+
44
+ ## Rules
45
+
46
+ ### XSS prevention
47
+
48
+ Angular auto-sanitizes interpolation and property bindings. Do not bypass the sanitizer with user-controlled input.
49
+
50
+ ```typescript
51
+ // WRONG — bypasses sanitization, classic XSS
52
+ this.safeHtml = this.sanitizer.bypassSecurityTrustHtml(userInput);
53
+
54
+ // CORRECT — explicit sanitization, returns string with dangerous content stripped
55
+ this.safeHtml = this.sanitizer.sanitize(SecurityContext.HTML, userInput);
56
+ ```
57
+
58
+ - Never call `bypassSecurityTrustHtml` / `bypassSecurityTrustScript` / `bypassSecurityTrustStyle` / `bypassSecurityTrustUrl` / `bypassSecurityTrustResourceUrl` on user-controlled input. If you must bypass, the source must be a static, trusted constant, with a comment explaining why.
59
+ - Avoid `[innerHTML]` for untrusted content. Use `{{ value }}` interpolation or `[innerText]` for plain text. If rich content is required, render it through a sanitizing pipe.
60
+ - Never bind `[href]` or `[src]` directly to user-provided URLs without scheme validation — Angular sanitizes some URL contexts but not all.
61
+ - Never build templates by string concatenation with user data.
62
+
63
+ ### HttpClient discipline
64
+
65
+ Use `HttpClient` exclusively — not raw `fetch()` or `XHR` — so interceptors (auth headers, error mapping, retry, logging) apply uniformly.
66
+
67
+ ```typescript
68
+ // WRONG — bypasses interceptors entirely
69
+ const res = await fetch('/api/users');
70
+
71
+ // CORRECT
72
+ users$ = this.http.get<User[]>('/api/users');
73
+ ```
74
+
75
+ - Attach auth tokens via an interceptor, never by hand on every call. One source of truth for `Authorization` headers.
76
+ - Type and validate API responses. Treat external data as `unknown` at the boundary and narrow with a schema (Zod / io-ts / hand-rolled guard) before letting it flow into typed signals or components.
77
+ - Never log full HTTP responses that may contain tokens, PII, or credentials. Redact before logging.
78
+ - For SSR fetches, prefer relative URLs (or the configured base URL) so the server doesn't re-hit the public host.
79
+
80
+ ### Secret and config management
81
+
82
+ ```typescript
83
+ // WRONG — hardcoded secret in source
84
+ const apiKey = 'sk-live-xxxxxxxxxxxxxxxxxxxxxxxx';
85
+
86
+ // CORRECT — env config shape, real value injected by CI/CD
87
+ import { environment } from '../environments/environment';
88
+ const apiKey = environment.apiKey;
89
+ ```
90
+
91
+ - Treat `environments/environment*.ts` as configuration shape, not as a place to commit production secrets. Real secrets come from CI/CD env vars or a secret manager.
92
+ - Do not introduce `process.env.*` reads into browser-bound code unless the build pipeline is configured to inline them safely.
93
+ - Never commit `.env` files containing real credentials.
94
+
95
+ ### Route guards
96
+
97
+ Every authenticated or role-restricted route must have a guard. Hiding a UI element is not access control.
98
+
99
+ ```typescript
100
+ {
101
+ path: 'admin',
102
+ canMatch: [authGuard, roleGuard('admin')],
103
+ loadChildren: () => import('./admin/admin.routes').then(m => m.ADMIN_ROUTES),
104
+ }
105
+ ```
106
+
107
+ - Use `canMatch` for sensitive routes so the route's lazy chunk is not loaded at all for unauthorized users.
108
+ - Use `canDeactivate` to prevent accidental data loss on unsaved forms — but never as a security mechanism.
109
+ - Guards must trust a server-side check too. The server is the security boundary; the client guard is a UX gate.
110
+
111
+ ### Auth tokens
112
+
113
+ - Store tokens in HTTP-only cookies when the backend supports it. If using `localStorage` / `sessionStorage`, accept the XSS risk and mitigate accordingly.
114
+ - Refresh tokens belong in HTTP-only cookies or a dedicated secure storage layer — never in plain `localStorage`.
115
+ - Clear all auth state on logout, including in-memory signals, interceptors caches, and storage.
116
+
117
+ ### SSR safety
118
+
119
+ When the app runs under Angular SSR:
120
+
121
+ - Never access `window`, `document`, `localStorage`, `sessionStorage`, or `navigator` directly. Gate browser-only code with `isPlatformBrowser(this.platformId)` or inject the `DOCUMENT` token for DOM access.
122
+ - Sanitize user input on the server too — DOM-based XSS is reachable through SSR'd markup.
123
+ - Do not place server-only secrets into `TransferState` — anything serialized there ships to the client.
124
+ - Watch for hydration mismatches: server-rendered markup must equal initial client render. Diverging output is both a bug and a possible injection vector.
125
+
126
+ ### Content Security Policy
127
+
128
+ Configure CSP headers server-side. Specifically:
129
+
130
+ - Avoid `unsafe-inline` in `script-src`. If SSR emits inline state, use Angular's CSP nonce support and propagate the nonce on every inline tag.
131
+ - Avoid `unsafe-eval` — Angular AOT does not need it.
132
+ - Restrict `connect-src` to the API origins the app actually uses.
133
+
134
+ ### Dependencies
135
+
136
+ - Treat `npm audit` / `pnpm audit` / `yarn audit` findings as real signals. Resolve high-severity issues before shipping touched code paths.
137
+ - Do not pin to versions with known active CVEs as a workaround for a feature change.
138
+
139
+ ## Anti-patterns to refuse
140
+
141
+ - `bypassSecurityTrust*` on a value derived in any way from user input.
142
+ - `[innerHTML]` bound to a request body, query param, or form value without sanitization.
143
+ - `fetch('/api/...')` in a service that already has `HttpClient` available.
144
+ - Auth checks done only by hiding a button — no guard, no server-side check.
145
+ - `window.localStorage.getItem(...)` in code that also runs under SSR.
146
+
147
+ ## Evidence
148
+
149
+ - For changes that affect rendering of untrusted content: a test asserting the rendered output does not contain the dangerous payload.
150
+ - For auth/guard changes: a `RouterTestingHarness` test that exercises both allowed and denied branches.
151
+ - For interceptor changes: a `HttpTestingController` test confirming the header / retry / error mapping behavior.
152
+ - For dependency upgrades touching security: the audit report after the change.