@qball-inc/the-bulwark 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.
Files changed (175) hide show
  1. package/.claude-plugin/plugin.json +43 -0
  2. package/agents/bulwark-fix-validator.md +633 -0
  3. package/agents/bulwark-implementer.md +391 -0
  4. package/agents/bulwark-issue-analyzer.md +308 -0
  5. package/agents/bulwark-standards-reviewer.md +221 -0
  6. package/agents/plan-creation-architect.md +323 -0
  7. package/agents/plan-creation-eng-lead.md +352 -0
  8. package/agents/plan-creation-po.md +300 -0
  9. package/agents/plan-creation-qa-critic.md +334 -0
  10. package/agents/product-ideation-competitive-analyzer.md +298 -0
  11. package/agents/product-ideation-idea-validator.md +268 -0
  12. package/agents/product-ideation-market-researcher.md +292 -0
  13. package/agents/product-ideation-pattern-documenter.md +308 -0
  14. package/agents/product-ideation-segment-analyzer.md +303 -0
  15. package/agents/product-ideation-strategist.md +259 -0
  16. package/agents/statusline-setup.md +97 -0
  17. package/hooks/hooks.json +59 -0
  18. package/package.json +45 -0
  19. package/scripts/hooks/cleanup-stale.sh +13 -0
  20. package/scripts/hooks/enforce-quality.sh +166 -0
  21. package/scripts/hooks/implementer-quality.sh +256 -0
  22. package/scripts/hooks/inject-protocol.sh +52 -0
  23. package/scripts/hooks/suggest-pipeline.sh +175 -0
  24. package/scripts/hooks/track-pipeline-start.sh +37 -0
  25. package/scripts/hooks/track-pipeline-stop.sh +52 -0
  26. package/scripts/init-rules.sh +35 -0
  27. package/scripts/init.sh +151 -0
  28. package/skills/anthropic-validator/SKILL.md +607 -0
  29. package/skills/anthropic-validator/references/agents-checklist.md +131 -0
  30. package/skills/anthropic-validator/references/commands-checklist.md +102 -0
  31. package/skills/anthropic-validator/references/hooks-checklist.md +151 -0
  32. package/skills/anthropic-validator/references/mcp-checklist.md +136 -0
  33. package/skills/anthropic-validator/references/plugins-checklist.md +148 -0
  34. package/skills/anthropic-validator/references/skills-checklist.md +85 -0
  35. package/skills/assertion-patterns/SKILL.md +296 -0
  36. package/skills/bug-magnet-data/SKILL.md +284 -0
  37. package/skills/bug-magnet-data/context/cli-args.md +91 -0
  38. package/skills/bug-magnet-data/context/db-query.md +104 -0
  39. package/skills/bug-magnet-data/context/file-contents.md +103 -0
  40. package/skills/bug-magnet-data/context/http-body.md +91 -0
  41. package/skills/bug-magnet-data/context/process-spawn.md +123 -0
  42. package/skills/bug-magnet-data/data/booleans/boundaries.yaml +143 -0
  43. package/skills/bug-magnet-data/data/collections/arrays.yaml +114 -0
  44. package/skills/bug-magnet-data/data/collections/objects.yaml +123 -0
  45. package/skills/bug-magnet-data/data/concurrency/race-conditions.yaml +118 -0
  46. package/skills/bug-magnet-data/data/concurrency/state-machines.yaml +115 -0
  47. package/skills/bug-magnet-data/data/dates/boundaries.yaml +137 -0
  48. package/skills/bug-magnet-data/data/dates/invalid.yaml +132 -0
  49. package/skills/bug-magnet-data/data/dates/timezone.yaml +118 -0
  50. package/skills/bug-magnet-data/data/encoding/charset.yaml +79 -0
  51. package/skills/bug-magnet-data/data/encoding/normalization.yaml +105 -0
  52. package/skills/bug-magnet-data/data/formats/email.yaml +154 -0
  53. package/skills/bug-magnet-data/data/formats/json.yaml +187 -0
  54. package/skills/bug-magnet-data/data/formats/url.yaml +165 -0
  55. package/skills/bug-magnet-data/data/language-specific/javascript.yaml +182 -0
  56. package/skills/bug-magnet-data/data/language-specific/python.yaml +174 -0
  57. package/skills/bug-magnet-data/data/language-specific/rust.yaml +148 -0
  58. package/skills/bug-magnet-data/data/numbers/boundaries.yaml +161 -0
  59. package/skills/bug-magnet-data/data/numbers/precision.yaml +89 -0
  60. package/skills/bug-magnet-data/data/numbers/special.yaml +69 -0
  61. package/skills/bug-magnet-data/data/strings/boundaries.yaml +109 -0
  62. package/skills/bug-magnet-data/data/strings/injection.yaml +208 -0
  63. package/skills/bug-magnet-data/data/strings/special-chars.yaml +190 -0
  64. package/skills/bug-magnet-data/data/strings/unicode.yaml +139 -0
  65. package/skills/bug-magnet-data/references/external-lists.md +115 -0
  66. package/skills/bulwark-brainstorm/SKILL.md +563 -0
  67. package/skills/bulwark-brainstorm/references/at-teammate-prompts.md +60 -0
  68. package/skills/bulwark-brainstorm/references/role-critical-analyst.md +78 -0
  69. package/skills/bulwark-brainstorm/references/role-development-lead.md +66 -0
  70. package/skills/bulwark-brainstorm/references/role-product-delivery-lead.md +79 -0
  71. package/skills/bulwark-brainstorm/references/role-product-manager.md +62 -0
  72. package/skills/bulwark-brainstorm/references/role-project-sme.md +59 -0
  73. package/skills/bulwark-brainstorm/references/role-technical-architect.md +66 -0
  74. package/skills/bulwark-research/SKILL.md +298 -0
  75. package/skills/bulwark-research/references/viewpoint-contrarian.md +63 -0
  76. package/skills/bulwark-research/references/viewpoint-direct-investigation.md +62 -0
  77. package/skills/bulwark-research/references/viewpoint-first-principles.md +65 -0
  78. package/skills/bulwark-research/references/viewpoint-practitioner.md +62 -0
  79. package/skills/bulwark-research/references/viewpoint-prior-art.md +66 -0
  80. package/skills/bulwark-scaffold/SKILL.md +330 -0
  81. package/skills/bulwark-statusline/SKILL.md +161 -0
  82. package/skills/bulwark-statusline/scripts/statusline.sh +144 -0
  83. package/skills/bulwark-verify/SKILL.md +519 -0
  84. package/skills/code-review/SKILL.md +428 -0
  85. package/skills/code-review/examples/anti-patterns/linting.ts +181 -0
  86. package/skills/code-review/examples/anti-patterns/security.ts +91 -0
  87. package/skills/code-review/examples/anti-patterns/standards.ts +195 -0
  88. package/skills/code-review/examples/anti-patterns/type-safety.ts +108 -0
  89. package/skills/code-review/examples/recommended/linting.ts +195 -0
  90. package/skills/code-review/examples/recommended/security.ts +154 -0
  91. package/skills/code-review/examples/recommended/standards.ts +231 -0
  92. package/skills/code-review/examples/recommended/type-safety.ts +181 -0
  93. package/skills/code-review/frameworks/angular.md +218 -0
  94. package/skills/code-review/frameworks/django.md +235 -0
  95. package/skills/code-review/frameworks/express.md +207 -0
  96. package/skills/code-review/frameworks/flask.md +298 -0
  97. package/skills/code-review/frameworks/generic.md +146 -0
  98. package/skills/code-review/frameworks/react.md +152 -0
  99. package/skills/code-review/frameworks/vue.md +244 -0
  100. package/skills/code-review/references/linting-patterns.md +221 -0
  101. package/skills/code-review/references/security-patterns.md +125 -0
  102. package/skills/code-review/references/standards-patterns.md +246 -0
  103. package/skills/code-review/references/type-safety-patterns.md +130 -0
  104. package/skills/component-patterns/SKILL.md +131 -0
  105. package/skills/component-patterns/references/pattern-cli-command.md +118 -0
  106. package/skills/component-patterns/references/pattern-database.md +166 -0
  107. package/skills/component-patterns/references/pattern-external-api.md +139 -0
  108. package/skills/component-patterns/references/pattern-file-parser.md +168 -0
  109. package/skills/component-patterns/references/pattern-http-server.md +162 -0
  110. package/skills/component-patterns/references/pattern-process-spawner.md +133 -0
  111. package/skills/continuous-feedback/SKILL.md +327 -0
  112. package/skills/continuous-feedback/references/collect-instructions.md +81 -0
  113. package/skills/continuous-feedback/references/specialize-code-review.md +82 -0
  114. package/skills/continuous-feedback/references/specialize-general.md +98 -0
  115. package/skills/continuous-feedback/references/specialize-test-audit.md +81 -0
  116. package/skills/create-skill/SKILL.md +359 -0
  117. package/skills/create-skill/references/agent-conventions.md +194 -0
  118. package/skills/create-skill/references/agent-template.md +195 -0
  119. package/skills/create-skill/references/content-guidance.md +291 -0
  120. package/skills/create-skill/references/decision-framework.md +124 -0
  121. package/skills/create-skill/references/template-pipeline.md +217 -0
  122. package/skills/create-skill/references/template-reference-heavy.md +111 -0
  123. package/skills/create-skill/references/template-research.md +210 -0
  124. package/skills/create-skill/references/template-script-driven.md +172 -0
  125. package/skills/create-skill/references/template-simple.md +80 -0
  126. package/skills/create-subagent/SKILL.md +353 -0
  127. package/skills/create-subagent/references/agent-conventions.md +268 -0
  128. package/skills/create-subagent/references/content-guidance.md +232 -0
  129. package/skills/create-subagent/references/decision-framework.md +134 -0
  130. package/skills/create-subagent/references/template-single-agent.md +192 -0
  131. package/skills/fix-bug/SKILL.md +241 -0
  132. package/skills/governance-protocol/SKILL.md +116 -0
  133. package/skills/init/SKILL.md +341 -0
  134. package/skills/issue-debugging/SKILL.md +385 -0
  135. package/skills/issue-debugging/references/anti-patterns.md +245 -0
  136. package/skills/issue-debugging/references/debug-report-schema.md +227 -0
  137. package/skills/mock-detection/SKILL.md +511 -0
  138. package/skills/mock-detection/references/false-positive-prevention.md +402 -0
  139. package/skills/mock-detection/references/stub-patterns.md +236 -0
  140. package/skills/pipeline-templates/SKILL.md +215 -0
  141. package/skills/pipeline-templates/references/code-change-workflow.md +277 -0
  142. package/skills/pipeline-templates/references/code-review.md +336 -0
  143. package/skills/pipeline-templates/references/fix-validation.md +421 -0
  144. package/skills/pipeline-templates/references/new-feature.md +335 -0
  145. package/skills/pipeline-templates/references/research-brainstorm.md +161 -0
  146. package/skills/pipeline-templates/references/research-planning.md +257 -0
  147. package/skills/pipeline-templates/references/test-audit.md +389 -0
  148. package/skills/pipeline-templates/references/test-execution-fix.md +238 -0
  149. package/skills/plan-creation/SKILL.md +497 -0
  150. package/skills/product-ideation/SKILL.md +372 -0
  151. package/skills/product-ideation/references/analysis-frameworks.md +161 -0
  152. package/skills/session-handoff/SKILL.md +139 -0
  153. package/skills/session-handoff/references/examples.md +223 -0
  154. package/skills/setup-lsp/SKILL.md +312 -0
  155. package/skills/setup-lsp/references/server-registry.md +85 -0
  156. package/skills/setup-lsp/references/troubleshooting.md +135 -0
  157. package/skills/subagent-output-templating/SKILL.md +415 -0
  158. package/skills/subagent-output-templating/references/examples.md +440 -0
  159. package/skills/subagent-prompting/SKILL.md +364 -0
  160. package/skills/subagent-prompting/references/examples.md +342 -0
  161. package/skills/test-audit/SKILL.md +531 -0
  162. package/skills/test-audit/references/known-limitations.md +41 -0
  163. package/skills/test-audit/references/priority-classification.md +30 -0
  164. package/skills/test-audit/references/prompts/deep-mode-detection.md +83 -0
  165. package/skills/test-audit/references/prompts/synthesis.md +57 -0
  166. package/skills/test-audit/references/rewrite-instructions.md +46 -0
  167. package/skills/test-audit/references/schemas/audit-output.yaml +100 -0
  168. package/skills/test-audit/references/schemas/diagnostic-output.yaml +49 -0
  169. package/skills/test-audit/scripts/data-flow-analyzer.ts +509 -0
  170. package/skills/test-audit/scripts/integration-mock-detector.ts +462 -0
  171. package/skills/test-audit/scripts/package.json +20 -0
  172. package/skills/test-audit/scripts/skip-detector.ts +211 -0
  173. package/skills/test-audit/scripts/verification-counter.ts +295 -0
  174. package/skills/test-classification/SKILL.md +310 -0
  175. package/skills/test-fixture-creation/SKILL.md +295 -0
@@ -0,0 +1,231 @@
1
+ /**
2
+ * Coding Standards Recommended Patterns
3
+ *
4
+ * These examples demonstrate good coding standards.
5
+ */
6
+
7
+ // CS1: Single Responsibility - Each function does one thing
8
+ async function validateOrder(order: Order): Promise<void> {
9
+ if (!order.items.length) {
10
+ throw new OrderValidationError('Empty order');
11
+ }
12
+ }
13
+
14
+ function calculateOrderTotal(order: Order): number {
15
+ return order.items.reduce((sum, item) => sum + item.price, 0);
16
+ }
17
+
18
+ async function saveOrder(order: Order): Promise<void> {
19
+ await database.orders.save(order);
20
+ }
21
+
22
+ async function notifyOrderConfirmation(order: Order): Promise<void> {
23
+ await emailService.send(order.customer.email, 'Order confirmed');
24
+ }
25
+
26
+ async function trackOrderProcessed(order: Order): Promise<void> {
27
+ await analytics.track('order_processed', order.id);
28
+ }
29
+
30
+ // Composition of single-responsibility functions
31
+ async function processOrderClean(order: Order): Promise<Order> {
32
+ await validateOrder(order);
33
+ order.total = calculateOrderTotal(order);
34
+ await saveOrder(order);
35
+ await notifyOrderConfirmation(order);
36
+ await trackOrderProcessed(order);
37
+ return order;
38
+ }
39
+
40
+ // CS2: Named Constants - No Magic Values
41
+ const DISCOUNT_THRESHOLDS = {
42
+ MINIMUM_FOR_DISCOUNT: 100,
43
+ } as const;
44
+
45
+ const DISCOUNT_RATES = {
46
+ GOLD: 0.2,
47
+ SILVER: 0.1,
48
+ DEFAULT: 0.05,
49
+ } as const;
50
+
51
+ type CustomerType = 'gold' | 'silver' | 'bronze';
52
+
53
+ function calculateDiscountClean(total: number, customerType: CustomerType): number {
54
+ if (total <= DISCOUNT_THRESHOLDS.MINIMUM_FOR_DISCOUNT) {
55
+ return total * DISCOUNT_RATES.DEFAULT;
56
+ }
57
+
58
+ const rate = customerType === 'gold'
59
+ ? DISCOUNT_RATES.GOLD
60
+ : customerType === 'silver'
61
+ ? DISCOUNT_RATES.SILVER
62
+ : DISCOUNT_RATES.DEFAULT;
63
+
64
+ return total * rate;
65
+ }
66
+
67
+ // CS2: Explicit Transformation - Return new object instead of mutation
68
+ function normalizeUserClean(user: User): NormalizedUser {
69
+ return {
70
+ ...user,
71
+ email: user.email.toLowerCase(),
72
+ name: user.name.trim(),
73
+ normalizedAt: new Date(),
74
+ };
75
+ }
76
+
77
+ // CS2: Explicit Dependencies - Pass state as parameter
78
+ interface Transaction {
79
+ id: string;
80
+ amount: number;
81
+ status: 'pending' | 'processing' | 'completed';
82
+ }
83
+
84
+ function processPaymentClean(
85
+ transaction: Transaction,
86
+ amount: number
87
+ ): Transaction {
88
+ return {
89
+ ...transaction,
90
+ amount,
91
+ status: 'processing',
92
+ };
93
+ }
94
+
95
+ // CS2: Pure Getters - No side effects
96
+ class UserProfileClean {
97
+ private _views = 0;
98
+ private readonly id: string;
99
+
100
+ get viewCount(): number {
101
+ return this._views;
102
+ }
103
+
104
+ // Side effect is explicit method, not hidden in getter
105
+ recordView(): void {
106
+ this._views++;
107
+ }
108
+
109
+ // Analytics tracking is separate concern
110
+ trackProfileView(): void {
111
+ analytics.track('profile_viewed', this.id);
112
+ }
113
+ }
114
+
115
+ // CS3: Fail Fast - Propagate errors with context
116
+ async function fetchDataClean(id: string): Promise<Data> {
117
+ try {
118
+ return await api.getData(id);
119
+ } catch (error) {
120
+ // Log for debugging
121
+ console.error(`Failed to fetch data for id=${id}:`, error);
122
+ // Re-throw with context
123
+ throw new DataFetchError(`Failed to fetch data: ${id}`, { cause: error });
124
+ }
125
+ }
126
+
127
+ // CS3: Fail on Invalid Input
128
+ function parseConfigClean(raw: string): Config {
129
+ try {
130
+ const parsed = JSON.parse(raw);
131
+ return validateConfig(parsed);
132
+ } catch (error) {
133
+ throw new ConfigParseError('Invalid configuration', { cause: error });
134
+ }
135
+ }
136
+
137
+ // CS3: Input Validation at Boundaries
138
+ function calculateAgeClean(birthYear: number): number {
139
+ const currentYear = new Date().getFullYear();
140
+
141
+ if (!Number.isInteger(birthYear)) {
142
+ throw new ValidationError('Birth year must be an integer');
143
+ }
144
+ if (birthYear < 1900) {
145
+ throw new ValidationError('Birth year too far in the past');
146
+ }
147
+ if (birthYear > currentYear) {
148
+ throw new ValidationError('Birth year cannot be in the future');
149
+ }
150
+
151
+ return currentYear - birthYear;
152
+ }
153
+
154
+ // CS4: Clean Imports - Only import what's used
155
+ import { neededHelper } from './utils';
156
+ import type { Config, Data } from './types';
157
+
158
+ // CS4: All Variables Used
159
+ function processItemsClean(items: Item[]): string[] {
160
+ // Only declare what's needed
161
+ return items.map((item) => item.name);
162
+ }
163
+
164
+ // CS4: No Commented-Out Code - Delete unused code
165
+ function handleRequestClean(req: Request) {
166
+ // Old implementation deleted, not commented
167
+ return processNewWay(req);
168
+ }
169
+
170
+ // CS4: No Dead Code - All paths reachable
171
+ function processClean(value: number): number {
172
+ if (value > 0) {
173
+ return value * 2;
174
+ }
175
+ return value;
176
+ }
177
+
178
+ /**
179
+ * Calculate a complex weighted metric from data points.
180
+ *
181
+ * @param data - Array of metric data points to analyze
182
+ * @param weights - Weight for each data point (must match data length)
183
+ * @param normalize - Whether to normalize the result to 0-1 range
184
+ * @param threshold - Minimum value to include in calculation
185
+ * @returns Calculated metric with metadata
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * const result = calculateComplexMetric(
190
+ * [{ value: 10 }, { value: 20 }],
191
+ * [0.3, 0.7],
192
+ * true,
193
+ * 5
194
+ * );
195
+ * ```
196
+ */
197
+ export function calculateComplexMetricClean(
198
+ data: MetricData[],
199
+ weights: number[],
200
+ normalize: boolean,
201
+ threshold: number
202
+ ): ComplexResult {
203
+ validateInputs(data, weights, threshold);
204
+ return internalCalculation(data, weights, normalize, threshold);
205
+ }
206
+
207
+ // Consistent Error Handling
208
+ async function fetchAllClean(): Promise<FetchResult> {
209
+ // Consistent try-catch for all operations
210
+ try {
211
+ const [users, orders, products] = await Promise.all([
212
+ fetch('/users').then((r) => r.json()),
213
+ fetch('/orders').then((r) => r.json()),
214
+ fetch('/products').then((r) => r.json()),
215
+ ]);
216
+
217
+ return { users, orders, products, success: true };
218
+ } catch (error) {
219
+ console.error('Failed to fetch data:', error);
220
+ throw new DataFetchError('Failed to load data', { cause: error });
221
+ }
222
+ }
223
+
224
+ // Consistent Async Style
225
+ async function loadDataClean(): Promise<ProcessedData> {
226
+ // Consistent await throughout
227
+ const config = await loadConfig();
228
+ const response = await fetch(config.url);
229
+ const data = await response.json();
230
+ return processData(data);
231
+ }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Type Safety Recommended Patterns
3
+ *
4
+ * These examples demonstrate type-safe coding practices.
5
+ */
6
+
7
+ // Proper typing instead of `any`
8
+ interface ProcessableData {
9
+ id: string;
10
+ payload: Record<string, unknown>;
11
+ }
12
+
13
+ function processDataSafe(data: ProcessableData) {
14
+ // Type-safe access
15
+ return data.payload;
16
+ }
17
+
18
+ // Typed return with proper interface
19
+ interface User {
20
+ id: string;
21
+ name: string;
22
+ email: string;
23
+ }
24
+
25
+ async function fetchUserSafe(id: string): Promise<User> {
26
+ const response = await fetch(`/api/users/${id}`);
27
+ const data = await response.json();
28
+
29
+ // Validate shape before returning
30
+ if (!isUser(data)) {
31
+ throw new Error('Invalid user data');
32
+ }
33
+ return data;
34
+ }
35
+
36
+ // Type guard for runtime validation
37
+ function isUser(data: unknown): data is User {
38
+ return (
39
+ typeof data === 'object' &&
40
+ data !== null &&
41
+ 'id' in data &&
42
+ 'name' in data &&
43
+ 'email' in data
44
+ );
45
+ }
46
+
47
+ // JSON parsing with validation
48
+ import { z } from 'zod';
49
+
50
+ const ConfigSchema = z.object({
51
+ apiUrl: z.string().url(),
52
+ timeout: z.number().positive(),
53
+ });
54
+
55
+ type Config = z.infer<typeof ConfigSchema>;
56
+
57
+ function loadConfigSafe(): Config {
58
+ const raw = JSON.parse(process.env.CONFIG ?? '{}');
59
+ return ConfigSchema.parse(raw); // Throws if invalid
60
+ }
61
+
62
+ // Explicit parameter types
63
+ function calculateSafe(a: number, b: number): number {
64
+ return a + b;
65
+ }
66
+
67
+ // Safe null handling with type narrowing
68
+ interface UserProfile {
69
+ name: string;
70
+ profile?: {
71
+ avatar?: string;
72
+ };
73
+ }
74
+
75
+ function getAvatarSafe(user: UserProfile | null): string {
76
+ if (!user) {
77
+ return 'default-avatar.png';
78
+ }
79
+ return user.profile?.avatar ?? 'default-avatar.png';
80
+ }
81
+
82
+ // Consistent optional chaining
83
+ function getNameSafe(user: UserProfile | null): string {
84
+ // All access uses optional chaining consistently
85
+ return user?.profile?.avatar ?? 'default';
86
+ }
87
+
88
+ // Nullish coalescing for valid falsy values
89
+ function getCountSafe(items?: number): number {
90
+ // Uses ?? to only default on null/undefined, not 0
91
+ return items ?? 10;
92
+ }
93
+
94
+ // Type narrowing instead of assertion
95
+ function convertSafe(value: string): number {
96
+ const parsed = parseInt(value, 10);
97
+ if (isNaN(parsed)) {
98
+ throw new Error(`Cannot convert "${value}" to number`);
99
+ }
100
+ return parsed;
101
+ }
102
+
103
+ // Type guard for API responses
104
+ interface ApiResponse<T> {
105
+ success: boolean;
106
+ data?: T;
107
+ error?: string;
108
+ }
109
+
110
+ function parseResponseSafe<T>(
111
+ response: ApiResponse<T>,
112
+ guard: (data: unknown) => data is T
113
+ ): T {
114
+ if (!response.success || !response.data) {
115
+ throw new Error(response.error ?? 'Request failed');
116
+ }
117
+
118
+ if (!guard(response.data)) {
119
+ throw new Error('Invalid response shape');
120
+ }
121
+
122
+ return response.data;
123
+ }
124
+
125
+ // Safe array access with validation
126
+ async function getFirstUserSafe(): Promise<User> {
127
+ const users = await fetchUsers();
128
+
129
+ if (users.length === 0) {
130
+ throw new Error('No users found');
131
+ }
132
+
133
+ return users[0]; // Now guaranteed to exist
134
+ }
135
+
136
+ // Typed catch clause
137
+ async function fetchDataSafe() {
138
+ try {
139
+ return await fetch('/api/data');
140
+ } catch (error) {
141
+ if (error instanceof Error) {
142
+ console.log(error.message);
143
+ } else {
144
+ console.log('Unknown error:', error);
145
+ }
146
+ }
147
+ }
148
+
149
+ // Proper generic constraints
150
+ interface Identifiable {
151
+ id: string;
152
+ }
153
+
154
+ function wrapSafe<T extends Identifiable>(value: T): { wrapped: T; id: string } {
155
+ return { wrapped: value, id: value.id };
156
+ }
157
+
158
+ // Discriminated unions for exhaustive checking
159
+ type Result<T> =
160
+ | { success: true; data: T }
161
+ | { success: false; error: string };
162
+
163
+ function handleResult<T>(result: Result<T>): T {
164
+ if (result.success) {
165
+ return result.data;
166
+ }
167
+ throw new Error(result.error);
168
+ }
169
+
170
+ // Branded types for semantic safety
171
+ type UserId = string & { readonly __brand: 'UserId' };
172
+ type OrderId = string & { readonly __brand: 'OrderId' };
173
+
174
+ function createUserId(id: string): UserId {
175
+ return id as UserId;
176
+ }
177
+
178
+ function getUserById(id: UserId): Promise<User> {
179
+ // Can only be called with UserId, not OrderId
180
+ return fetch(`/api/users/${id}`).then((r) => r.json());
181
+ }
@@ -0,0 +1,218 @@
1
+ # Angular Framework Patterns
2
+
3
+ Security and quality patterns specific to Angular applications.
4
+
5
+ ---
6
+
7
+ ## Security Patterns
8
+
9
+ ### XSS Prevention
10
+
11
+ | Pattern | Risk | Detection |
12
+ |---------|------|-----------|
13
+ | `innerHTML` binding | High | `[innerHTML]="userInput"` without sanitization |
14
+ | `bypassSecurityTrust*` | Critical | Bypassing Angular sanitizer |
15
+ | Template injection | High | Dynamic template construction |
16
+
17
+ **Safe Pattern:**
18
+ ```typescript
19
+ // BAD
20
+ @Component({
21
+ template: `<div [innerHTML]="userComment"></div>`
22
+ })
23
+
24
+ // GOOD - Angular sanitizes by default for interpolation
25
+ @Component({
26
+ template: `<div>{{userComment}}</div>`
27
+ })
28
+
29
+ // If HTML is needed, sanitize explicitly
30
+ import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
31
+
32
+ @Component({
33
+ template: `<div [innerHTML]="sanitizedContent"></div>`
34
+ })
35
+ export class CommentComponent {
36
+ sanitizedContent: SafeHtml;
37
+
38
+ constructor(private sanitizer: DomSanitizer) {}
39
+
40
+ setContent(html: string) {
41
+ // Only use when HTML is from trusted source
42
+ this.sanitizedContent = this.sanitizer.sanitize(SecurityContext.HTML, html);
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### URL Safety
48
+
49
+ | Pattern | Risk | Detection |
50
+ |---------|------|-----------|
51
+ | Dynamic href | High | `[href]="userUrl"` |
52
+ | Router navigate with user input | Medium | `router.navigate([userInput])` |
53
+
54
+ **Safe Pattern:**
55
+ ```typescript
56
+ // Validate URLs before using
57
+ validateUrl(url: string): boolean {
58
+ try {
59
+ const parsed = new URL(url);
60
+ return ['http:', 'https:'].includes(parsed.protocol);
61
+ } catch {
62
+ return false;
63
+ }
64
+ }
65
+ ```
66
+
67
+ ### HTTP Security
68
+
69
+ ```typescript
70
+ // Always use HttpClient (includes XSRF protection)
71
+ import { HttpClient } from '@angular/common/http';
72
+
73
+ // Configure XSRF
74
+ @NgModule({
75
+ imports: [
76
+ HttpClientModule,
77
+ HttpClientXsrfModule.withOptions({
78
+ cookieName: 'XSRF-TOKEN',
79
+ headerName: 'X-XSRF-TOKEN'
80
+ })
81
+ ]
82
+ })
83
+ ```
84
+
85
+ ---
86
+
87
+ ## Type Safety Patterns
88
+
89
+ ### Component Inputs/Outputs
90
+
91
+ ```typescript
92
+ // BAD
93
+ @Input() data: any;
94
+ @Output() changed = new EventEmitter<any>();
95
+
96
+ // GOOD
97
+ interface UserData {
98
+ id: string;
99
+ name: string;
100
+ }
101
+
102
+ @Input() data!: UserData;
103
+ @Output() changed = new EventEmitter<UserData>();
104
+ ```
105
+
106
+ ### Observable Typing
107
+
108
+ ```typescript
109
+ // BAD
110
+ users$: Observable<any>;
111
+
112
+ // GOOD
113
+ users$: Observable<User[]>;
114
+
115
+ // With error handling
116
+ users$ = this.http.get<User[]>('/api/users').pipe(
117
+ catchError(error => {
118
+ console.error('Failed to fetch users:', error);
119
+ return of([]);
120
+ })
121
+ );
122
+ ```
123
+
124
+ ### Form Typing
125
+
126
+ ```typescript
127
+ // BAD
128
+ form = new FormGroup({
129
+ name: new FormControl(''),
130
+ email: new FormControl('')
131
+ });
132
+
133
+ // GOOD - Typed forms (Angular 14+)
134
+ interface UserForm {
135
+ name: FormControl<string>;
136
+ email: FormControl<string>;
137
+ }
138
+
139
+ form = new FormGroup<UserForm>({
140
+ name: new FormControl('', { nonNullable: true }),
141
+ email: new FormControl('', { nonNullable: true })
142
+ });
143
+ ```
144
+
145
+ ---
146
+
147
+ ## Linting Patterns
148
+
149
+ ### Component Size
150
+
151
+ | Metric | Threshold | Action |
152
+ |--------|-----------|--------|
153
+ | Template lines | >100 | Split to sub-components |
154
+ | Component methods | >10 | Extract to service |
155
+ | Constructor params | >5 | Review dependencies |
156
+
157
+ ### Subscription Management
158
+
159
+ ```typescript
160
+ // BAD - Memory leak
161
+ ngOnInit() {
162
+ this.service.getData().subscribe(data => this.data = data);
163
+ }
164
+
165
+ // GOOD - Automatic cleanup
166
+ data$ = this.service.getData();
167
+ // Use async pipe in template: {{ data$ | async }}
168
+
169
+ // Or manual cleanup
170
+ private destroy$ = new Subject<void>();
171
+
172
+ ngOnInit() {
173
+ this.service.getData()
174
+ .pipe(takeUntil(this.destroy$))
175
+ .subscribe(data => this.data = data);
176
+ }
177
+
178
+ ngOnDestroy() {
179
+ this.destroy$.next();
180
+ this.destroy$.complete();
181
+ }
182
+ ```
183
+
184
+ ---
185
+
186
+ ## Coding Standards
187
+
188
+ ### Naming Conventions
189
+
190
+ | Element | Convention | Example |
191
+ |---------|------------|---------|
192
+ | Component | PascalCase + Component suffix | `UserProfileComponent` |
193
+ | Service | PascalCase + Service suffix | `AuthService` |
194
+ | Directive | PascalCase + Directive suffix | `HighlightDirective` |
195
+ | Pipe | PascalCase + Pipe suffix | `CurrencyPipe` |
196
+
197
+ ### File Organization
198
+
199
+ ```
200
+ feature/
201
+ feature.component.ts
202
+ feature.component.html
203
+ feature.component.scss
204
+ feature.component.spec.ts
205
+ feature.module.ts
206
+ feature-routing.module.ts
207
+ services/
208
+ models/
209
+ ```
210
+
211
+ ---
212
+
213
+ ## What to Skip
214
+
215
+ - Angular CLI generated code
216
+ - Zone.js related patterns
217
+ - Test bed setup boilerplate
218
+ - Module declarations