@eltonssouza/development-utility-kit 1.0.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/.claude/agents/analyst.md +198 -0
- package/.claude/agents/backend-developer.md +126 -0
- package/.claude/agents/brain-keeper.md +229 -0
- package/.claude/agents/code-reviewer.md +181 -0
- package/.claude/agents/database-engineer.md +94 -0
- package/.claude/agents/devops-engineer.md +141 -0
- package/.claude/agents/frontend-developer.md +97 -0
- package/.claude/agents/gate-keeper.md +118 -0
- package/.claude/agents/migrator.md +291 -0
- package/.claude/agents/mobile-developer.md +80 -0
- package/.claude/agents/n8n-specialist.md +94 -0
- package/.claude/agents/product-owner.md +115 -0
- package/.claude/agents/qa-engineer.md +232 -0
- package/.claude/agents/release-engineer.md +204 -0
- package/.claude/agents/scaffold.md +87 -0
- package/.claude/agents/security-engineer.md +199 -0
- package/.claude/agents/sprint-runner.md +44 -0
- package/.claude/agents/stack-resolver.md +84 -0
- package/.claude/agents/tech-lead.md +182 -0
- package/.claude/agents/update-template.md +54 -0
- package/.claude/agents/ux-designer.md +118 -0
- package/.claude/settings.json +44 -0
- package/.claude/skills/README.md +332 -0
- package/.claude/skills/active-project/SKILL.md +129 -0
- package/.claude/skills/api-integration-test/SKILL.md +64 -0
- package/.claude/skills/auto-test-guard/SKILL.md +237 -0
- package/.claude/skills/auto-test-guard/resources/backend-tests.md +20 -0
- package/.claude/skills/auto-test-guard/resources/e2e-tests.md +24 -0
- package/.claude/skills/auto-test-guard/resources/execution-report.md +49 -0
- package/.claude/skills/auto-test-guard/resources/frontend-tests.md +18 -0
- package/.claude/skills/auto-test-guard/resources/initial-setup.md +108 -0
- package/.claude/skills/auto-test-guard/resources/run-suite.md +48 -0
- package/.claude/skills/auto-test-guard/resources/senior-gate.md +19 -0
- package/.claude/skills/brain-keeper/SKILL.md +60 -0
- package/.claude/skills/brain-keeper/obsidian/app.json +9 -0
- package/.claude/skills/brain-keeper/obsidian/appearance.json +4 -0
- package/.claude/skills/brain-keeper/obsidian/core-plugins.json +20 -0
- package/.claude/skills/brain-keeper/obsidian/daily-notes.json +5 -0
- package/.claude/skills/brain-keeper/obsidian/graph.json +32 -0
- package/.claude/skills/brain-keeper/obsidian/snippets/folder-colors.css +90 -0
- package/.claude/skills/brain-keeper/obsidian/templates.json +5 -0
- package/.claude/skills/brain-keeper/templates/README.md +51 -0
- package/.claude/skills/brain-keeper/templates/adr.md +40 -0
- package/.claude/skills/brain-keeper/templates/bug.md +35 -0
- package/.claude/skills/brain-keeper/templates/daily.md +38 -0
- package/.claude/skills/brain-keeper/templates/feature.md +62 -0
- package/.claude/skills/brain-keeper/templates/meeting.md +34 -0
- package/.claude/skills/brain-keeper/templates/tech-debt.md +21 -0
- package/.claude/skills/caveman/SKILL.md +187 -0
- package/.claude/skills/create-stack-pack/SKILL.md +281 -0
- package/.claude/skills/grill-me/SKILL.md +79 -0
- package/.claude/skills/honcho-memory/SKILL.md +207 -0
- package/.claude/skills/honcho-memory/docs/api-endpoints-verified.md +75 -0
- package/.claude/skills/honcho-memory/hooks/on-prompt-submit.js +221 -0
- package/.claude/skills/honcho-memory/hooks/on-stop.js +193 -0
- package/.claude/skills/honcho-memory/lib/honcho-client.js +363 -0
- package/.claude/skills/honcho-memory/lib/memory-injector.js +93 -0
- package/.claude/skills/honcho-memory/package.json +32 -0
- package/.claude/skills/honcho-memory/scripts/cli.js +370 -0
- package/.claude/skills/honcho-memory/scripts/setup.js +109 -0
- package/.claude/skills/honcho-memory/tests/t001-api-endpoints-verified.test.js +89 -0
- package/.claude/skills/honcho-memory/tests/t002-structure.test.js +97 -0
- package/.claude/skills/honcho-memory/tests/t003-honcho-client.test.js +162 -0
- package/.claude/skills/honcho-memory/tests/t004-soft-delete.test.js +259 -0
- package/.claude/skills/honcho-memory/tests/t005-memory-injector.test.js +175 -0
- package/.claude/skills/honcho-memory/tests/t006-on-prompt-submit.test.js +215 -0
- package/.claude/skills/honcho-memory/tests/t007-on-stop.test.js +165 -0
- package/.claude/skills/honcho-memory/tests/t008-cli.test.js +214 -0
- package/.claude/skills/honcho-memory/tests/t009-setup.test.js +232 -0
- package/.claude/skills/honcho-memory/tests/t010-skill-md.test.js +114 -0
- package/.claude/skills/honcho-memory/tests/t011-settings-hooks.test.js +105 -0
- package/.claude/skills/honcho-memory/tests/t012-docs-update.test.js +106 -0
- package/.claude/skills/honcho-memory/tests/t013-smoke-e2e.test.js +90 -0
- package/.claude/skills/pair-debug/SKILL.md +288 -0
- package/.claude/skills/prd-ready-check/SKILL.md +58 -0
- package/.claude/skills/project-manager/SKILL.md +167 -0
- package/.claude/skills/quality-standards/SKILL.md +201 -0
- package/.claude/skills/quick-feature/SKILL.md +264 -0
- package/.claude/skills/run-sprint/SKILL.md +342 -0
- package/.claude/skills/scaffold/SKILL.md +58 -0
- package/.claude/skills/stack-discovery/SKILL.md +159 -0
- package/.claude/skills/test-coverage-auditor/SKILL.md +59 -0
- package/.claude/skills/to-issues/SKILL.md +163 -0
- package/.claude/skills/to-prd/SKILL.md +130 -0
- package/.claude/skills/update-template/SKILL.md +254 -0
- package/.claude/stacks/CODEOWNERS +30 -0
- package/.claude/stacks/README.md +88 -0
- package/.claude/stacks/_template.md +116 -0
- package/.claude/stacks/java/spring-boot-3.md +376 -0
- package/.claude/stacks/java/spring-boot-4.md +438 -0
- package/.claude/stacks/typescript/angular-18.md +420 -0
- package/.claude/stacks/typescript/angular-19.md +397 -0
- package/.claude/stacks/typescript/angular-21.md +494 -0
- package/CLAUDE.md +453 -0
- package/README.md +391 -0
- package/bin/cli.js +773 -0
- package/bin/lib/backup.js +62 -0
- package/bin/lib/detect-stack.js +476 -0
- package/bin/lib/help.js +233 -0
- package/bin/lib/identity.js +108 -0
- package/bin/lib/local-dir.js +69 -0
- package/bin/lib/manifest.js +236 -0
- package/bin/lib/sync-all.js +394 -0
- package/bin/lib/version-check.js +398 -0
- package/dashboard/db.js +199 -0
- package/dashboard/package.json +22 -0
- package/dashboard/public/app.js +709 -0
- package/dashboard/public/content/docs/agents-reference.en.md +911 -0
- package/dashboard/public/content/docs/architecture-overview.en.md +260 -0
- package/dashboard/public/content/docs/autonomy-matrix.en.md +186 -0
- package/dashboard/public/content/docs/git-flow.en.md +525 -0
- package/dashboard/public/content/docs/honcho-memory.en.md +394 -0
- package/dashboard/public/content/docs/hooks-reference.en.md +420 -0
- package/dashboard/public/content/docs/pipeline.en.md +400 -0
- package/dashboard/public/content/docs/quality-gate.en.md +315 -0
- package/dashboard/public/content/docs/skills-reference.en.md +500 -0
- package/dashboard/public/content/docs/stack-rules.en.md +362 -0
- package/dashboard/public/content/docs/troubleshooting.en.md +637 -0
- package/dashboard/public/content/manifest.json +102 -0
- package/dashboard/public/content/manual/backend.en.md +1138 -0
- package/dashboard/public/content/manual/existing-project.en.md +831 -0
- package/dashboard/public/content/manual/frontend.en.md +1065 -0
- package/dashboard/public/content/manual/fullstack.en.md +1508 -0
- package/dashboard/public/content/manual/mobile.en.md +866 -0
- package/dashboard/public/index.html +108 -0
- package/dashboard/public/style.css +610 -0
- package/dashboard/public/vendor/marked.min.js +69 -0
- package/dashboard/rtk.js +143 -0
- package/dashboard/server-app.js +403 -0
- package/dashboard/server.js +104 -0
- package/dashboard/test/sprint1.test.js +406 -0
- package/dashboard/test/sprint2.test.js +571 -0
- package/dashboard/test/sprint3.test.js +560 -0
- package/package.json +33 -0
- package/scripts/hooks/subagent-telemetry.sh +14 -0
- package/scripts/hooks/telemetry-writer.js +250 -0
- package/scripts/latest-versions.json +56 -0
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
---
|
|
2
|
+
stack: typescript/angular-19
|
|
3
|
+
versions_covered: "19.0.x — 19.2.x"
|
|
4
|
+
last_validated: 2026-05-27
|
|
5
|
+
validated_against: "sandbox — Angular 19.1.3 (mid-cycle maintenance)"
|
|
6
|
+
status: active
|
|
7
|
+
pack_owner: "@elton"
|
|
8
|
+
security_review: 2026-05-27
|
|
9
|
+
next_review_due: 2027-05-27
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Angular 19.x (TypeScript 5.5)
|
|
13
|
+
|
|
14
|
+
Knowledge pack for projects on Angular 19.x (released Nov/2024). Covers projects in **legacy maintenance** mode — new features should target `angular-21.md`. Angular 19 introduced Resource API, Signal Forms (preview), `linkedSignal()`, stable `effect()`, and the `output()` function.
|
|
15
|
+
|
|
16
|
+
## 1. When to use this pack
|
|
17
|
+
|
|
18
|
+
- Project declares `Frontend stack: Angular 19.x` in `## Project Identity`.
|
|
19
|
+
- `package.json` declares `@angular/core: ^19.0.0` through `^19.2.x`.
|
|
20
|
+
- For Angular 18 projects: use `angular-18.md`.
|
|
21
|
+
- For Angular 21+ greenfield: use `angular-21.md`. This pack is for **maintained legacy** that has not yet jumped to 21.
|
|
22
|
+
|
|
23
|
+
## 2. Stack baseline (what this pack assumes)
|
|
24
|
+
|
|
25
|
+
| Component | Version range | Notes |
|
|
26
|
+
|---|---|---|
|
|
27
|
+
| Angular | 19.0.x — 19.2.x | Released Nov/2024; LTS through May/2026 |
|
|
28
|
+
| Node.js | 20.x LTS (min) / 22.x LTS preferred | Node 18 dropped in Angular 19 |
|
|
29
|
+
| TypeScript | 5.5.x — 5.6.x | TS 5.7 not yet supported in 19 |
|
|
30
|
+
| RxJS | 7.8.x | RxJS only for HTTP/streams; signals for local state |
|
|
31
|
+
| ng-bootstrap | 18.x | Compatible with Angular 19 |
|
|
32
|
+
| Jest | 29.x + jest-preset-angular 14.x | Karma deprecated; Jest is default |
|
|
33
|
+
| @testing-library/angular | 17.x | Component testing — query by role/text |
|
|
34
|
+
| Playwright | 1.48+ | E2E + browser-based a11y scan |
|
|
35
|
+
| @axe-core/playwright | 4.10+ | E2E a11y per ADR-007 |
|
|
36
|
+
| jest-axe | 9.x | Component-level a11y per ADR-007 |
|
|
37
|
+
| @lhci/cli | 0.14+ | Lighthouse budget per ADR-007 |
|
|
38
|
+
| Zone.js | OPTIONAL (zoneless viable in 19) | Zoneless more stable than 18 — evaluate per project |
|
|
39
|
+
|
|
40
|
+
## 3. Project structure
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
frontend/
|
|
44
|
+
├── src/app/
|
|
45
|
+
│ ├── core/ ← auth, http interceptors, guards (CanMatchFn)
|
|
46
|
+
│ ├── shared/ ← reusable standalone components, pipes, directives
|
|
47
|
+
│ ├── features/ ← lazy-loaded feature folders (each with routes.ts)
|
|
48
|
+
│ └── app.routes.ts
|
|
49
|
+
├── src/environments/ ← environment.ts, environment.prod.ts
|
|
50
|
+
├── src/styles/ ← global tokens, theme.scss
|
|
51
|
+
├── src/assets/
|
|
52
|
+
└── angular.json
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Rules**: 3 physical files per component (TS + HTML + SCSS — see §4). Standalone is default; NgModules forbidden for new code. Lazy load every feature.
|
|
56
|
+
|
|
57
|
+
## 4. Code patterns
|
|
58
|
+
|
|
59
|
+
### Component — 3 separate files (HARD RULE, ADR-008+)
|
|
60
|
+
|
|
61
|
+
`product-list.component.ts`:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { Component, ChangeDetectionStrategy, inject, resource } from '@angular/core';
|
|
65
|
+
import { CommonModule } from '@angular/common';
|
|
66
|
+
import { firstValueFrom } from 'rxjs';
|
|
67
|
+
import { ProductService } from './product.service';
|
|
68
|
+
|
|
69
|
+
@Component({
|
|
70
|
+
selector: 'app-product-list',
|
|
71
|
+
standalone: true,
|
|
72
|
+
imports: [CommonModule],
|
|
73
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
74
|
+
templateUrl: './product-list.component.html',
|
|
75
|
+
styleUrl: './product-list.component.scss',
|
|
76
|
+
})
|
|
77
|
+
export class ProductListComponent {
|
|
78
|
+
private svc = inject(ProductService);
|
|
79
|
+
|
|
80
|
+
// Resource API (NEW in 19) — automatic loading/error state
|
|
81
|
+
productsResource = resource({
|
|
82
|
+
loader: () => firstValueFrom(this.svc.list()),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
`product-list.component.html`:
|
|
88
|
+
|
|
89
|
+
```html
|
|
90
|
+
@if (productsResource.isLoading()) {
|
|
91
|
+
<p>Loading...</p>
|
|
92
|
+
} @else if (productsResource.error()) {
|
|
93
|
+
<p class="error">{{ productsResource.error()?.message }}</p>
|
|
94
|
+
} @else {
|
|
95
|
+
<ul>
|
|
96
|
+
@for (p of productsResource.value(); track p.id) {
|
|
97
|
+
<li>{{ p.name }}</li>
|
|
98
|
+
} @empty {
|
|
99
|
+
<li>No products.</li>
|
|
100
|
+
}
|
|
101
|
+
</ul>
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`product-list.component.scss`:
|
|
106
|
+
|
|
107
|
+
```scss
|
|
108
|
+
.error { color: var(--color-error); }
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Rule**: NEVER inline `template:` or `styles:` in the `.ts`. No size threshold. No "small component" exception. `code-reviewer` / `tech-lead` blocks the merge on violation.
|
|
112
|
+
|
|
113
|
+
### Resource API (NEW in 19)
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
// reactive params + auto reload when params change
|
|
117
|
+
productsResource = resource({
|
|
118
|
+
request: () => ({ category: this.category() }),
|
|
119
|
+
loader: ({ request }) => firstValueFrom(this.svc.list(request.category)),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
// rxResource for cold observables
|
|
123
|
+
detail = rxResource({
|
|
124
|
+
request: () => this.id(),
|
|
125
|
+
loader: ({ request }) => this.svc.get(request),
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**Rule**: prefer `resource()` over manual `signal<{loading,data,error}>` shapes. Replaces ad-hoc loading flags.
|
|
130
|
+
|
|
131
|
+
### Signal Forms (NEW in 19 — preview)
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { signalForm, control } from '@angular/forms';
|
|
135
|
+
import { required, maxLength, email } from '@angular/forms/validators';
|
|
136
|
+
|
|
137
|
+
form = signalForm({
|
|
138
|
+
name: control('', { validators: [required, maxLength(120)] }),
|
|
139
|
+
email: control('', { validators: [required, email] }),
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
submit() {
|
|
143
|
+
if (this.form.valid()) {
|
|
144
|
+
this.svc.create(this.form.value()).subscribe();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
**Note**: Signal Forms API still evolving in 19. Projects may prefer typed Reactive Forms (`FormBuilder.nonNullable.group`) until 21 stabilizes. Document trade-off in the project's `tech-debt.md`.
|
|
150
|
+
|
|
151
|
+
### Output function (NEW in 19) — replaces `@Output` decorator
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
// ❌ Legacy
|
|
155
|
+
@Output() saved = new EventEmitter<Product>();
|
|
156
|
+
|
|
157
|
+
// ✅ Angular 19
|
|
158
|
+
saved = output<Product>();
|
|
159
|
+
|
|
160
|
+
// emit:
|
|
161
|
+
this.saved.emit(product);
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Migration tool: `ng generate @angular/core:output-migration`.
|
|
165
|
+
|
|
166
|
+
### linkedSignal — derived but mutable
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
// derived value that can be overridden by user
|
|
170
|
+
total = linkedSignal(() => this.items().reduce((s, i) => s + i.price, 0));
|
|
171
|
+
|
|
172
|
+
// allow manual override:
|
|
173
|
+
overrideTotal(v: number) { this.total.set(v); }
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Effect (stable in 19)
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
constructor() {
|
|
180
|
+
effect(() => {
|
|
181
|
+
console.log('Cart size:', this.items().length);
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**Rule**: never trigger HTTP in `effect()` — use Resource API. Effects for logging, persistence, DOM side effects only.
|
|
187
|
+
|
|
188
|
+
### Service with typed HttpClient + interceptor
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
@Injectable({ providedIn: 'root' })
|
|
192
|
+
export class ProductService {
|
|
193
|
+
private http = inject(HttpClient);
|
|
194
|
+
private base = '/api/v1/products';
|
|
195
|
+
|
|
196
|
+
list(category?: string): Observable<Product[]> {
|
|
197
|
+
const params = category ? new HttpParams().set('category', category) : undefined;
|
|
198
|
+
return this.http.get<Product[]>(this.base, { params });
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
create(req: CreateProductRequest): Observable<Product> {
|
|
202
|
+
return this.http.post<Product>(this.base, req);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// core/auth.interceptor.ts (functional)
|
|
207
|
+
export const authInterceptor: HttpInterceptorFn = (req, next) => {
|
|
208
|
+
const token = inject(AuthStore).token();
|
|
209
|
+
return token ? next(req.clone({ setHeaders: { Authorization: `Bearer ${token}` } })) : next(req);
|
|
210
|
+
};
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Guards (CanMatchFn — functional)
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
export const authGuard: CanMatchFn = () => {
|
|
217
|
+
const auth = inject(AuthStore);
|
|
218
|
+
const router = inject(Router);
|
|
219
|
+
return auth.isLoggedIn() || router.createUrlTree(['/login']);
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
// app.routes.ts
|
|
223
|
+
{
|
|
224
|
+
path: 'admin',
|
|
225
|
+
canMatch: [authGuard],
|
|
226
|
+
loadChildren: () => import('./features/admin/admin.routes'),
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## 5. Testing
|
|
231
|
+
|
|
232
|
+
### Component (Jest + Testing Library)
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
import { render, screen } from '@testing-library/angular';
|
|
236
|
+
import { of } from 'rxjs';
|
|
237
|
+
import { ProductListComponent } from './product-list.component';
|
|
238
|
+
import { ProductService } from './product.service';
|
|
239
|
+
|
|
240
|
+
test('resource loads and renders products', async () => {
|
|
241
|
+
await render(ProductListComponent, {
|
|
242
|
+
providers: [
|
|
243
|
+
{ provide: ProductService, useValue: { list: () => of([{ id: '1', name: 'widget' }]) } },
|
|
244
|
+
],
|
|
245
|
+
});
|
|
246
|
+
expect(await screen.findByText('widget')).toBeVisible();
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### A11y component (jest-axe — per ADR-007)
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
import { axe, toHaveNoViolations } from 'jest-axe';
|
|
254
|
+
expect.extend(toHaveNoViolations);
|
|
255
|
+
|
|
256
|
+
test('has no a11y violations', async () => {
|
|
257
|
+
const { container } = await render(ProductListComponent, { providers: [...] });
|
|
258
|
+
expect(await axe(container)).toHaveNoViolations();
|
|
259
|
+
});
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Threshold: 0 `serious` / 0 `critical` (ADR-007).
|
|
263
|
+
|
|
264
|
+
### E2E (Playwright + axe-core)
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
import { test, expect } from '@playwright/test';
|
|
268
|
+
import AxeBuilder from '@axe-core/playwright';
|
|
269
|
+
|
|
270
|
+
test('product list is accessible', async ({ page }) => {
|
|
271
|
+
await page.goto('/products');
|
|
272
|
+
await expect(page.getByRole('list')).toBeVisible();
|
|
273
|
+
const results = await new AxeBuilder({ page }).analyze();
|
|
274
|
+
expect(results.violations.filter(v => ['serious', 'critical'].includes(v.impact!))).toEqual([]);
|
|
275
|
+
});
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
### Pyramid ratio (ADR-007)
|
|
279
|
+
|
|
280
|
+
E2E ≤ 30% of total tests (hard-fail above; ideal ≤ 15%). `auto-test-guard` counts and blocks on violation.
|
|
281
|
+
|
|
282
|
+
## 6. Build & run commands
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
npm install
|
|
286
|
+
npm start # ng serve, port 4200
|
|
287
|
+
npm run build -- --configuration=production # prod build
|
|
288
|
+
npm test -- --watchAll=false --coverage # Jest + coverage
|
|
289
|
+
npx playwright test # E2E
|
|
290
|
+
npx playwright test --ui # E2E with UI
|
|
291
|
+
npx lhci autorun # Lighthouse budget
|
|
292
|
+
npx ng update @angular/core@19 @angular/cli@19 # safe within-major updates
|
|
293
|
+
npm audit --audit-level=high # CVE scan
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## 7. Security (per ADR-007 + ADR-027)
|
|
297
|
+
|
|
298
|
+
### 7.1 Authentication & Authorization
|
|
299
|
+
|
|
300
|
+
- **JWT** stored in `httpOnly` cookie (preferred) OR in-memory signal (NEVER `localStorage` for tokens — XSS-vulnerable).
|
|
301
|
+
- **Functional `HttpInterceptorFn`** attaches `Authorization: Bearer <token>` per request.
|
|
302
|
+
- **Route guards**: `CanMatchFn` for lazy-load gates (prevents bundle download for unauthorized users); `CanActivateFn` for runtime checks.
|
|
303
|
+
- **Refresh token flow**: silent renewal via interceptor on 401 + retry.
|
|
304
|
+
|
|
305
|
+
### 7.2 CORS
|
|
306
|
+
|
|
307
|
+
Backend responsibility — Angular only consumes. NEVER request `*` from backend. Allowed origins must be the production app's origin only.
|
|
308
|
+
|
|
309
|
+
### 7.3 Validation & XSS
|
|
310
|
+
|
|
311
|
+
- Angular auto-escapes interpolation `{{ value }}` — safe by default.
|
|
312
|
+
- **NEVER** `[innerHTML]="userInput"` without `DomSanitizer.bypassSecurityTrustHtml(...)` (and even then, audit the source).
|
|
313
|
+
- **NEVER** `eval()` or `Function()` with user input.
|
|
314
|
+
- Signal Forms validators are typed natively in 19 — fewer cast errors.
|
|
315
|
+
- Sanitize file uploads on the backend (MIME + extension + content scan).
|
|
316
|
+
|
|
317
|
+
### 7.4 Secrets management
|
|
318
|
+
|
|
319
|
+
- NEVER commit API keys, OAuth client secrets, or tokens to `src/environments/*.ts`.
|
|
320
|
+
- Use build-time injection via CI env vars: `ng build --define environment.apiKey="$API_KEY"`.
|
|
321
|
+
- For runtime config: `/assets/config.json` fetched on bootstrap (not committed).
|
|
322
|
+
- Local dev: `environment.local.ts` in `.gitignore`.
|
|
323
|
+
|
|
324
|
+
### 7.5 Rate limiting
|
|
325
|
+
|
|
326
|
+
Backend responsibility. Frontend SHOULD debounce search inputs and disable submit buttons during in-flight requests to avoid spamming:
|
|
327
|
+
|
|
328
|
+
```typescript
|
|
329
|
+
search = signal('');
|
|
330
|
+
debouncedSearch = toSignal(toObservable(this.search).pipe(debounceTime(300)));
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### 7.6 OWASP Top 10 mapping
|
|
334
|
+
|
|
335
|
+
| OWASP | Mitigation in this stack |
|
|
336
|
+
|---|---|
|
|
337
|
+
| A01 Broken Access Control | Route guards (`CanMatchFn`) + backend re-checks; never trust frontend RBAC |
|
|
338
|
+
| A02 Cryptographic Failures | TLS 1.3; tokens in `httpOnly` cookies or memory; no plaintext in `localStorage` |
|
|
339
|
+
| A03 Injection | Angular auto-escape; `DomSanitizer` for trusted HTML; backend parameterized queries |
|
|
340
|
+
| A04 Insecure Design | ADR for new auth/payment flows; threat-model lazy routes |
|
|
341
|
+
| A05 Security Misconfiguration | CSP header from backend; `ng build --configuration=production` strips dev hooks |
|
|
342
|
+
| A06 Vulnerable Components | `npm audit --audit-level=high` in CI; Renovate/Dependabot; `ng update` per minor |
|
|
343
|
+
| A07 Auth Failures | Generic error messages; rate-limit handled by backend; MFA flow respects standard `oauth2-pkce` |
|
|
344
|
+
| A08 Data Integrity | SRI for CDN scripts; signed build artifacts; verify lockfile in CI |
|
|
345
|
+
| A09 Logging Failures | Browser console scrubbed in prod; never log JWT/PII; structured logs forwarded to backend |
|
|
346
|
+
| A10 SSRF | N/A frontend-direct; ensure backend allowlists outbound URLs |
|
|
347
|
+
|
|
348
|
+
### 7.7 LGPD specifics
|
|
349
|
+
|
|
350
|
+
- Cookie consent banner before any non-essential cookie/analytics.
|
|
351
|
+
- Right to deletion: UI flow that triggers backend soft-delete + email confirmation.
|
|
352
|
+
- Data subject access: profile page exports user data as JSON via signed link.
|
|
353
|
+
- PII fields tagged in forms (e.g. `data-pii="true"`) so analytics can suppress them.
|
|
354
|
+
|
|
355
|
+
## 8. Anti-patterns (block in code-review)
|
|
356
|
+
|
|
357
|
+
| ❌ Bad | ✅ Good | Why |
|
|
358
|
+
|---|---|---|
|
|
359
|
+
| `template: '<div>...'` or `styles: [...]` inline in `.ts` | 3 separate files (`.ts` + `.html` + `.scss`) | Hard rule — non-negotiable (ADR-008) |
|
|
360
|
+
| `@Output() x = new EventEmitter()` (new code) | `x = output<T>()` | Function API; decorator deprecated in roadmap |
|
|
361
|
+
| `any` in TypeScript | Explicit type or `unknown` + narrow | TS strict mode |
|
|
362
|
+
| Manual `loading`/`error`/`data` signals | `resource()` / `rxResource()` | Built-in state machine |
|
|
363
|
+
| NgModule for new feature | `standalone: true` component + `loadChildren` route | Standalone is default since 17 |
|
|
364
|
+
| `ChangeDetectionStrategy.Default` | `ChangeDetectionStrategy.OnPush` | Required for signal-based change detection |
|
|
365
|
+
| `signal().push(item)` (mutation) | `signal.update(arr => [...arr, item])` | Signals require new reference |
|
|
366
|
+
| `[innerHTML]="userInput"` raw | `DomSanitizer.sanitize(SecurityContext.HTML, x)` | XSS risk |
|
|
367
|
+
| Token in `localStorage` | `httpOnly` cookie or in-memory signal | XSS-stealable |
|
|
368
|
+
| RxJS for local component state | Signals | RxJS overhead unnecessary for state |
|
|
369
|
+
| `*ngIf` / `*ngFor` (legacy) | `@if` / `@for` (built-in control flow) | Type-safer + tree-shakable |
|
|
370
|
+
| Zone.js for greenfield (in 19) | Evaluate zoneless (`provideExperimentalZonelessChangeDetection`) | Smaller bundle, more stable in 19 |
|
|
371
|
+
| `effect()` triggering HTTP | `resource()` / explicit `subscribe` | Effects are for side effects, not data fetch |
|
|
372
|
+
|
|
373
|
+
## 9. Migration hints — Angular 19 → 21
|
|
374
|
+
|
|
375
|
+
When migrating a project from this pack to `angular-21.md`:
|
|
376
|
+
|
|
377
|
+
- **Skip Angular 20** (released ~May/2025) if practical — jumping 19 → 21 is supported via two consecutive `ng update` runs. Read each release's breaking changes.
|
|
378
|
+
- **Resource API matures** — `request` / `loader` signatures stabilize; some 19-preview options may rename.
|
|
379
|
+
- **Signal Forms** leave preview — APIs from 19 may need rename (`signalForm` → final shape). Run `ng update` migration schematics.
|
|
380
|
+
- **Server Components** exit experimental — may be adopted for new pages.
|
|
381
|
+
- **esbuild builder** becomes default for all new projects (already opt-in in 19).
|
|
382
|
+
- **Zoneless** likely default in 21 — audit any code relying on `NgZone.run`.
|
|
383
|
+
- **TypeScript** moves to 5.7+ — review `tsconfig.strict` deltas.
|
|
384
|
+
- Use the `migrator` agent — it knows both packs and produces an ADR per major cross.
|
|
385
|
+
|
|
386
|
+
## 10. References
|
|
387
|
+
|
|
388
|
+
- ADR-007 (Senior+ gate thresholds — a11y, Lighthouse, pyramid)
|
|
389
|
+
- ADR-026 (Generic agents + packs architecture)
|
|
390
|
+
- ADR-027 (This pack's governance — frontmatter, security review cadence)
|
|
391
|
+
- ADR-029 (Canonical pack format)
|
|
392
|
+
- Angular 19 release notes: https://blog.angular.dev/meet-angular-v19-7b29dfd05b84
|
|
393
|
+
- Angular Update Guide: https://update.angular.io/
|
|
394
|
+
- Signal Forms RFC: https://github.com/angular/angular/discussions/53485
|
|
395
|
+
- Resource API docs: https://angular.dev/guide/signals/resource
|
|
396
|
+
- OWASP Top 10 (2021): https://owasp.org/Top10/
|
|
397
|
+
- OWASP Angular Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/AngularJS_Security_Cheat_Sheet.html
|