autoworkflow 3.1.5 → 3.5.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 (123) hide show
  1. package/.claude/commands/analyze.md +19 -0
  2. package/.claude/commands/audit.md +26 -0
  3. package/.claude/commands/build.md +39 -0
  4. package/.claude/commands/commit.md +25 -0
  5. package/.claude/commands/fix.md +23 -0
  6. package/.claude/commands/plan.md +18 -0
  7. package/.claude/commands/suggest.md +23 -0
  8. package/.claude/commands/verify.md +18 -0
  9. package/.claude/hooks/post-bash-router.sh +20 -0
  10. package/.claude/hooks/post-commit.sh +140 -0
  11. package/.claude/hooks/pre-edit.sh +129 -0
  12. package/.claude/hooks/session-check.sh +79 -0
  13. package/.claude/settings.json +40 -6
  14. package/.claude/settings.local.json +3 -1
  15. package/.claude/skills/actix.md +337 -0
  16. package/.claude/skills/alembic.md +504 -0
  17. package/.claude/skills/angular.md +237 -0
  18. package/.claude/skills/api-design.md +187 -0
  19. package/.claude/skills/aspnet-core.md +377 -0
  20. package/.claude/skills/astro.md +245 -0
  21. package/.claude/skills/auth-clerk.md +327 -0
  22. package/.claude/skills/auth-firebase.md +367 -0
  23. package/.claude/skills/auth-nextauth.md +359 -0
  24. package/.claude/skills/auth-supabase.md +368 -0
  25. package/.claude/skills/axum.md +386 -0
  26. package/.claude/skills/blazor.md +456 -0
  27. package/.claude/skills/chi.md +348 -0
  28. package/.claude/skills/code-review.md +133 -0
  29. package/.claude/skills/csharp.md +296 -0
  30. package/.claude/skills/css-modules.md +325 -0
  31. package/.claude/skills/cypress.md +343 -0
  32. package/.claude/skills/debugging.md +133 -0
  33. package/.claude/skills/diesel.md +392 -0
  34. package/.claude/skills/django.md +301 -0
  35. package/.claude/skills/docker.md +319 -0
  36. package/.claude/skills/doctrine.md +473 -0
  37. package/.claude/skills/documentation.md +182 -0
  38. package/.claude/skills/dotnet.md +409 -0
  39. package/.claude/skills/drizzle.md +293 -0
  40. package/.claude/skills/echo.md +321 -0
  41. package/.claude/skills/eloquent.md +256 -0
  42. package/.claude/skills/emotion.md +426 -0
  43. package/.claude/skills/entity-framework.md +370 -0
  44. package/.claude/skills/express.md +316 -0
  45. package/.claude/skills/fastapi.md +329 -0
  46. package/.claude/skills/fastify.md +299 -0
  47. package/.claude/skills/fiber.md +315 -0
  48. package/.claude/skills/flask.md +322 -0
  49. package/.claude/skills/gin.md +342 -0
  50. package/.claude/skills/git.md +116 -0
  51. package/.claude/skills/github-actions.md +353 -0
  52. package/.claude/skills/go.md +377 -0
  53. package/.claude/skills/gorm.md +409 -0
  54. package/.claude/skills/graphql.md +478 -0
  55. package/.claude/skills/hibernate.md +379 -0
  56. package/.claude/skills/hono.md +306 -0
  57. package/.claude/skills/java.md +400 -0
  58. package/.claude/skills/jest.md +313 -0
  59. package/.claude/skills/jpa.md +282 -0
  60. package/.claude/skills/kotlin.md +347 -0
  61. package/.claude/skills/kubernetes.md +363 -0
  62. package/.claude/skills/laravel.md +414 -0
  63. package/.claude/skills/mcp-browser.md +320 -0
  64. package/.claude/skills/mcp-database.md +219 -0
  65. package/.claude/skills/mcp-fetch.md +241 -0
  66. package/.claude/skills/mcp-filesystem.md +204 -0
  67. package/.claude/skills/mcp-github.md +217 -0
  68. package/.claude/skills/mcp-memory.md +240 -0
  69. package/.claude/skills/mcp-search.md +218 -0
  70. package/.claude/skills/mcp-slack.md +262 -0
  71. package/.claude/skills/micronaut.md +388 -0
  72. package/.claude/skills/mongodb.md +319 -0
  73. package/.claude/skills/mongoose.md +355 -0
  74. package/.claude/skills/mysql.md +281 -0
  75. package/.claude/skills/nestjs.md +335 -0
  76. package/.claude/skills/nextjs-app-router.md +260 -0
  77. package/.claude/skills/nextjs-pages.md +172 -0
  78. package/.claude/skills/nuxt.md +202 -0
  79. package/.claude/skills/openapi.md +489 -0
  80. package/.claude/skills/performance.md +199 -0
  81. package/.claude/skills/php.md +398 -0
  82. package/.claude/skills/playwright.md +371 -0
  83. package/.claude/skills/postgresql.md +257 -0
  84. package/.claude/skills/prisma.md +293 -0
  85. package/.claude/skills/pydantic.md +304 -0
  86. package/.claude/skills/pytest.md +313 -0
  87. package/.claude/skills/python.md +272 -0
  88. package/.claude/skills/quarkus.md +377 -0
  89. package/.claude/skills/react.md +230 -0
  90. package/.claude/skills/redis.md +391 -0
  91. package/.claude/skills/refactoring.md +143 -0
  92. package/.claude/skills/remix.md +246 -0
  93. package/.claude/skills/rest-api.md +490 -0
  94. package/.claude/skills/rocket.md +366 -0
  95. package/.claude/skills/rust.md +341 -0
  96. package/.claude/skills/sass.md +380 -0
  97. package/.claude/skills/sea-orm.md +382 -0
  98. package/.claude/skills/security.md +167 -0
  99. package/.claude/skills/sequelize.md +395 -0
  100. package/.claude/skills/spring-boot.md +416 -0
  101. package/.claude/skills/sqlalchemy.md +269 -0
  102. package/.claude/skills/sqlx-rust.md +408 -0
  103. package/.claude/skills/state-jotai.md +346 -0
  104. package/.claude/skills/state-mobx.md +353 -0
  105. package/.claude/skills/state-pinia.md +431 -0
  106. package/.claude/skills/state-redux.md +337 -0
  107. package/.claude/skills/state-tanstack-query.md +434 -0
  108. package/.claude/skills/state-zustand.md +340 -0
  109. package/.claude/skills/styled-components.md +403 -0
  110. package/.claude/skills/svelte.md +238 -0
  111. package/.claude/skills/sveltekit.md +207 -0
  112. package/.claude/skills/symfony.md +437 -0
  113. package/.claude/skills/tailwind.md +279 -0
  114. package/.claude/skills/terraform.md +394 -0
  115. package/.claude/skills/testing-library.md +371 -0
  116. package/.claude/skills/trpc.md +426 -0
  117. package/.claude/skills/typeorm.md +368 -0
  118. package/.claude/skills/vitest.md +330 -0
  119. package/.claude/skills/vue.md +202 -0
  120. package/.claude/skills/warp.md +365 -0
  121. package/README.md +135 -52
  122. package/package.json +1 -1
  123. package/system/triggers.md +152 -11
@@ -0,0 +1,377 @@
1
+ # Quarkus Skill
2
+
3
+ ## Project Setup
4
+ \`\`\`bash
5
+ # Create new project
6
+ quarkus create app com.example:myapp \\
7
+ --extension='resteasy-reactive-jackson,hibernate-orm-panache,jdbc-postgresql,smallrye-jwt'
8
+
9
+ # Run in dev mode (hot reload)
10
+ quarkus dev
11
+
12
+ # Build native executable
13
+ quarkus build --native
14
+ \`\`\`
15
+
16
+ ## REST Resource with JAX-RS
17
+ \`\`\`java
18
+ @Path("/api/v1/users")
19
+ @Produces(MediaType.APPLICATION_JSON)
20
+ @Consumes(MediaType.APPLICATION_JSON)
21
+ public class UserResource {
22
+
23
+ @Inject
24
+ UserService userService;
25
+
26
+ @GET
27
+ public List<UserResponse> listUsers(
28
+ @QueryParam("page") @DefaultValue("0") int page,
29
+ @QueryParam("size") @DefaultValue("20") int size) {
30
+ return userService.findAll(page, size);
31
+ }
32
+
33
+ @GET
34
+ @Path("/{id}")
35
+ public UserResponse getUser(@PathParam("id") String id) {
36
+ return userService.findById(id)
37
+ .orElseThrow(() -> new NotFoundException("User not found: " + id));
38
+ }
39
+
40
+ @POST
41
+ public Response createUser(@Valid CreateUserRequest request) {
42
+ UserResponse user = userService.create(request);
43
+ return Response.status(Response.Status.CREATED)
44
+ .entity(user)
45
+ .location(URI.create("/api/v1/users/" + user.id()))
46
+ .build();
47
+ }
48
+
49
+ @PUT
50
+ @Path("/{id}")
51
+ public UserResponse updateUser(
52
+ @PathParam("id") String id,
53
+ @Valid UpdateUserRequest request) {
54
+ return userService.update(id, request);
55
+ }
56
+
57
+ @DELETE
58
+ @Path("/{id}")
59
+ public Response deleteUser(@PathParam("id") String id) {
60
+ userService.delete(id);
61
+ return Response.noContent().build();
62
+ }
63
+ }
64
+ \`\`\`
65
+
66
+ ## Reactive REST Resource
67
+ \`\`\`java
68
+ @Path("/api/v1/users")
69
+ @Produces(MediaType.APPLICATION_JSON)
70
+ @Consumes(MediaType.APPLICATION_JSON)
71
+ public class UserResource {
72
+
73
+ @Inject
74
+ UserService userService;
75
+
76
+ @GET
77
+ public Uni<List<UserResponse>> listUsers() {
78
+ return userService.findAllAsync();
79
+ }
80
+
81
+ @GET
82
+ @Path("/{id}")
83
+ public Uni<UserResponse> getUser(@PathParam("id") String id) {
84
+ return userService.findByIdAsync(id)
85
+ .onItem().ifNull().failWith(() -> new NotFoundException("User not found"));
86
+ }
87
+
88
+ @POST
89
+ public Uni<Response> createUser(@Valid CreateUserRequest request) {
90
+ return userService.createAsync(request)
91
+ .onItem().transform(user -> Response.status(Response.Status.CREATED)
92
+ .entity(user)
93
+ .build());
94
+ }
95
+ }
96
+ \`\`\`
97
+
98
+ ## Panache Entity (Active Record Pattern)
99
+ \`\`\`java
100
+ @Entity
101
+ @Table(name = "users")
102
+ public class User extends PanacheEntityBase {
103
+
104
+ @Id
105
+ @GeneratedValue(strategy = GenerationType.UUID)
106
+ public String id;
107
+
108
+ @Column(unique = true, nullable = false)
109
+ public String email;
110
+
111
+ @Column(nullable = false)
112
+ public String name;
113
+
114
+ @Column(name = "password_hash", nullable = false)
115
+ public String passwordHash;
116
+
117
+ @Column(name = "is_active")
118
+ public boolean isActive = true;
119
+
120
+ @Column(name = "created_at")
121
+ public Instant createdAt;
122
+
123
+ // Static finder methods
124
+ public static Optional<User> findByEmail(String email) {
125
+ return find("email", email).firstResultOptional();
126
+ }
127
+
128
+ public static List<User> findActive() {
129
+ return list("isActive", true);
130
+ }
131
+
132
+ public static PanacheQuery<User> findActiveQuery() {
133
+ return find("isActive", Sort.by("createdAt").descending(), true);
134
+ }
135
+
136
+ // Lifecycle callbacks
137
+ @PrePersist
138
+ void onCreate() {
139
+ createdAt = Instant.now();
140
+ }
141
+ }
142
+
143
+ // Usage
144
+ User user = new User();
145
+ user.email = "test@example.com";
146
+ user.name = "Test";
147
+ user.persist();
148
+
149
+ Optional<User> found = User.findByEmail("test@example.com");
150
+ List<User> page = User.findActiveQuery().page(0, 20).list();
151
+ long count = User.count("isActive", true);
152
+ \`\`\`
153
+
154
+ ## Panache Repository Pattern
155
+ \`\`\`java
156
+ @ApplicationScoped
157
+ public class UserRepository implements PanacheRepositoryBase<User, String> {
158
+
159
+ public Optional<User> findByEmail(String email) {
160
+ return find("email", email).firstResultOptional();
161
+ }
162
+
163
+ public List<User> findActive(int page, int size) {
164
+ return find("isActive", Sort.by("createdAt").descending(), true)
165
+ .page(page, size)
166
+ .list();
167
+ }
168
+
169
+ public long countActive() {
170
+ return count("isActive", true);
171
+ }
172
+ }
173
+
174
+ // Reactive repository
175
+ @ApplicationScoped
176
+ public class UserRepository implements PanacheRepositoryBase<User, String> {
177
+
178
+ public Uni<Optional<User>> findByEmailAsync(String email) {
179
+ return find("email", email).firstResultOptional();
180
+ }
181
+
182
+ public Uni<List<User>> findActiveAsync() {
183
+ return find("isActive", true).list();
184
+ }
185
+ }
186
+ \`\`\`
187
+
188
+ ## Service with CDI
189
+ \`\`\`java
190
+ @ApplicationScoped
191
+ public class UserService {
192
+
193
+ @Inject
194
+ UserRepository userRepository;
195
+
196
+ @Inject
197
+ PasswordEncoder passwordEncoder;
198
+
199
+ public List<UserResponse> findAll(int page, int size) {
200
+ return userRepository.findActive(page, size)
201
+ .stream()
202
+ .map(UserResponse::from)
203
+ .toList();
204
+ }
205
+
206
+ public Optional<UserResponse> findById(String id) {
207
+ return userRepository.findByIdOptional(id)
208
+ .map(UserResponse::from);
209
+ }
210
+
211
+ @Transactional
212
+ public UserResponse create(CreateUserRequest request) {
213
+ if (userRepository.findByEmail(request.email()).isPresent()) {
214
+ throw new ConflictException("Email already exists");
215
+ }
216
+
217
+ User user = new User();
218
+ user.email = request.email();
219
+ user.name = request.name();
220
+ user.passwordHash = passwordEncoder.encode(request.password());
221
+ userRepository.persist(user);
222
+
223
+ return UserResponse.from(user);
224
+ }
225
+
226
+ @Transactional
227
+ public UserResponse update(String id, UpdateUserRequest request) {
228
+ User user = userRepository.findByIdOptional(id)
229
+ .orElseThrow(() -> new NotFoundException("User not found"));
230
+
231
+ if (request.name() != null) {
232
+ user.name = request.name();
233
+ }
234
+
235
+ return UserResponse.from(user);
236
+ }
237
+
238
+ @Transactional
239
+ public void delete(String id) {
240
+ if (!userRepository.deleteById(id)) {
241
+ throw new NotFoundException("User not found");
242
+ }
243
+ }
244
+ }
245
+ \`\`\`
246
+
247
+ ## Exception Handling
248
+ \`\`\`java
249
+ @Provider
250
+ public class ExceptionMappers {
251
+
252
+ @ServerExceptionMapper
253
+ public Response handleNotFound(NotFoundException e) {
254
+ return Response.status(Response.Status.NOT_FOUND)
255
+ .entity(new ErrorResponse("NOT_FOUND", e.getMessage()))
256
+ .build();
257
+ }
258
+
259
+ @ServerExceptionMapper
260
+ public Response handleConflict(ConflictException e) {
261
+ return Response.status(Response.Status.CONFLICT)
262
+ .entity(new ErrorResponse("CONFLICT", e.getMessage()))
263
+ .build();
264
+ }
265
+
266
+ @ServerExceptionMapper
267
+ public Response handleValidation(ConstraintViolationException e) {
268
+ Map<String, String> errors = e.getConstraintViolations().stream()
269
+ .collect(Collectors.toMap(
270
+ v -> v.getPropertyPath().toString(),
271
+ ConstraintViolation::getMessage
272
+ ));
273
+ return Response.status(Response.Status.BAD_REQUEST)
274
+ .entity(new ValidationErrorResponse("VALIDATION_ERROR", errors))
275
+ .build();
276
+ }
277
+ }
278
+
279
+ public record ErrorResponse(String code, String message) {}
280
+ public record ValidationErrorResponse(String code, Map<String, String> errors) {}
281
+ \`\`\`
282
+
283
+ ## Configuration
284
+ \`\`\`properties
285
+ # application.properties
286
+
287
+ # Database
288
+ quarkus.datasource.db-kind=postgresql
289
+ quarkus.datasource.jdbc.url=\${DATABASE_URL}
290
+ quarkus.datasource.username=\${DATABASE_USER}
291
+ quarkus.datasource.password=\${DATABASE_PASSWORD}
292
+
293
+ # Hibernate
294
+ quarkus.hibernate-orm.database.generation=validate
295
+
296
+ # JWT Security
297
+ mp.jwt.verify.publickey.location=publicKey.pem
298
+ mp.jwt.verify.issuer=https://example.com
299
+
300
+ # Native build optimizations
301
+ quarkus.native.additional-build-args=--initialize-at-run-time=org.example.MyClass
302
+ \`\`\`
303
+
304
+ ## Testing
305
+ \`\`\`java
306
+ @QuarkusTest
307
+ class UserResourceTest {
308
+
309
+ @Test
310
+ void testGetUser() {
311
+ given()
312
+ .when().get("/api/v1/users/1")
313
+ .then()
314
+ .statusCode(200)
315
+ .body("email", equalTo("test@example.com"));
316
+ }
317
+
318
+ @Test
319
+ void testCreateUser() {
320
+ given()
321
+ .contentType(MediaType.APPLICATION_JSON)
322
+ .body("""
323
+ {"email": "new@example.com", "name": "New User", "password": "password123"}
324
+ """)
325
+ .when().post("/api/v1/users")
326
+ .then()
327
+ .statusCode(201)
328
+ .header("Location", containsString("/api/v1/users/"));
329
+ }
330
+
331
+ @Test
332
+ void testCreateUserInvalidEmail() {
333
+ given()
334
+ .contentType(MediaType.APPLICATION_JSON)
335
+ .body("""
336
+ {"email": "invalid", "name": "Test", "password": "password123"}
337
+ """)
338
+ .when().post("/api/v1/users")
339
+ .then()
340
+ .statusCode(400);
341
+ }
342
+ }
343
+
344
+ @QuarkusTest
345
+ @TestProfile(TestProfile.class) // Use test configuration
346
+ class UserServiceTest {
347
+
348
+ @Inject
349
+ UserService userService;
350
+
351
+ @Test
352
+ @Transactional
353
+ void testCreateAndFind() {
354
+ var request = new CreateUserRequest("test@example.com", "Test", "password123");
355
+ var created = userService.create(request);
356
+
357
+ assertThat(created.email()).isEqualTo("test@example.com");
358
+
359
+ var found = userService.findById(created.id());
360
+ assertThat(found).isPresent();
361
+ }
362
+ }
363
+ \`\`\`
364
+
365
+ ## ✅ DO
366
+ - Use Panache for simplified data access
367
+ - Use CDI \`@Inject\` for dependency injection
368
+ - Use \`@Transactional\` on service methods
369
+ - Use \`Uni<T>\` for reactive endpoints
370
+ - Use \`@ServerExceptionMapper\` for error handling
371
+ - Profile with \`quarkus dev\` for optimal native image
372
+
373
+ ## ❌ DON'T
374
+ - Don't use constructor injection (use field injection with CDI)
375
+ - Don't return null - use Optional or throw exceptions
376
+ - Don't block in reactive code
377
+ - Don't forget \`@Transactional\` for write operations with Panache
@@ -0,0 +1,230 @@
1
+ # React Skill
2
+
3
+ ## Component Structure
4
+ \`\`\`tsx
5
+ export function UserProfile({ userId }: Props) {
6
+ // 1. Hooks first
7
+ const [user, setUser] = useState<User | null>(null);
8
+ const [loading, setLoading] = useState(true);
9
+
10
+ // 2. Derived values
11
+ const fullName = user ? \`\${user.firstName} \${user.lastName}\` : '';
12
+
13
+ // 3. Effects
14
+ useEffect(() => {
15
+ fetchUser(userId).then(setUser).finally(() => setLoading(false));
16
+ }, [userId]);
17
+
18
+ // 4. Handlers
19
+ const handleLogout = () => { /* ... */ };
20
+
21
+ // 5. Early returns
22
+ if (loading) return <Skeleton />;
23
+ if (!user) return <NotFound />;
24
+
25
+ // 6. Main render
26
+ return <div>...</div>;
27
+ }
28
+ \`\`\`
29
+
30
+ ## Custom Hooks
31
+ \`\`\`tsx
32
+ // ✅ Extract reusable logic into custom hooks
33
+ function useLocalStorage<T>(key: string, initialValue: T) {
34
+ const [value, setValue] = useState<T>(() => {
35
+ const stored = localStorage.getItem(key);
36
+ return stored ? JSON.parse(stored) : initialValue;
37
+ });
38
+
39
+ useEffect(() => {
40
+ localStorage.setItem(key, JSON.stringify(value));
41
+ }, [key, value]);
42
+
43
+ return [value, setValue] as const;
44
+ }
45
+
46
+ // ✅ Data fetching hook
47
+ function useUser(userId: string) {
48
+ const [user, setUser] = useState<User | null>(null);
49
+ const [loading, setLoading] = useState(true);
50
+ const [error, setError] = useState<Error | null>(null);
51
+
52
+ useEffect(() => {
53
+ setLoading(true);
54
+ fetchUser(userId)
55
+ .then(setUser)
56
+ .catch(setError)
57
+ .finally(() => setLoading(false));
58
+ }, [userId]);
59
+
60
+ return { user, loading, error };
61
+ }
62
+ \`\`\`
63
+
64
+ ## Performance Optimization
65
+ \`\`\`tsx
66
+ // ✅ Memoize expensive computations
67
+ const sortedItems = useMemo(
68
+ () => items.sort((a, b) => b.score - a.score),
69
+ [items]
70
+ );
71
+
72
+ // ✅ Memoize callbacks passed to children
73
+ const handleClick = useCallback(
74
+ (id: string) => onSelect(id),
75
+ [onSelect]
76
+ );
77
+
78
+ // ✅ Memoize components receiving object/array props
79
+ const MemoizedList = memo(function List({ items }: Props) {
80
+ return items.map(item => <Item key={item.id} {...item} />);
81
+ });
82
+
83
+ // ✅ Skip re-renders with proper comparison
84
+ const MemoizedCard = memo(Card, (prev, next) => {
85
+ return prev.id === next.id && prev.name === next.name;
86
+ });
87
+ \`\`\`
88
+
89
+ ## Error Boundaries
90
+ \`\`\`tsx
91
+ class ErrorBoundary extends Component<Props, State> {
92
+ state = { hasError: false, error: null };
93
+
94
+ static getDerivedStateFromError(error: Error) {
95
+ return { hasError: true, error };
96
+ }
97
+
98
+ componentDidCatch(error: Error, info: ErrorInfo) {
99
+ logErrorToService(error, info.componentStack);
100
+ }
101
+
102
+ render() {
103
+ if (this.state.hasError) {
104
+ return <ErrorFallback error={this.state.error} />;
105
+ }
106
+ return this.props.children;
107
+ }
108
+ }
109
+
110
+ // Usage
111
+ <ErrorBoundary fallback={<ErrorPage />}>
112
+ <App />
113
+ </ErrorBoundary>
114
+ \`\`\`
115
+
116
+ ## Form Handling (react-hook-form)
117
+ \`\`\`tsx
118
+ import { useForm } from 'react-hook-form';
119
+ import { zodResolver } from '@hookform/resolvers/zod';
120
+
121
+ const schema = z.object({
122
+ email: z.string().email(),
123
+ password: z.string().min(8),
124
+ });
125
+
126
+ function LoginForm() {
127
+ const { register, handleSubmit, formState: { errors } } = useForm({
128
+ resolver: zodResolver(schema),
129
+ });
130
+
131
+ const onSubmit = (data: FormData) => {
132
+ // data is typed and validated
133
+ };
134
+
135
+ return (
136
+ <form onSubmit={handleSubmit(onSubmit)}>
137
+ <input {...register('email')} />
138
+ {errors.email && <span>{errors.email.message}</span>}
139
+
140
+ <input type="password" {...register('password')} />
141
+ {errors.password && <span>{errors.password.message}</span>}
142
+
143
+ <button type="submit">Login</button>
144
+ </form>
145
+ );
146
+ }
147
+ \`\`\`
148
+
149
+ ## Context API
150
+ \`\`\`tsx
151
+ // ✅ Create typed context with default
152
+ interface AuthContextType {
153
+ user: User | null;
154
+ login: (email: string, password: string) => Promise<void>;
155
+ logout: () => void;
156
+ }
157
+
158
+ const AuthContext = createContext<AuthContextType | null>(null);
159
+
160
+ // ✅ Custom hook for consuming context
161
+ function useAuth() {
162
+ const context = useContext(AuthContext);
163
+ if (!context) {
164
+ throw new Error('useAuth must be used within AuthProvider');
165
+ }
166
+ return context;
167
+ }
168
+
169
+ // ✅ Provider component
170
+ function AuthProvider({ children }: { children: ReactNode }) {
171
+ const [user, setUser] = useState<User | null>(null);
172
+
173
+ const login = async (email: string, password: string) => {
174
+ const user = await authService.login(email, password);
175
+ setUser(user);
176
+ };
177
+
178
+ const logout = () => setUser(null);
179
+
180
+ return (
181
+ <AuthContext.Provider value={{ user, login, logout }}>
182
+ {children}
183
+ </AuthContext.Provider>
184
+ );
185
+ }
186
+ \`\`\`
187
+
188
+ ## Compound Component Pattern
189
+ \`\`\`tsx
190
+ // ✅ Flexible, composable API
191
+ const Tabs = ({ children, defaultTab }) => {
192
+ const [activeTab, setActiveTab] = useState(defaultTab);
193
+ return (
194
+ <TabsContext.Provider value={{ activeTab, setActiveTab }}>
195
+ {children}
196
+ </TabsContext.Provider>
197
+ );
198
+ };
199
+
200
+ Tabs.List = ({ children }) => <div role="tablist">{children}</div>;
201
+ Tabs.Tab = ({ id, children }) => { /* ... */ };
202
+ Tabs.Panel = ({ id, children }) => { /* ... */ };
203
+
204
+ // Usage
205
+ <Tabs defaultTab="settings">
206
+ <Tabs.List>
207
+ <Tabs.Tab id="profile">Profile</Tabs.Tab>
208
+ <Tabs.Tab id="settings">Settings</Tabs.Tab>
209
+ </Tabs.List>
210
+ <Tabs.Panel id="profile"><Profile /></Tabs.Panel>
211
+ <Tabs.Panel id="settings"><Settings /></Tabs.Panel>
212
+ </Tabs>
213
+ \`\`\`
214
+
215
+ ## ❌ DON'T
216
+ - Use useEffect for data fetching (use React Query/SWR)
217
+ - Mutate state directly
218
+ - Use index as key for dynamic lists
219
+ - Create components inside components
220
+ - Overuse useMemo/useCallback (measure first)
221
+ - Put too much in Context (causes re-renders)
222
+
223
+ ## ✅ DO
224
+ - Use React Query/SWR for server state
225
+ - Use useState for UI state only
226
+ - Memoize expensive computations: useMemo
227
+ - Extract custom hooks for reusable logic
228
+ - Use Error Boundaries for graceful failures
229
+ - Use react-hook-form for complex forms
230
+ - Split Context by update frequency