@el-j/magic-helix-plugins 4.0.0-beta.1 → 4.0.0-beta.3

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 (120) hide show
  1. package/dist/architecture/codeowners.md +123 -0
  2. package/dist/architecture/monorepo.md +146 -0
  3. package/dist/architecture/nx.md +122 -0
  4. package/dist/architecture/turborepo.md +114 -0
  5. package/dist/ci/github-actions.md +268 -0
  6. package/dist/ci/gitlab-ci.md +330 -0
  7. package/dist/containers/docker-multistage.md +120 -0
  8. package/dist/containers/kubernetes-deploy.md +210 -0
  9. package/dist/cpp/index.cjs +79 -0
  10. package/dist/cpp/index.mjs +209 -0
  11. package/dist/csharp/index.cjs +2 -2
  12. package/dist/csharp/{index.js → index.mjs} +17 -11
  13. package/dist/csharp/templates/framework-aspnetcore.md +205 -0
  14. package/dist/csharp/templates/framework-blazor.md +271 -0
  15. package/dist/csharp/templates/lang-csharp.md +162 -0
  16. package/dist/devops/docker-compose.md +111 -0
  17. package/dist/devops/docker-dockerfile.md +94 -0
  18. package/dist/devops/github-actions.md +160 -0
  19. package/dist/devops/gitlab-ci.md +210 -0
  20. package/dist/generic/lang-typescript.md +57 -0
  21. package/dist/generic/state-redux.md +21 -0
  22. package/dist/generic/state-rxjs.md +6 -0
  23. package/dist/generic/style-mui.md +23 -0
  24. package/dist/generic/style-tailwind.md +76 -0
  25. package/dist/generic/test-cypress.md +21 -0
  26. package/dist/generic/test-jest.md +20 -0
  27. package/dist/generic/test-playwright.md +21 -0
  28. package/dist/generic/test-vitest.md +131 -0
  29. package/dist/go/index.cjs +3 -3
  30. package/dist/go/{index.js → index.mjs} +18 -15
  31. package/dist/go/templates/lang-go.md +571 -0
  32. package/dist/index.cjs +1 -1
  33. package/dist/index.mjs +24 -0
  34. package/dist/java/index.cjs +2 -2
  35. package/dist/java/{index.js → index.mjs} +25 -19
  36. package/dist/java/templates/build-gradle.md +102 -0
  37. package/dist/java/templates/build-maven.md +86 -0
  38. package/dist/java/templates/framework-spring-boot.md +179 -0
  39. package/dist/java/templates/lang-java.md +78 -0
  40. package/dist/java/templates/lang-kotlin.md +88 -0
  41. package/dist/meta/magic-helix-meta.md +213 -0
  42. package/dist/meta/meta-debug.md +459 -0
  43. package/dist/meta/meta-implement.md +450 -0
  44. package/dist/meta/meta-roadmap.md +265 -0
  45. package/dist/nodejs/templates/angular-core.md +19 -0
  46. package/dist/nodejs/templates/lang-typescript.md +57 -0
  47. package/dist/nodejs/templates/nestjs-core.md +7 -0
  48. package/dist/nodejs/templates/react-core.md +677 -0
  49. package/dist/nodejs/templates/react-zustand.md +7 -0
  50. package/dist/nodejs/templates/state-redux.md +21 -0
  51. package/dist/nodejs/templates/state-rxjs.md +6 -0
  52. package/dist/nodejs/templates/style-primevue.md +6 -0
  53. package/dist/nodejs/templates/style-quasar.md +22 -0
  54. package/dist/nodejs/templates/style-tailwind.md +76 -0
  55. package/dist/nodejs/templates/test-cypress.md +21 -0
  56. package/dist/nodejs/templates/test-jest.md +20 -0
  57. package/dist/nodejs/templates/test-playwright.md +21 -0
  58. package/dist/nodejs/templates/test-vitest.md +131 -0
  59. package/dist/nodejs/templates/vue-core.md +108 -0
  60. package/dist/nodejs/templates/vue-pinia.md +5 -0
  61. package/dist/patterns/architecture/clean-architecture.md +469 -0
  62. package/dist/patterns/architecture/dependency-injection.md +517 -0
  63. package/dist/patterns/architecture/domain-driven-design.md +621 -0
  64. package/dist/patterns/architecture/layered-architecture.md +382 -0
  65. package/dist/patterns/architecture/repository-pattern.md +408 -0
  66. package/dist/patterns/domain-expertise/nextjs-rules.md +115 -0
  67. package/dist/patterns/domain-expertise/react-patterns.md +181 -0
  68. package/dist/patterns/domain-expertise/server-components.md +212 -0
  69. package/dist/patterns/domain-expertise/shadcn-ui.md +52 -0
  70. package/dist/patterns/domain-expertise/tailwind-patterns.md +52 -0
  71. package/dist/patterns/environment/container-awareness.md +17 -0
  72. package/dist/patterns/environment/ide-features.md +17 -0
  73. package/dist/patterns/environment/os-commands.md +17 -0
  74. package/dist/patterns/organization/heading-hierarchy.md +103 -0
  75. package/dist/patterns/organization/sequential-workflows.md +102 -0
  76. package/dist/patterns/organization/xml-rule-groups.md +64 -0
  77. package/dist/patterns/reasoning/agent-loop.md +151 -0
  78. package/dist/patterns/reasoning/confirmation-gates.md +141 -0
  79. package/dist/patterns/reasoning/dependency-analysis.md +132 -0
  80. package/dist/patterns/reasoning/one-tool-per-iteration.md +152 -0
  81. package/dist/patterns/reasoning/preview-before-action.md +194 -0
  82. package/dist/patterns/reasoning/reflection-checkpoints.md +166 -0
  83. package/dist/patterns/reasoning/result-verification.md +157 -0
  84. package/dist/patterns/reasoning/subtask-breakdown.md +131 -0
  85. package/dist/patterns/reasoning/thinking-tags.md +100 -0
  86. package/dist/patterns/role-definition/capability-declarations.md +72 -0
  87. package/dist/patterns/role-definition/expert-identity.md +45 -0
  88. package/dist/patterns/role-definition/scope-boundaries.md +61 -0
  89. package/dist/patterns/safety/code-safety-rules.md +17 -0
  90. package/dist/patterns/safety/credential-handling.md +17 -0
  91. package/dist/patterns/safety/destructive-warnings.md +17 -0
  92. package/dist/patterns/safety/refusal-messages.md +17 -0
  93. package/dist/patterns/tone/adaptive-tone.md +17 -0
  94. package/dist/patterns/tone/concise-communication.md +17 -0
  95. package/dist/patterns/tone/forbidden-phrases.md +17 -0
  96. package/dist/patterns/tool-guidelines/function-schemas.md +143 -0
  97. package/dist/patterns/tool-guidelines/parameter-examples.md +137 -0
  98. package/dist/patterns/tool-guidelines/usage-policies.md +105 -0
  99. package/dist/php/index.cjs +2 -2
  100. package/dist/php/{index.js → index.mjs} +12 -6
  101. package/dist/php/templates/framework-laravel.md +112 -0
  102. package/dist/php/templates/lang-php.md +94 -0
  103. package/dist/python/index.cjs +4 -4
  104. package/dist/python/{index.js → index.mjs} +10 -7
  105. package/dist/python/templates/lang-python.md +508 -0
  106. package/dist/ruby/index.cjs +2 -2
  107. package/dist/ruby/{index.js → index.mjs} +16 -10
  108. package/dist/ruby/templates/framework-rails.md +309 -0
  109. package/dist/ruby/templates/framework-sinatra.md +227 -0
  110. package/dist/ruby/templates/lang-ruby.md +216 -0
  111. package/dist/rust/index.cjs +3 -3
  112. package/dist/rust/{index.js → index.mjs} +24 -18
  113. package/dist/rust/templates/lang-rust.md +89 -0
  114. package/dist/swift/index.cjs +32 -0
  115. package/dist/swift/index.mjs +112 -0
  116. package/dist/swift/templates/framework-vapor.md +352 -0
  117. package/dist/swift/templates/lang-swift.md +291 -0
  118. package/package.json +31 -21
  119. package/dist/index.js +0 -20
  120. /package/dist/nodejs/{index.js → index.mjs} +0 -0
@@ -0,0 +1,677 @@
1
+ # Framework: React
2
+
3
+ ## Modern React: Functional, Composable, Framework-Agnostic
4
+
5
+ **PREFER** functional programming over classes. Use plain objects, pure functions, and React hooks.
6
+
7
+ ## Architecture: Separation of Concerns
8
+
9
+ **Components should be logic-free presentation layers. Business logic lives in framework-free services.**
10
+
11
+ ### Layer Structure
12
+
13
+ ```
14
+ src/
15
+ ├── components/ # Presentation layer (React-specific, logic-free)
16
+ │ ├── UserProfile.tsx # UI only: rendering, event handlers → call hooks
17
+ │ └── OrderList.tsx
18
+
19
+ ├── hooks/ # React bridge layer (connects React to business logic)
20
+ │ ├── useUser.ts # Wraps services in React state/effects
21
+ │ └── useOrders.ts
22
+
23
+ ├── services/ # Business logic layer (framework-free, pure functions)
24
+ │ ├── user.ts # Pure functions, no React dependencies
25
+ │ └── order.ts
26
+
27
+ └── models/ # Domain models (types + pure functions)
28
+ ├── user.ts # Plain types + validators
29
+ └── order.ts
30
+ ```
31
+
32
+ ## Modern Patterns
33
+
34
+ ### ✅ DO: Use Functional Components + Hooks
35
+
36
+ ```tsx
37
+ // ✅ Modern: Functional component
38
+ export function UserProfile({ userId }: { userId: string }) {
39
+ const { user, loading } = useUser(userId);
40
+
41
+ if (loading) return <Spinner />;
42
+ return <div>{user.name}</div>;
43
+ }
44
+ ```
45
+
46
+ ### ❌ DON'T: Use Class Components
47
+
48
+ ```tsx
49
+ // ❌ Outdated: Class component
50
+ class UserProfile extends React.Component {
51
+ // Don't use classes in modern React
52
+ }
53
+ ```
54
+
55
+ ### ✅ DO: Use Plain Objects and Pure Functions
56
+
57
+ ```tsx
58
+ // ✅ Modern: Plain type + pure functions
59
+ export type User = {
60
+ id: string;
61
+ name: string;
62
+ email: string;
63
+ };
64
+
65
+ export function validateUser(user: User): boolean {
66
+ return user.email.includes('@');
67
+ }
68
+ ```
69
+
70
+ ### ❌ DON'T: Use Classes for Models
71
+
72
+ ```tsx
73
+ // ❌ Outdated: Class-based model
74
+ class User {
75
+ constructor(public id: string, public name: string) {}
76
+ validate() { /* ... */ }
77
+ }
78
+ ```
79
+
80
+ ### Rule: Components are Logic-Free
81
+
82
+ **ALWAYS** keep components focused on rendering and user interactions. **NEVER** put business logic in components.
83
+
84
+ ```tsx
85
+ // ✅ Good: Logic-free component
86
+ export function UserProfile({ userId }: { userId: string }) {
87
+ const { user, loading, updateEmail } = useUser(userId);
88
+
89
+ if (loading) return <Spinner />;
90
+ if (!user) return <NotFound />;
91
+
92
+ return (
93
+ <div>
94
+ <h1>{user.name}</h1>
95
+ <EmailForm
96
+ currentEmail={user.email}
97
+ onSubmit={updateEmail} // Hook handles the logic
98
+ />
99
+ </div>
100
+ );
101
+ }
102
+
103
+ // ❌ Bad: Business logic in component
104
+ export function UserProfile({ userId }: { userId: string }) {
105
+ const [user, setUser] = useState<User | null>(null);
106
+
107
+ async function updateEmail(newEmail: string) {
108
+ // ❌ Validation logic in component
109
+ if (!newEmail.includes('@')) {
110
+ alert('Invalid email');
111
+ return;
112
+ }
113
+
114
+ // ❌ API call in component
115
+ const response = await fetch(`/api/users/${userId}`, {
116
+ method: 'PATCH',
117
+ body: JSON.stringify({ email: newEmail })
118
+ });
119
+
120
+ const updated = await response.json();
121
+ setUser(updated);
122
+ }
123
+
124
+ return (/* ... */);
125
+ }
126
+ ```
127
+
128
+ ### Rule: Hooks Wire React to Services
129
+
130
+ **ALWAYS** use custom hooks to bridge React state/effects with framework-free services.
131
+
132
+ ```tsx
133
+ // ✅ hooks/useUser.ts - React bridge
134
+ import { useState, useEffect } from 'react';
135
+ import * as userService from '@/services/userService';
136
+ import type { User } from '@/models/User';
137
+
138
+ export function useUser(userId: string) {
139
+ const [user, setUser] = useState<User | null>(null);
140
+ const [loading, setLoading] = useState(true);
141
+ const [error, setError] = useState<Error | null>(null);
142
+
143
+ useEffect(() => {
144
+ let cancelled = false;
145
+
146
+ async function loadUser() {
147
+ try {
148
+ setLoading(true);
149
+ const data = await userService.getUser(userId); // Pure function call
150
+ if (!cancelled) {
151
+ setUser(data);
152
+ }
153
+ } catch (err) {
154
+ if (!cancelled) {
155
+ setError(err as Error);
156
+ }
157
+ } finally {
158
+ if (!cancelled) {
159
+ setLoading(false);
160
+ }
161
+ }
162
+ }
163
+
164
+ loadUser();
165
+
166
+ return () => {
167
+ cancelled = true; // Cleanup
168
+ };
169
+ }, [userId]);
170
+
171
+ const updateEmail = async (newEmail: string) => {
172
+ const updated = await userService.updateUserEmail(userId, newEmail);
173
+ setUser(updated);
174
+ };
175
+
176
+ return { user, loading, error, updateEmail };
177
+ }
178
+ ```
179
+
180
+ ### Rule: Services are Framework-Free
181
+
182
+ **ALWAYS** write services as pure functions or plain objects. **NEVER** import React or use hooks in services.
183
+
184
+ ```typescript
185
+ // ✅ Modern: services/userService.ts - Pure functions (no class)
186
+ import type { User } from '@/models/User';
187
+ import { validateEmail } from '@/models/User';
188
+ import { apiClient } from '@/lib/apiClient';
189
+
190
+ export async function getUser(userId: string): Promise<User> {
191
+ const response = await apiClient.get(`/users/${userId}`);
192
+ return {
193
+ id: response.data.id,
194
+ name: response.data.name,
195
+ email: response.data.email,
196
+ createdAt: new Date(response.data.created_at),
197
+ };
198
+ }
199
+
200
+ export async function updateUserEmail(userId: string, newEmail: string): Promise<User> {
201
+ // Business logic: validation
202
+ if (!validateEmail(newEmail)) {
203
+ throw new Error('Invalid email format');
204
+ }
205
+
206
+ // API call
207
+ const response = await apiClient.patch(`/users/${userId}`, {
208
+ email: newEmail
209
+ });
210
+
211
+ return {
212
+ id: response.data.id,
213
+ name: response.data.name,
214
+ email: response.data.email,
215
+ createdAt: new Date(response.data.created_at),
216
+ };
217
+ }
218
+
219
+ export async function deleteUser(userId: string): Promise<void> {
220
+ await apiClient.delete(`/users/${userId}`);
221
+ }
222
+
223
+ // Alternative: Object grouping for organization
224
+ export const userService = {
225
+ getUser,
226
+ updateUserEmail,
227
+ deleteUser,
228
+ } as const;
229
+ ```
230
+
231
+ **Benefits:**
232
+ - Easy to test (no React mocking needed)
233
+ - Reusable in Node.js scripts, CLI tools, React Native
234
+ - Can swap React for Vue/Svelte/Angular without rewriting logic
235
+ - Tree-shakeable (only import functions you use)
236
+
237
+ ### Rule: Domain Models are Plain Objects + Pure Functions
238
+
239
+ **PREFER** plain TypeScript types with pure utility functions over classes.
240
+
241
+ ```typescript
242
+ // ✅ Modern: models/User.ts - Type + pure functions
243
+ export type User = {
244
+ readonly id: string;
245
+ name: string;
246
+ email: string;
247
+ createdAt: Date;
248
+ };
249
+
250
+ // Pure utility functions
251
+ export function createUser(data: { name: string; email: string }): User {
252
+ if (!validateEmail(data.email)) {
253
+ throw new Error('Invalid email format');
254
+ }
255
+
256
+ return {
257
+ id: crypto.randomUUID(),
258
+ name: data.name,
259
+ email: data.email,
260
+ createdAt: new Date(),
261
+ };
262
+ }
263
+
264
+ export function isAdmin(user: User): boolean {
265
+ return user.email.endsWith('@company.com');
266
+ }
267
+
268
+ export function validateEmail(email: string): boolean {
269
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
270
+ }
271
+
272
+ // For complex transformations
273
+ export function userFromAPI(data: unknown): User {
274
+ // Add runtime validation with zod or similar
275
+ return {
276
+ id: data.id,
277
+ name: data.name,
278
+ email: data.email,
279
+ createdAt: new Date(data.created_at),
280
+ };
281
+ }
282
+ ```
283
+
284
+ ### Complete Example: Order Management
285
+
286
+ ```
287
+ src/
288
+ ├── components/
289
+ │ └── OrderList.tsx # UI only
290
+ ├── hooks/
291
+ │ └── useOrders.ts # React state bridge
292
+ ├── services/
293
+ │ └── orderService.ts # Business logic
294
+ └── models/
295
+ └── Order.ts # Domain model
296
+ ```
297
+
298
+ ```tsx
299
+ // components/OrderList.tsx - Presentation
300
+ export function OrderList() {
301
+ const { orders, loading, createOrder, cancelOrder } = useOrders();
302
+
303
+ if (loading) return <Spinner />;
304
+
305
+ return (
306
+ <div>
307
+ <button onClick={() => createOrder({ items: [...] })}>
308
+ New Order
309
+ </button>
310
+ {orders.map(order => (
311
+ <OrderCard
312
+ key={order.id}
313
+ order={order}
314
+ onCancel={() => cancelOrder(order.id)}
315
+ />
316
+ ))}
317
+ </div>
318
+ );
319
+ }
320
+
321
+ // hooks/useOrders.ts - React bridge
322
+ export function useOrders() {
323
+ const [orders, setOrders] = useState<Order[]>([]);
324
+ const [loading, setLoading] = useState(true);
325
+
326
+ useEffect(() => {
327
+ orderService.getOrders().then(setOrders).finally(() => setLoading(false));
328
+ }, []);
329
+
330
+ const createOrder = async (data: CreateOrderData) => {
331
+ const newOrder = await orderService.createOrder(data);
332
+ setOrders(prev => [...prev, newOrder]);
333
+ };
334
+
335
+ const cancelOrder = async (orderId: string) => {
336
+ await orderService.cancelOrder(orderId);
337
+ setOrders(prev => prev.filter(o => o.id !== orderId));
338
+ };
339
+
340
+ return { orders, loading, createOrder, cancelOrder };
341
+ }
342
+
343
+ // services/orderService.ts - Framework-free logic
344
+ class OrderService {
345
+ async getOrders(): Promise<Order[]> {
346
+ const response = await apiClient.get('/orders');
347
+ return response.data.map(Order.fromAPI);
348
+ }
349
+
350
+ async createOrder(data: CreateOrderData): Promise<Order> {
351
+ // Validation
352
+ if (data.items.length === 0) {
353
+ throw new Error('Order must have at least one item');
354
+ }
355
+
356
+ const response = await apiClient.post('/orders', data);
357
+ return Order.fromAPI(response.data);
358
+ }
359
+
360
+ async cancelOrder(orderId: string): Promise<void> {
361
+ await apiClient.post(`/orders/${orderId}/cancel`);
362
+ }
363
+ }
364
+
365
+ export const orderService = new OrderService();
366
+
367
+ // models/Order.ts - Domain model
368
+ export class Order {
369
+ constructor(
370
+ public readonly id: string,
371
+ public status: OrderStatus,
372
+ public items: OrderItem[],
373
+ public total: number
374
+ ) {}
375
+
376
+ static fromAPI(data: any): Order {
377
+ return new Order(
378
+ data.id,
379
+ data.status,
380
+ data.items.map(OrderItem.fromAPI),
381
+ data.total
382
+ );
383
+ }
384
+
385
+ canBeCancelled(): boolean {
386
+ return this.status === 'pending' || this.status === 'processing';
387
+ }
388
+ }
389
+ ```
390
+
391
+ ## React Best Practices
392
+
393
+ ### Modern React Patterns
394
+
395
+ - **ALWAYS** use Functional Components with Hooks (never classes)
396
+ - **ALWAYS** use plain objects and pure functions over classes for models/services
397
+ - **PREFER** function exports over default exports for better tree-shaking
398
+ - **PREFER** named exports: `export function Component()` over `export default`
399
+ - **HOOKS**: Use `useState` for simple state, `useReducer` for complex state logic
400
+ - **EFFECTS**: `useEffect` dependencies must be complete. Use `eslint-plugin-react-hooks`
401
+ - **MEMOIZATION**: Use `useCallback` for functions passed as props, `useMemo` for expensive calculations
402
+ - **NAMING**: Components are `PascalCase.tsx`, utilities are `camelCase.ts`
403
+
404
+ ### Recommended Modern Libraries
405
+
406
+ **State Management:**
407
+ - ✅ Zustand (simple, functional, no boilerplate)
408
+ - ✅ Jotai (atomic state, React-like)
409
+ - ✅ TanStack Query (server state, caching)
410
+ - ⚠️ Redux Toolkit (if you need Redux, use RTK not classic Redux)
411
+ - ❌ Classic Redux (too much boilerplate)
412
+
413
+ **Forms:**
414
+ - ✅ React Hook Form (performant, minimal re-renders)
415
+ - ✅ Zod (schema validation, type-safe)
416
+ - ❌ Formik (legacy, prefer React Hook Form)
417
+
418
+ **Data Fetching:**
419
+ - ✅ TanStack Query (React Query) - best for server state
420
+ - ✅ SWR (simple, from Vercel)
421
+ - ✅ Native `fetch` + custom hooks for simple cases
422
+
423
+ **Styling:**
424
+ - ✅ Tailwind CSS (utility-first, fast)
425
+ - ✅ CSS Modules (scoped, simple)
426
+ - ✅ Vanilla Extract (type-safe CSS-in-TS)
427
+ - ⚠️ Styled Components (runtime cost, slower)
428
+ - ⚠️ Emotion (runtime cost, slower)
429
+
430
+ **Type Safety:**
431
+ - ✅ TypeScript (essential)
432
+ - ✅ Zod (runtime validation + type inference)
433
+ - ✅ ts-pattern (pattern matching)
434
+
435
+ **Testing:**
436
+ - ✅ Vitest (modern, fast, Vite-compatible)
437
+ - ✅ Testing Library (user-centric testing)
438
+ - ⚠️ Jest (slower, but widely used)
439
+
440
+ ### Example: Modern Stack
441
+
442
+ ```tsx
443
+ // ✅ Modern React with TanStack Query + Zod
444
+ import { useQuery, useMutation } from '@tanstack/react-query';
445
+ import { z } from 'zod';
446
+
447
+ // Schema validation with Zod
448
+ const UserSchema = z.object({
449
+ id: z.string(),
450
+ name: z.string(),
451
+ email: z.string().email(),
452
+ });
453
+
454
+ type User = z.infer<typeof UserSchema>;
455
+
456
+ // Pure function for API call
457
+ async function fetchUser(userId: string): Promise<User> {
458
+ const res = await fetch(`/api/users/${userId}`);
459
+ const data = await res.json();
460
+ return UserSchema.parse(data); // Runtime validation
461
+ }
462
+
463
+ // Component using TanStack Query (no custom hook needed!)
464
+ export function UserProfile({ userId }: { userId: string }) {
465
+ const { data: user, isLoading } = useQuery({
466
+ queryKey: ['user', userId],
467
+ queryFn: () => fetchUser(userId),
468
+ });
469
+
470
+ if (isLoading) return <Spinner />;
471
+ if (!user) return <NotFound />;
472
+
473
+ return <div>{user.name}</div>;
474
+ }
475
+ ```
476
+
477
+ ### Example: Zustand for Client State
478
+
479
+ ```typescript
480
+ // stores/userStore.ts - Simple, functional state
481
+ import { create } from 'zustand';
482
+
483
+ type UserStore = {
484
+ currentUser: User | null;
485
+ setUser: (user: User) => void;
486
+ logout: () => void;
487
+ };
488
+
489
+ export const useUserStore = create<UserStore>((set) => ({
490
+ currentUser: null,
491
+ setUser: (user) => set({ currentUser: user }),
492
+ logout: () => set({ currentUser: null }),
493
+ }));
494
+
495
+ // Usage in component
496
+ export function Header() {
497
+ const currentUser = useUserStore((state) => state.currentUser);
498
+ const logout = useUserStore((state) => state.logout);
499
+
500
+ return <button onClick={logout}>{currentUser?.name}</button>;
501
+ }
502
+ ```
503
+
504
+ ## File Organization
505
+
506
+ ```
507
+ src/
508
+ ├── components/ # React components (presentation only)
509
+ │ ├── UserProfile/
510
+ │ │ ├── UserProfile.tsx
511
+ │ │ ├── UserProfile.test.tsx
512
+ │ │ └── index.ts
513
+ │ └── shared/ # Reusable UI components
514
+ │ ├── Button.tsx
515
+ │ └── Card.tsx
516
+
517
+ ├── hooks/ # Custom hooks (React bridge to services)
518
+ │ ├── useUser.ts
519
+ │ ├── useOrders.ts
520
+ │ └── useAuth.ts
521
+
522
+ ├── services/ # Business logic (framework-free)
523
+ │ ├── userService.ts
524
+ │ ├── orderService.ts
525
+ │ └── authService.ts
526
+
527
+ ├── models/ # Domain models and types
528
+ │ ├── User.ts
529
+ │ ├── Order.ts
530
+ │ └── types.ts
531
+
532
+ ├── lib/ # Utilities (API client, formatters, etc.)
533
+ │ ├── apiClient.ts
534
+ │ └── validators.ts
535
+
536
+ └── App.tsx
537
+ ```
538
+
539
+ ## Testing Strategy
540
+
541
+ **ALWAYS write tests BEFORE implementation (Test-Driven Development).**
542
+
543
+ ### TDD Workflow
544
+
545
+ ```typescript
546
+ // 1. RED: Write failing test first
547
+ describe('UserService', () => {
548
+ it('should validate email format before API call', async () => {
549
+ await expect(
550
+ userService.updateUserEmail('1', 'invalid-email')
551
+ ).rejects.toThrow('Invalid email format');
552
+
553
+ expect(apiClient.patch).not.toHaveBeenCalled();
554
+ });
555
+ });
556
+
557
+ // 2. GREEN: Write minimal code to pass
558
+ export async function updateUserEmail(userId: string, email: string): Promise<User> {
559
+ if (!isValidEmail(email)) {
560
+ throw new Error('Invalid email format');
561
+ }
562
+ const response = await apiClient.patch(`/users/${userId}`, { email });
563
+ return response.data;
564
+ }
565
+
566
+ // 3. REFACTOR: Clean up while keeping tests green
567
+ ```
568
+
569
+ ### Test Layer Strategy
570
+
571
+ **Services (Framework-Free):** Test first, 90%+ coverage
572
+ ```typescript
573
+ // userService.test.ts - Write BEFORE userService.ts
574
+ import { userService } from './userService';
575
+ import { apiClient } from '@/lib/apiClient';
576
+
577
+ jest.mock('@/lib/apiClient');
578
+
579
+ describe('UserService - TDD', () => {
580
+ // Test validates business rules
581
+ test('validates email before updating', async () => {
582
+ await expect(
583
+ userService.updateUserEmail('1', 'invalid-email')
584
+ ).rejects.toThrow('Invalid email format');
585
+
586
+ expect(apiClient.patch).not.toHaveBeenCalled();
587
+ });
588
+
589
+ test('updates email when valid', async () => {
590
+ const mockUser = { id: '1', email: 'new@example.com' };
591
+ (apiClient.patch as jest.Mock).mockResolvedValue({ data: mockUser });
592
+
593
+ const result = await userService.updateUserEmail('1', 'new@example.com');
594
+
595
+ expect(apiClient.patch).toHaveBeenCalledWith('/users/1', {
596
+ email: 'new@example.com'
597
+ });
598
+ expect(result.email).toBe('new@example.com');
599
+ });
600
+ });
601
+ ```
602
+
603
+ **Hooks (React Integration):** Test React behavior
604
+ ```tsx
605
+ // useUser.test.ts
606
+ import { renderHook, waitFor } from '@testing-library/react';
607
+ import { useUser } from './useUser';
608
+ import { userService } from '@/services/userService';
609
+
610
+ jest.mock('@/services/userService');
611
+
612
+ test('loads user on mount', async () => {
613
+ const mockUser = { id: '1', name: 'John', email: 'john@example.com' };
614
+ (userService.getUser as jest.Mock).mockResolvedValue(mockUser);
615
+
616
+ const { result } = renderHook(() => useUser('1'));
617
+
618
+ expect(result.current.loading).toBe(true);
619
+
620
+ await waitFor(() => {
621
+ expect(result.current.loading).toBe(false);
622
+ expect(result.current.user).toEqual(mockUser);
623
+ });
624
+ });
625
+ ```
626
+
627
+ **Components (UI):** Test rendering and user interactions
628
+ ```tsx
629
+ // UserProfile.test.tsx
630
+ import { render, screen } from '@testing-library/react';
631
+ import { UserProfile } from './UserProfile';
632
+
633
+ // Mock the hook
634
+ jest.mock('@/hooks/useUser', () => ({
635
+ useUser: () => ({
636
+ user: { id: '1', name: 'John', email: 'john@example.com' },
637
+ loading: false,
638
+ updateEmail: jest.fn()
639
+ })
640
+ }));
641
+
642
+ test('renders user name', () => {
643
+ render(<UserProfile userId="1" />);
644
+ expect(screen.getByText('John')).toBeInTheDocument();
645
+ });
646
+ ```
647
+
648
+ ### Test Coverage Goals
649
+
650
+ - **Services/Business Logic**: 90%+ coverage (test-first, critical paths)
651
+ - **Hooks**: 80%+ coverage (state/effect integration)
652
+ - **Components**: 70%+ coverage (user interactions, conditional rendering)
653
+ - **Models/Utilities**: 100% coverage (pure functions, edge cases)
654
+
655
+ ## Architecture Summary
656
+
657
+ 1. **Components** = Presentation (UI rendering, event handlers)
658
+ - Import: hooks, UI libraries, CSS
659
+ - Export: JSX components
660
+ - No business logic, no API calls
661
+
662
+ 2. **Hooks** = React Bridge (state management, effects)
663
+ - Import: React hooks, services
664
+ - Export: State + actions
665
+ - Minimal logic (mostly wiring)
666
+
667
+ 3. **Services** = Business Logic (framework-free)
668
+ - Import: models, utilities, API client
669
+ - Export: Business operations
670
+ - No React dependencies
671
+
672
+ 4. **Models** = Domain Rules (types, validation, transformations)
673
+ - Import: nothing or minimal utilities
674
+ - Export: Classes, types, validators
675
+ - Pure TypeScript
676
+
677
+ **Reference:** See `patterns/architecture/clean-architecture.md` and `patterns/architecture/layered-architecture.md` for detailed architectural guidance.
@@ -0,0 +1,7 @@
1
+ # State: Zustand
2
+ - **ALWAYS** use Zustand for global state management.
3
+ - Define stores in `src/stores/` or `src/hooks/`.
4
+ - **PREFER** the `create(set => ({ ... }))` syntax.
5
+ - **ACTIONS** should be defined as methods inside the created store object.
6
+ - **NEVER** mutate state directly. Always use the `set` function.
7
+ - `set({ count: state.count + 1 })`
@@ -0,0 +1,21 @@
1
+ # State Management: Redux
2
+ - **ALWAYS** use Redux Toolkit (`@reduxjs/toolkit`) for modern Redux development.
3
+ - **ALWAYS** use `createSlice()` for reducer logic and actions.
4
+ - **ALWAYS** use `configureStore()` for store setup with good defaults.
5
+ - **ALWAYS** use TypeScript for type-safe Redux code.
6
+ - **ALWAYS** define action types as string constants or use `createAction()`.
7
+ - **ALWAYS** use `createAsyncThunk()` for async logic and API calls.
8
+ - **ALWAYS** normalize state shape for better performance and consistency.
9
+ - **ALWAYS** use selector functions with `createSelector()` for computed values.
10
+ - **ALWAYS** use `useSelector()` and `useDispatch()` hooks in React components.
11
+ - **ALWAYS** avoid direct state mutations - use immutable updates.
12
+ - **ALWAYS** use `redux-persist` for state persistence when needed.
13
+ - **ALWAYS** use `redux-saga` or `redux-thunk` for complex async flows.
14
+ - **ALWAYS** use `redux-devtools-extension` for debugging in development.
15
+ - **ALWAYS** write comprehensive tests for reducers and selectors.
16
+ - **ALWAYS** use `entityAdapter` from RTK for CRUD operations.
17
+ - **ALWAYS** handle loading and error states in async thunks.
18
+ - **ALWAYS** use `extraReducers` in `createSlice()` for async thunk handling.
19
+ - **ALWAYS** keep reducers pure and side-effect free.
20
+ - **ALWAYS** use meaningful action names and payload structures.
21
+ - **ALWAYS** document complex state transformations with comments.
@@ -0,0 +1,6 @@
1
+ # State: RxJS
2
+ - **ALWAYS** suffix Observable variables with a `$` (e.g., `users$`).
3
+ - **ALWAYS** use `BehaviorSubject` for state that needs to be "replayed" to new subscribers.
4
+ - **ALWAYS** `pipe()` operators. Do not use chained `.` operators.
5
+ - **NEVER** forget to `unsubscribe()`. Manage subscriptions in a central way, e.g., a `destroy$` Subject that completes `onComponentDestroy`.
6
+ - **PREFER** `switchMap` for handling new inner observables (like HTTP requests) and `mergeMap` for parallel operations.