@el-j/magic-helix-core 4.0.0-beta.2 → 4.0.0-beta.4

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 (165) hide show
  1. package/dist/index-B88j4AyE.js +13 -0
  2. package/dist/index-B88j4AyE.js.map +1 -0
  3. package/dist/index-CY-pQbuu.cjs +2 -0
  4. package/dist/index-CY-pQbuu.cjs.map +1 -0
  5. package/dist/index.cjs +75 -1
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.ts +0 -1
  8. package/dist/index.mjs +2214 -51
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/pattern-combiner.d.ts +1 -1
  11. package/dist/plugin-loader.d.ts +2 -1
  12. package/package.json +4 -4
  13. package/dist/BasePlugin-6wv0hYJ9.js +0 -98
  14. package/dist/BasePlugin-6wv0hYJ9.js.map +0 -1
  15. package/dist/BasePlugin-odQJAKA-.cjs +0 -2
  16. package/dist/BasePlugin-odQJAKA-.cjs.map +0 -1
  17. package/dist/builtin-plugins/base/BasePlugin.d.ts +0 -69
  18. package/dist/builtin-plugins/cpp/index.d.ts +0 -46
  19. package/dist/builtin-plugins/csharp/index.d.ts +0 -20
  20. package/dist/builtin-plugins/go/index.d.ts +0 -23
  21. package/dist/builtin-plugins/index.d.ts +0 -16
  22. package/dist/builtin-plugins/java/index.d.ts +0 -22
  23. package/dist/builtin-plugins/nodejs/index.d.ts +0 -44
  24. package/dist/builtin-plugins/php/index.d.ts +0 -20
  25. package/dist/builtin-plugins/python/index.d.ts +0 -27
  26. package/dist/builtin-plugins/ruby/index.d.ts +0 -20
  27. package/dist/builtin-plugins/rust/index.d.ts +0 -53
  28. package/dist/builtin-plugins/swift/index.d.ts +0 -22
  29. package/dist/default_templates/angular/angular-core.md +0 -19
  30. package/dist/default_templates/architecture/codeowners.md +0 -123
  31. package/dist/default_templates/architecture/monorepo.md +0 -146
  32. package/dist/default_templates/architecture/nx.md +0 -122
  33. package/dist/default_templates/architecture/turborepo.md +0 -114
  34. package/dist/default_templates/ci/github-actions.md +0 -268
  35. package/dist/default_templates/ci/gitlab-ci.md +0 -330
  36. package/dist/default_templates/containers/docker-multistage.md +0 -120
  37. package/dist/default_templates/containers/kubernetes-deploy.md +0 -210
  38. package/dist/default_templates/devops/docker-compose.md +0 -111
  39. package/dist/default_templates/devops/docker-dockerfile.md +0 -94
  40. package/dist/default_templates/devops/github-actions.md +0 -160
  41. package/dist/default_templates/devops/gitlab-ci.md +0 -210
  42. package/dist/default_templates/dotnet/framework-aspnetcore.md +0 -205
  43. package/dist/default_templates/dotnet/framework-blazor.md +0 -271
  44. package/dist/default_templates/dotnet/lang-csharp.md +0 -162
  45. package/dist/default_templates/generic/lang-typescript.md +0 -57
  46. package/dist/default_templates/generic/state-redux.md +0 -21
  47. package/dist/default_templates/generic/state-rxjs.md +0 -6
  48. package/dist/default_templates/generic/style-mui.md +0 -23
  49. package/dist/default_templates/generic/style-tailwind.md +0 -76
  50. package/dist/default_templates/generic/test-cypress.md +0 -21
  51. package/dist/default_templates/generic/test-jest.md +0 -20
  52. package/dist/default_templates/generic/test-playwright.md +0 -21
  53. package/dist/default_templates/generic/test-vitest.md +0 -131
  54. package/dist/default_templates/go/lang-go.md +0 -571
  55. package/dist/default_templates/java/build-gradle.md +0 -102
  56. package/dist/default_templates/java/build-maven.md +0 -86
  57. package/dist/default_templates/java/framework-spring-boot.md +0 -179
  58. package/dist/default_templates/java/lang-java.md +0 -78
  59. package/dist/default_templates/java/lang-kotlin.md +0 -88
  60. package/dist/default_templates/meta/magic-helix-meta.md +0 -213
  61. package/dist/default_templates/meta/meta-debug.md +0 -459
  62. package/dist/default_templates/meta/meta-implement.md +0 -450
  63. package/dist/default_templates/meta/meta-roadmap.md +0 -265
  64. package/dist/default_templates/nestjs/nestjs-core.md +0 -7
  65. package/dist/default_templates/patterns/architecture/clean-architecture.md +0 -469
  66. package/dist/default_templates/patterns/architecture/dependency-injection.md +0 -517
  67. package/dist/default_templates/patterns/architecture/domain-driven-design.md +0 -621
  68. package/dist/default_templates/patterns/architecture/layered-architecture.md +0 -382
  69. package/dist/default_templates/patterns/architecture/repository-pattern.md +0 -408
  70. package/dist/default_templates/patterns/domain-expertise/nextjs-rules.md +0 -115
  71. package/dist/default_templates/patterns/domain-expertise/react-patterns.md +0 -181
  72. package/dist/default_templates/patterns/domain-expertise/server-components.md +0 -212
  73. package/dist/default_templates/patterns/domain-expertise/shadcn-ui.md +0 -52
  74. package/dist/default_templates/patterns/domain-expertise/tailwind-patterns.md +0 -52
  75. package/dist/default_templates/patterns/environment/container-awareness.md +0 -17
  76. package/dist/default_templates/patterns/environment/ide-features.md +0 -17
  77. package/dist/default_templates/patterns/environment/os-commands.md +0 -17
  78. package/dist/default_templates/patterns/organization/heading-hierarchy.md +0 -103
  79. package/dist/default_templates/patterns/organization/sequential-workflows.md +0 -102
  80. package/dist/default_templates/patterns/organization/xml-rule-groups.md +0 -64
  81. package/dist/default_templates/patterns/reasoning/agent-loop.md +0 -151
  82. package/dist/default_templates/patterns/reasoning/confirmation-gates.md +0 -141
  83. package/dist/default_templates/patterns/reasoning/dependency-analysis.md +0 -132
  84. package/dist/default_templates/patterns/reasoning/one-tool-per-iteration.md +0 -152
  85. package/dist/default_templates/patterns/reasoning/preview-before-action.md +0 -194
  86. package/dist/default_templates/patterns/reasoning/reflection-checkpoints.md +0 -166
  87. package/dist/default_templates/patterns/reasoning/result-verification.md +0 -157
  88. package/dist/default_templates/patterns/reasoning/subtask-breakdown.md +0 -131
  89. package/dist/default_templates/patterns/reasoning/thinking-tags.md +0 -100
  90. package/dist/default_templates/patterns/role-definition/capability-declarations.md +0 -72
  91. package/dist/default_templates/patterns/role-definition/expert-identity.md +0 -45
  92. package/dist/default_templates/patterns/role-definition/scope-boundaries.md +0 -61
  93. package/dist/default_templates/patterns/safety/code-safety-rules.md +0 -17
  94. package/dist/default_templates/patterns/safety/credential-handling.md +0 -17
  95. package/dist/default_templates/patterns/safety/destructive-warnings.md +0 -17
  96. package/dist/default_templates/patterns/safety/refusal-messages.md +0 -17
  97. package/dist/default_templates/patterns/tone/adaptive-tone.md +0 -17
  98. package/dist/default_templates/patterns/tone/concise-communication.md +0 -17
  99. package/dist/default_templates/patterns/tone/forbidden-phrases.md +0 -17
  100. package/dist/default_templates/patterns/tool-guidelines/function-schemas.md +0 -143
  101. package/dist/default_templates/patterns/tool-guidelines/parameter-examples.md +0 -137
  102. package/dist/default_templates/patterns/tool-guidelines/usage-policies.md +0 -105
  103. package/dist/default_templates/php/framework-laravel.md +0 -112
  104. package/dist/default_templates/php/lang-php.md +0 -94
  105. package/dist/default_templates/python/lang-python.md +0 -508
  106. package/dist/default_templates/react/react-core.md +0 -677
  107. package/dist/default_templates/react/react-zustand.md +0 -7
  108. package/dist/default_templates/ruby/framework-rails.md +0 -309
  109. package/dist/default_templates/ruby/framework-sinatra.md +0 -227
  110. package/dist/default_templates/ruby/lang-ruby.md +0 -216
  111. package/dist/default_templates/rust/lang-rust.md +0 -89
  112. package/dist/default_templates/swift/framework-vapor.md +0 -352
  113. package/dist/default_templates/swift/lang-swift.md +0 -291
  114. package/dist/default_templates/vue/style-primevue.md +0 -6
  115. package/dist/default_templates/vue/style-quasar.md +0 -22
  116. package/dist/default_templates/vue/vue-core.md +0 -108
  117. package/dist/default_templates/vue/vue-pinia.md +0 -5
  118. package/dist/index-0GK4RlUx.js +0 -1748
  119. package/dist/index-0GK4RlUx.js.map +0 -1
  120. package/dist/index-AkVwRl-r.js +0 -92
  121. package/dist/index-AkVwRl-r.js.map +0 -1
  122. package/dist/index-B6BeG1yT.cjs +0 -68
  123. package/dist/index-B6BeG1yT.cjs.map +0 -1
  124. package/dist/index-B8pyjKdF.js +0 -94
  125. package/dist/index-B8pyjKdF.js.map +0 -1
  126. package/dist/index-BQ6v041y.js +0 -13
  127. package/dist/index-BQ6v041y.js.map +0 -1
  128. package/dist/index-Baxb1vI_.js +0 -210
  129. package/dist/index-Baxb1vI_.js.map +0 -1
  130. package/dist/index-Bg8DD8ku.js +0 -216
  131. package/dist/index-Bg8DD8ku.js.map +0 -1
  132. package/dist/index-BqTqxCpG.cjs +0 -89
  133. package/dist/index-BqTqxCpG.cjs.map +0 -1
  134. package/dist/index-Bv4Q1Pr7.cjs +0 -33
  135. package/dist/index-Bv4Q1Pr7.cjs.map +0 -1
  136. package/dist/index-CN8J45Nc.cjs +0 -24
  137. package/dist/index-CN8J45Nc.cjs.map +0 -1
  138. package/dist/index-CPbv2Od1.js +0 -62
  139. package/dist/index-CPbv2Od1.js.map +0 -1
  140. package/dist/index-Cf-MC6Al.js +0 -63
  141. package/dist/index-Cf-MC6Al.js.map +0 -1
  142. package/dist/index-DDPXXXDy.cjs +0 -19
  143. package/dist/index-DDPXXXDy.cjs.map +0 -1
  144. package/dist/index-DO30AzDe.cjs +0 -19
  145. package/dist/index-DO30AzDe.cjs.map +0 -1
  146. package/dist/index-DkvW5yBY.js +0 -2249
  147. package/dist/index-DkvW5yBY.js.map +0 -1
  148. package/dist/index-Dn1ehjIj.cjs +0 -80
  149. package/dist/index-Dn1ehjIj.cjs.map +0 -1
  150. package/dist/index-DqHvgoXJ.cjs +0 -19
  151. package/dist/index-DqHvgoXJ.cjs.map +0 -1
  152. package/dist/index-K39pdw94.cjs +0 -31
  153. package/dist/index-K39pdw94.cjs.map +0 -1
  154. package/dist/index-OT2XAJkc.js +0 -117
  155. package/dist/index-OT2XAJkc.js.map +0 -1
  156. package/dist/index-TPAX4XKg.cjs +0 -30
  157. package/dist/index-TPAX4XKg.cjs.map +0 -1
  158. package/dist/index-WmVSB57y.js +0 -107
  159. package/dist/index-WmVSB57y.js.map +0 -1
  160. package/dist/index-mYXvc3Fs.js +0 -68
  161. package/dist/index-mYXvc3Fs.js.map +0 -1
  162. package/dist/index-nioXOg4m.cjs +0 -76
  163. package/dist/index-nioXOg4m.cjs.map +0 -1
  164. package/dist/index-okhY3fWD.cjs +0 -2
  165. package/dist/index-okhY3fWD.cjs.map +0 -1
@@ -1,677 +0,0 @@
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.
@@ -1,7 +0,0 @@
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 })`