agent-directives 0.3.0 → 0.4.1

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