@sylphx/flow 0.2.1 → 0.2.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.
- package/assets/agents/coder.md +1 -1
- package/assets/agents/orchestrator.md +1 -1
- package/assets/agents/reviewer.md +1 -1
- package/assets/agents/writer.md +13 -13
- package/assets/slash-commands/context.md +112 -0
- package/dist/assets/agents/coder.md +32 -0
- package/dist/assets/agents/orchestrator.md +36 -0
- package/dist/assets/agents/reviewer.md +30 -0
- package/dist/assets/agents/writer.md +30 -0
- package/dist/assets/knowledge/data/sql.md +216 -0
- package/dist/assets/knowledge/guides/saas-template.md +85 -0
- package/dist/assets/knowledge/guides/system-prompt.md +344 -0
- package/dist/assets/knowledge/guides/tech-stack.md +92 -0
- package/dist/assets/knowledge/guides/ui-ux.md +44 -0
- package/dist/assets/knowledge/stacks/nextjs-app.md +165 -0
- package/dist/assets/knowledge/stacks/node-api.md +220 -0
- package/dist/assets/knowledge/stacks/react-app.md +232 -0
- package/dist/assets/knowledge/universal/deployment.md +109 -0
- package/dist/assets/knowledge/universal/performance.md +121 -0
- package/dist/assets/knowledge/universal/security.md +79 -0
- package/dist/assets/knowledge/universal/testing.md +111 -0
- package/dist/assets/output-styles/silent.md +23 -0
- package/dist/assets/rules/core.md +144 -0
- package/dist/assets/slash-commands/commit.md +23 -0
- package/dist/assets/slash-commands/context.md +112 -0
- package/dist/assets/slash-commands/explain.md +35 -0
- package/dist/assets/slash-commands/mep.md +63 -0
- package/dist/assets/slash-commands/review.md +39 -0
- package/dist/assets/slash-commands/test.md +30 -0
- package/dist/chunk-1rptg3yg.js +4 -0
- package/dist/chunk-1rptg3yg.js.map +10 -0
- package/dist/{chunk-124wqbdb.js → chunk-4fr8q0jy.js} +3 -3
- package/dist/{chunk-124wqbdb.js.map → chunk-4fr8q0jy.js.map} +1 -1
- package/dist/{chunk-f6y5vttn.js → chunk-5szm4n3x.js} +3 -3
- package/dist/{chunk-f6y5vttn.js.map → chunk-5szm4n3x.js.map} +1 -1
- package/dist/chunk-7nht27vs.js +4 -0
- package/dist/{chunk-g9t3me0w.js.map → chunk-7nht27vs.js.map} +2 -2
- package/dist/chunk-8krxe10w.js +3 -0
- package/dist/{chunk-e966bjm5.js.map → chunk-8krxe10w.js.map} +2 -2
- package/dist/{chunk-wpe7rw5c.js → chunk-8z1sf25t.js} +3 -3
- package/dist/{chunk-wpe7rw5c.js.map → chunk-8z1sf25t.js.map} +1 -1
- package/dist/chunk-9c2nr2fz.js +25 -0
- package/dist/chunk-9c2nr2fz.js.map +61 -0
- package/dist/{chunk-4p754rhd.js → chunk-asr22mbn.js} +2 -2
- package/dist/{chunk-4p754rhd.js.map → chunk-asr22mbn.js.map} +2 -2
- package/dist/chunk-bnxtqetr.js +23 -0
- package/dist/chunk-bnxtqetr.js.map +11 -0
- package/dist/chunk-cs1s5c3g.js +54 -0
- package/dist/chunk-cs1s5c3g.js.map +53 -0
- package/dist/chunk-cv1nhr27.js +2 -0
- package/dist/{chunk-hshjnpm0.js.map → chunk-cv1nhr27.js.map} +1 -1
- package/dist/chunk-d4hj6d4t.js +6 -0
- package/dist/chunk-d4hj6d4t.js.map +11 -0
- package/dist/chunk-f06ma45b.js +15 -0
- package/dist/chunk-f06ma45b.js.map +16 -0
- package/dist/chunk-fs3f7acb.js +4 -0
- package/dist/chunk-fs3f7acb.js.map +12 -0
- package/dist/{chunk-5r4afhzp.js → chunk-gh83x9ya.js} +3 -3
- package/dist/{chunk-5r4afhzp.js.map → chunk-gh83x9ya.js.map} +1 -1
- package/dist/{chunk-qa8b725g.js → chunk-gyq335sw.js} +6 -5
- package/dist/{chunk-qa8b725g.js.map → chunk-gyq335sw.js.map} +1 -1
- package/dist/{chunk-hs3nxzyz.js → chunk-hft1735c.js} +2 -2
- package/dist/{chunk-hs3nxzyz.js.map → chunk-hft1735c.js.map} +2 -2
- package/dist/chunk-hj6r7703.js +3 -0
- package/dist/{chunk-78bfvh46.js.map → chunk-hj6r7703.js.map} +2 -2
- package/dist/chunk-hxj4eapp.js +14 -0
- package/dist/chunk-hxj4eapp.js.map +20 -0
- package/dist/chunk-jgsq3xax.js +23 -0
- package/dist/chunk-jgsq3xax.js.map +132 -0
- package/dist/{chunk-646h52kd.js → chunk-m9nt0bj3.js} +3 -3
- package/dist/{chunk-646h52kd.js.map → chunk-m9nt0bj3.js.map} +1 -1
- package/dist/{chunk-bd11hvvz.js → chunk-ndah8mn9.js} +2 -2
- package/dist/{chunk-bd11hvvz.js.map → chunk-ndah8mn9.js.map} +1 -1
- package/dist/chunk-s6g21d1g.js +27 -0
- package/dist/{chunk-0h7sfwq3.js.map → chunk-s6g21d1g.js.map} +4 -5
- package/dist/{chunk-hshjnpm0.js → chunk-sxy6vp20.js} +2 -2
- package/dist/chunk-sxy6vp20.js.map +9 -0
- package/dist/chunk-vjf57v4h.js +4 -0
- package/dist/chunk-vjf57v4h.js.map +10 -0
- package/dist/{chunk-jxny6xft.js → chunk-w2vbmr93.js} +2 -2
- package/dist/{chunk-jxny6xft.js.map → chunk-w2vbmr93.js.map} +1 -1
- package/dist/chunk-wd9qbbe5.js +5 -0
- package/dist/chunk-wd9qbbe5.js.map +10 -0
- package/dist/chunk-wnaa55wn.js +108 -0
- package/dist/chunk-wnaa55wn.js.map +24 -0
- package/dist/chunk-wrx1n6q6.js +16 -0
- package/dist/chunk-wrx1n6q6.js.map +16 -0
- package/dist/chunk-xata5rw6.js +119 -0
- package/dist/{chunk-878q8xdr.js.map → chunk-xata5rw6.js.map} +7 -18
- package/dist/chunk-z2rtyk3d.js +7 -0
- package/dist/{chunk-ygdr4fw7.js.map → chunk-z2rtyk3d.js.map} +2 -2
- package/dist/index.js +446 -482
- package/dist/index.js.map +301 -202
- package/package.json +4 -1
- package/dist/chunk-0h7sfwq3.js +0 -27
- package/dist/chunk-78bfvh46.js +0 -3
- package/dist/chunk-878q8xdr.js +0 -86
- package/dist/chunk-e966bjm5.js +0 -3
- package/dist/chunk-fxwaa2mg.js +0 -4
- package/dist/chunk-fxwaa2mg.js.map +0 -10
- package/dist/chunk-g9t3me0w.js +0 -4
- package/dist/chunk-ygdr4fw7.js +0 -7
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Node.js API
|
|
3
|
+
description: Express/Fastify, REST/GraphQL, authentication, middleware, error handling
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Backend API Development
|
|
7
|
+
|
|
8
|
+
## REST Structure
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
GET /users List
|
|
12
|
+
POST /users Create
|
|
13
|
+
GET /users/:id Get
|
|
14
|
+
PATCH /users/:id Update
|
|
15
|
+
DELETE /users/:id Delete
|
|
16
|
+
GET /users/:id/posts Nested
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Status**: 200 OK, 201 Created, 204 No Content, 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 500 Internal Server Error
|
|
20
|
+
|
|
21
|
+
**Response format (project standard):**
|
|
22
|
+
```json
|
|
23
|
+
{ "data": {...}, "meta": {...} }
|
|
24
|
+
{ "items": [...], "total": 100, "page": 1, "limit": 20 }
|
|
25
|
+
{ "error": { "code": "VALIDATION_ERROR", "message": "...", "field": "..." } }
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## N+1 Problem
|
|
29
|
+
|
|
30
|
+
```javascript
|
|
31
|
+
// BAD - N+1 queries
|
|
32
|
+
for (user of users) { user.posts = await getPosts(user.id) }
|
|
33
|
+
|
|
34
|
+
// GOOD - Join
|
|
35
|
+
await db.users.findMany({ include: { posts: true } })
|
|
36
|
+
|
|
37
|
+
// GOOD - Batch fetch
|
|
38
|
+
const userIds = users.map(u => u.id)
|
|
39
|
+
const posts = await db.posts.findMany({ where: { userId: { in: userIds } } })
|
|
40
|
+
|
|
41
|
+
// GraphQL: DataLoader
|
|
42
|
+
const loader = new DataLoader(async (ids) => {
|
|
43
|
+
const items = await db.find({ where: { id: { in: ids } } })
|
|
44
|
+
return ids.map(id => items.filter(i => i.parentId === id))
|
|
45
|
+
})
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Database
|
|
49
|
+
|
|
50
|
+
**Connection pooling:**
|
|
51
|
+
```javascript
|
|
52
|
+
const pool = new Pool({ max: 20, idleTimeoutMillis: 30000 })
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Caching pattern:**
|
|
56
|
+
```javascript
|
|
57
|
+
async function getUser(id) {
|
|
58
|
+
const cached = await redis.get(`user:${id}`)
|
|
59
|
+
if (cached) return JSON.parse(cached)
|
|
60
|
+
|
|
61
|
+
const user = await db.users.findUnique({ where: { id } })
|
|
62
|
+
await redis.set(`user:${id}`, JSON.stringify(user), 'EX', 3600)
|
|
63
|
+
return user
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Invalidate on update
|
|
67
|
+
async function updateUser(id, data) {
|
|
68
|
+
const user = await db.users.update({ where: { id }, data })
|
|
69
|
+
await redis.del(`user:${id}`)
|
|
70
|
+
return user
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Authentication
|
|
75
|
+
|
|
76
|
+
**Session vs Token:**
|
|
77
|
+
- Session: Traditional web apps, need fine-grained control
|
|
78
|
+
- Token (JWT): SPAs, mobile apps, microservices
|
|
79
|
+
|
|
80
|
+
**JWT middleware (project standard):**
|
|
81
|
+
```javascript
|
|
82
|
+
function requireAuth(req, res, next) {
|
|
83
|
+
const token = req.headers.authorization?.split(' ')[1]
|
|
84
|
+
if (!token) return res.status(401).json({ error: { code: 'NO_TOKEN' } })
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
req.userId = jwt.verify(token, process.env.JWT_SECRET).userId
|
|
88
|
+
next()
|
|
89
|
+
} catch {
|
|
90
|
+
res.status(401).json({ error: { code: 'INVALID_TOKEN' } })
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Authorization patterns:**
|
|
96
|
+
```javascript
|
|
97
|
+
// Role check
|
|
98
|
+
function requireRole(...roles) {
|
|
99
|
+
return async (req, res, next) => {
|
|
100
|
+
const user = await db.users.findUnique({ where: { id: req.userId } })
|
|
101
|
+
if (!roles.includes(user.role)) {
|
|
102
|
+
return res.status(403).json({ error: { code: 'FORBIDDEN' } })
|
|
103
|
+
}
|
|
104
|
+
next()
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Ownership check
|
|
109
|
+
function requireOwnership(getter) {
|
|
110
|
+
return async (req, res, next) => {
|
|
111
|
+
const resource = await getter(req)
|
|
112
|
+
if (resource.userId !== req.userId) {
|
|
113
|
+
return res.status(403).json({ error: { code: 'NOT_OWNER' } })
|
|
114
|
+
}
|
|
115
|
+
next()
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Error Handling
|
|
121
|
+
|
|
122
|
+
**Project standard:**
|
|
123
|
+
```javascript
|
|
124
|
+
class ApiError extends Error {
|
|
125
|
+
constructor(statusCode, code, message) {
|
|
126
|
+
super(message)
|
|
127
|
+
this.statusCode = statusCode
|
|
128
|
+
this.code = code
|
|
129
|
+
this.isOperational = true
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Error middleware (last!)
|
|
134
|
+
app.use((err, req, res, next) => {
|
|
135
|
+
if (err.isOperational) {
|
|
136
|
+
return res.status(err.statusCode).json({
|
|
137
|
+
error: { code: err.code, message: err.message }
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.error('PROGRAMMER ERROR:', err)
|
|
142
|
+
res.status(500).json({ error: { code: 'INTERNAL_ERROR' } })
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
// Usage
|
|
146
|
+
if (!user) throw new ApiError(404, 'NOT_FOUND', 'User not found')
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Performance
|
|
150
|
+
|
|
151
|
+
**Targets**: DB < 100ms, API < 200ms
|
|
152
|
+
|
|
153
|
+
**Optimize:**
|
|
154
|
+
- N+1 → Joins / DataLoader
|
|
155
|
+
- Connection pooling
|
|
156
|
+
- Redis caching
|
|
157
|
+
- Job queues (background tasks)
|
|
158
|
+
- Pagination (always LIMIT)
|
|
159
|
+
|
|
160
|
+
## GraphQL Basics
|
|
161
|
+
|
|
162
|
+
**When**: Complex relationships, flexible queries
|
|
163
|
+
**vs REST**: Simple CRUD → REST
|
|
164
|
+
|
|
165
|
+
**DataLoader (required for N+1):**
|
|
166
|
+
```javascript
|
|
167
|
+
const postLoader = new DataLoader(async (userIds) => {
|
|
168
|
+
const posts = await db.posts.findMany({ where: { userId: { in: userIds } } })
|
|
169
|
+
return userIds.map(id => posts.filter(p => p.userId === id))
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
// In resolver
|
|
173
|
+
User: {
|
|
174
|
+
posts: (user) => postLoader.load(user.id)
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Common Patterns
|
|
179
|
+
|
|
180
|
+
**Repository:**
|
|
181
|
+
```javascript
|
|
182
|
+
class UserRepo {
|
|
183
|
+
findById(id) { return db.users.findUnique({ where: { id } }) }
|
|
184
|
+
save(data) { return db.users.create({ data }) }
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Service:**
|
|
189
|
+
```javascript
|
|
190
|
+
class UserService {
|
|
191
|
+
async createUser(data) {
|
|
192
|
+
if (!data.email) throw new Error('Email required')
|
|
193
|
+
return await this.repo.save(data)
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Middleware chain:**
|
|
199
|
+
```javascript
|
|
200
|
+
app.use(cors())
|
|
201
|
+
app.use(express.json())
|
|
202
|
+
app.use(rateLimit())
|
|
203
|
+
app.use(auth())
|
|
204
|
+
app.use(errorHandler()) // Last!
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Best Practices
|
|
208
|
+
|
|
209
|
+
✅ Prepared statements (prevent injection)
|
|
210
|
+
✅ Connection pooling
|
|
211
|
+
✅ Index foreign keys
|
|
212
|
+
✅ Rate limit auth endpoints
|
|
213
|
+
✅ Hash passwords (bcrypt/argon2)
|
|
214
|
+
✅ HTTPS only
|
|
215
|
+
✅ Validate server-side
|
|
216
|
+
|
|
217
|
+
❌ SQL injection (string concat)
|
|
218
|
+
❌ Plain text passwords
|
|
219
|
+
❌ N+1 queries
|
|
220
|
+
❌ No error handling
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: React Application
|
|
3
|
+
description: React patterns, hooks, state management, performance, common bugs and fixes
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# React Development
|
|
7
|
+
|
|
8
|
+
## State Management Decision Tree
|
|
9
|
+
```
|
|
10
|
+
Used by 3+ unrelated components?
|
|
11
|
+
├─ NO → useState in common ancestor
|
|
12
|
+
└─ YES → Server data?
|
|
13
|
+
├─ YES → React Query/SWR (not state!)
|
|
14
|
+
└─ NO → Complex actions?
|
|
15
|
+
├─ YES → useReducer / Zustand
|
|
16
|
+
└─ NO → Context API
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Common Bugs & Fixes
|
|
20
|
+
|
|
21
|
+
### Infinite useEffect Loop
|
|
22
|
+
|
|
23
|
+
**Object/array deps:**
|
|
24
|
+
```javascript
|
|
25
|
+
// BAD
|
|
26
|
+
useEffect(() => { fetch(users) }, [users])
|
|
27
|
+
|
|
28
|
+
// FIX
|
|
29
|
+
useEffect(() => { fetch(users) }, [userIds.join(',')]) // Primitive
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Unconditional state update:**
|
|
33
|
+
```javascript
|
|
34
|
+
// BAD
|
|
35
|
+
useEffect(() => { setCount(count + 1) }, [count])
|
|
36
|
+
|
|
37
|
+
// FIX
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (condition && count < max) setCount(count + 1)
|
|
40
|
+
}, [count, condition, max])
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Stale Closure
|
|
44
|
+
|
|
45
|
+
```javascript
|
|
46
|
+
// BAD - uses initial count
|
|
47
|
+
const [count, setCount] = useState(0)
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
setInterval(() => setCount(count + 1), 1000)
|
|
50
|
+
}, [])
|
|
51
|
+
|
|
52
|
+
// FIX: Functional update
|
|
53
|
+
setInterval(() => setCount(c => c + 1), 1000)
|
|
54
|
+
|
|
55
|
+
// FIX: useRef (complex cases)
|
|
56
|
+
const countRef = useRef(count)
|
|
57
|
+
useEffect(() => { countRef.current = count })
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
setInterval(() => setCount(countRef.current + 1), 1000)
|
|
60
|
+
}, [])
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Missing Cleanup
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
// BAD
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
subscribeToData(setData)
|
|
69
|
+
}, [])
|
|
70
|
+
|
|
71
|
+
// GOOD
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
const unsubscribe = subscribeToData(setData)
|
|
74
|
+
return () => unsubscribe()
|
|
75
|
+
}, [])
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Performance
|
|
79
|
+
|
|
80
|
+
**Optimize when:**
|
|
81
|
+
- Profiler shows slowness
|
|
82
|
+
- User-visible lag
|
|
83
|
+
- 100+ renders/second
|
|
84
|
+
|
|
85
|
+
**Profile first, optimize second.**
|
|
86
|
+
|
|
87
|
+
### Unnecessary Re-renders
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
// BAD
|
|
91
|
+
function Parent() {
|
|
92
|
+
const config = { theme: 'dark' }
|
|
93
|
+
return <Child config={config} />
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// GOOD
|
|
97
|
+
const config = useMemo(() => ({ theme: 'dark' }), [])
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Order:**
|
|
101
|
+
1. Fix parent
|
|
102
|
+
2. useMemo/useCallback
|
|
103
|
+
3. Profile
|
|
104
|
+
4. React.memo (last resort)
|
|
105
|
+
|
|
106
|
+
### Virtualization
|
|
107
|
+
|
|
108
|
+
**When**: 500+ items, laggy scroll
|
|
109
|
+
|
|
110
|
+
```javascript
|
|
111
|
+
import { FixedSizeList } from 'react-window'
|
|
112
|
+
<FixedSizeList height={600} itemCount={items.length} itemSize={35}>
|
|
113
|
+
{({ index, style }) => <div style={style}>{items[index]}</div>}
|
|
114
|
+
</FixedSizeList>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Debounce Search
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
const debouncedQuery = useDebounce(query, 300)
|
|
121
|
+
useEffect(() => {
|
|
122
|
+
if (debouncedQuery) searchAPI(debouncedQuery)
|
|
123
|
+
}, [debouncedQuery])
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Data Fetching
|
|
127
|
+
|
|
128
|
+
**Never** useState for server data. You lose: caching, loading, errors, refetching, race conditions.
|
|
129
|
+
|
|
130
|
+
**Use React Query:**
|
|
131
|
+
```javascript
|
|
132
|
+
const { data, isLoading, error } = useQuery({
|
|
133
|
+
queryKey: ['users'],
|
|
134
|
+
queryFn: () => fetch('/api/users').then(r => r.json())
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
const mutation = useMutation({
|
|
138
|
+
mutationFn: (user) => fetch('/api/users', { method: 'POST', body: JSON.stringify(user) }),
|
|
139
|
+
onSuccess: () => queryClient.invalidateQueries(['users'])
|
|
140
|
+
})
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Forms
|
|
144
|
+
|
|
145
|
+
**React Hook Form** (recommended):
|
|
146
|
+
```javascript
|
|
147
|
+
const { register, handleSubmit, formState: { errors } } = useForm()
|
|
148
|
+
|
|
149
|
+
<form onSubmit={handleSubmit(onSubmit)}>
|
|
150
|
+
<input {...register('email', {
|
|
151
|
+
required: 'Required',
|
|
152
|
+
pattern: { value: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i }
|
|
153
|
+
})} />
|
|
154
|
+
{errors.email && <span>{errors.email.message}</span>}
|
|
155
|
+
</form>
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Controlled vs Uncontrolled:**
|
|
159
|
+
- Controlled: Validation on change, dependent fields
|
|
160
|
+
- Uncontrolled: Simple submit, file inputs
|
|
161
|
+
|
|
162
|
+
## Accessibility
|
|
163
|
+
|
|
164
|
+
**Critical:**
|
|
165
|
+
1. Semantic HTML (`<button>` not `<div onClick>`)
|
|
166
|
+
2. Alt text (meaningful or `alt=""`)
|
|
167
|
+
3. Form labels (every input)
|
|
168
|
+
4. Keyboard nav (Tab, Enter/Space)
|
|
169
|
+
5. Focus visible
|
|
170
|
+
|
|
171
|
+
**Test**: VoiceOver (Cmd+F5), Tab through app
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
// BAD
|
|
175
|
+
<div onClick={handleClick}>Click</div>
|
|
176
|
+
|
|
177
|
+
// GOOD
|
|
178
|
+
<button onClick={handleClick}>Click</button>
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Debug Workflow
|
|
182
|
+
|
|
183
|
+
**Component not updating:**
|
|
184
|
+
1. State changing? (console.log)
|
|
185
|
+
2. setState called?
|
|
186
|
+
3. Mutating state? (must create new)
|
|
187
|
+
4. Memoized with stale props?
|
|
188
|
+
|
|
189
|
+
**Performance:**
|
|
190
|
+
1. Profile (React DevTools)
|
|
191
|
+
2. Identify slow component
|
|
192
|
+
3. Check: Unnecessary re-renders?
|
|
193
|
+
4. Check: Expensive computation?
|
|
194
|
+
5. Optimize bottleneck only
|
|
195
|
+
|
|
196
|
+
## Production Patterns
|
|
197
|
+
|
|
198
|
+
**Error Boundary:**
|
|
199
|
+
```javascript
|
|
200
|
+
class ErrorBoundary extends React.Component {
|
|
201
|
+
state = { hasError: false }
|
|
202
|
+
static getDerivedStateFromError() { return { hasError: true } }
|
|
203
|
+
componentDidCatch(error, info) { logError(error, info) }
|
|
204
|
+
render() {
|
|
205
|
+
return this.state.hasError ? <ErrorFallback /> : this.props.children
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Code Splitting:**
|
|
211
|
+
```javascript
|
|
212
|
+
const Dashboard = lazy(() => import('./Dashboard'))
|
|
213
|
+
<Suspense fallback={<Loading />}>
|
|
214
|
+
<Dashboard />
|
|
215
|
+
</Suspense>
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Key Decisions
|
|
219
|
+
|
|
220
|
+
**State unclear?** → useState, refactor later
|
|
221
|
+
**Performance slow?** → Profile first
|
|
222
|
+
**Form complex?** → React Hook Form
|
|
223
|
+
**Data fetching?** → React Query
|
|
224
|
+
**Not sure?** → Simpler option
|
|
225
|
+
|
|
226
|
+
## Anti-Patterns
|
|
227
|
+
|
|
228
|
+
❌ Server data in useState
|
|
229
|
+
❌ Premature optimization
|
|
230
|
+
❌ React.memo everywhere
|
|
231
|
+
❌ Missing cleanup
|
|
232
|
+
❌ Div as button
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Deployment & DevOps
|
|
3
|
+
description: Docker, CI/CD, monitoring, scaling, infrastructure
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Deployment & DevOps
|
|
7
|
+
|
|
8
|
+
## Deployment Strategies
|
|
9
|
+
|
|
10
|
+
**Blue-Green**: Two environments (blue=current, green=new) → test → switch. Zero downtime, instant rollback, 2x cost.
|
|
11
|
+
|
|
12
|
+
**Rolling**: Replace gradually (10% at a time). No extra infrastructure, lower risk, slower.
|
|
13
|
+
|
|
14
|
+
**Canary**: Route small % to new version. Test in production, minimal risk, complex routing.
|
|
15
|
+
|
|
16
|
+
**Feature Flags**: Deploy code disabled, enable via config. Decouple deploy from release, A/B testing, kill switches.
|
|
17
|
+
|
|
18
|
+
## CI/CD Pipeline
|
|
19
|
+
|
|
20
|
+
### Continuous Integration (On Every Commit)
|
|
21
|
+
1. Run linter
|
|
22
|
+
2. Run tests (unit, integration)
|
|
23
|
+
3. Build application
|
|
24
|
+
4. Security scanning
|
|
25
|
+
5. Code quality checks
|
|
26
|
+
|
|
27
|
+
**Best practices**: Fast feedback (< 10min), fail fast, keep builds green
|
|
28
|
+
|
|
29
|
+
### Continuous Deployment
|
|
30
|
+
**Stages**: CI passes → deploy staging → E2E tests → deploy production → health checks → rollback if failed
|
|
31
|
+
|
|
32
|
+
**Tools**: GitHub Actions, GitLab CI, Jenkins, CircleCI
|
|
33
|
+
|
|
34
|
+
## Containerization (Docker)
|
|
35
|
+
|
|
36
|
+
### Best Practices
|
|
37
|
+
- Multi-stage builds (build → slim runtime)
|
|
38
|
+
- Slim/alpine images
|
|
39
|
+
- Layer caching (deps before code)
|
|
40
|
+
- .dockerignore
|
|
41
|
+
- Don't run as root
|
|
42
|
+
|
|
43
|
+
## Orchestration (Kubernetes)
|
|
44
|
+
|
|
45
|
+
**Core**: Pod (containers), Deployment (replicas), Service (load balancer), Ingress (routing), ConfigMap (config), Secret (sensitive)
|
|
46
|
+
|
|
47
|
+
**Scaling**:
|
|
48
|
+
- Horizontal Pod Autoscaler: Scale based on CPU/memory, min/max replicas
|
|
49
|
+
- Cluster Autoscaler: Add/remove nodes
|
|
50
|
+
|
|
51
|
+
## Monitoring & Observability
|
|
52
|
+
|
|
53
|
+
### Logs
|
|
54
|
+
**Structured logging**: JSON format with level, timestamp, message, context (user_id, trace_id)
|
|
55
|
+
|
|
56
|
+
**Centralized**: ELK, Datadog, CloudWatch
|
|
57
|
+
**Best practices**: Log errors with context, trace IDs, don't log secrets, set retention
|
|
58
|
+
|
|
59
|
+
### Metrics
|
|
60
|
+
**Track**: Request rate, error rate, response time (latency), resource usage (CPU, memory, disk)
|
|
61
|
+
**Tools**: Prometheus, Grafana, Datadog
|
|
62
|
+
|
|
63
|
+
### Tracing
|
|
64
|
+
**Distributed tracing**: Track requests across services, identify bottlenecks
|
|
65
|
+
**Tools**: Jaeger, Zipkin, Datadog APM
|
|
66
|
+
|
|
67
|
+
### Alerting
|
|
68
|
+
**Alert on**: Error rate > threshold, response time > SLA, resource exhaustion, service down
|
|
69
|
+
**Best practices**: Actionable only, include runbooks, escalation policies, on-call rotation
|
|
70
|
+
|
|
71
|
+
## High Availability
|
|
72
|
+
|
|
73
|
+
### Load Balancing
|
|
74
|
+
**Algorithms**: Round robin (equal), least connections (least busy), IP hash (consistent)
|
|
75
|
+
**Health checks**: HTTP (/_health), TCP, check every 10-30s, remove unhealthy
|
|
76
|
+
|
|
77
|
+
### Database Replication
|
|
78
|
+
**Primary-Replica**: Writes to primary, reads from replicas, async (eventual consistency)
|
|
79
|
+
**Multi-Primary**: Write to any, conflict resolution needed, complex but highly available
|
|
80
|
+
|
|
81
|
+
### Backup & Disaster Recovery
|
|
82
|
+
**Backups**: Automated daily, retention (7 days, 4 weeks, 12 months), test restores, offsite/cross-region
|
|
83
|
+
**RTO/RPO**: RTO (recovery time), RPO (data loss acceptable)
|
|
84
|
+
|
|
85
|
+
## Security
|
|
86
|
+
|
|
87
|
+
**Network**: Firewall (whitelist), private subnets for DBs, VPN for internal, DDoS protection
|
|
88
|
+
**Secrets**: Never commit to git, use secret managers (AWS Secrets Manager, Vault), rotate regularly, least privilege
|
|
89
|
+
**SSL/TLS**: HTTPS everywhere, auto-renewal (Let's Encrypt), strong ciphers, HSTS headers
|
|
90
|
+
**Compliance**: Encryption at rest, encryption in transit, access logs, security audits
|
|
91
|
+
|
|
92
|
+
## Cost Optimization
|
|
93
|
+
|
|
94
|
+
**Right-sizing**: Monitor usage, scale down over-provisioned, spot/preemptible for non-critical
|
|
95
|
+
**Auto-scaling**: Scale down off-peak, scale up peak
|
|
96
|
+
**Reserved Instances**: 1-3 year commit for 30-70% discount (predictable workloads)
|
|
97
|
+
**Storage**: Lifecycle policies (move old to cheaper), delete unused, compress backups
|
|
98
|
+
|
|
99
|
+
## Common Patterns
|
|
100
|
+
|
|
101
|
+
**Immutable Infrastructure**: Never modify servers, deploy new, terminate old
|
|
102
|
+
**Infrastructure as Code**: Terraform, CloudFormation, Pulumi → version controlled, reproducible
|
|
103
|
+
**GitOps**: Git as truth, auto-deploy on merge, drift detection (ArgoCD, Flux)
|
|
104
|
+
|
|
105
|
+
## Best Practices
|
|
106
|
+
|
|
107
|
+
**Documentation**: Runbooks, architecture diagrams, incident response, on-call guides
|
|
108
|
+
**Change Management**: Review process, deployment windows, rollback procedures, communication
|
|
109
|
+
**Incident Response**: Detect → Triage → Mitigate → Resolve → Post-mortem
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Performance Optimization
|
|
3
|
+
description: Profiling, caching, optimization patterns across frontend and backend
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Performance Optimization
|
|
7
|
+
|
|
8
|
+
## Measure First
|
|
9
|
+
|
|
10
|
+
**Never optimize without measuring.**
|
|
11
|
+
|
|
12
|
+
### Tools
|
|
13
|
+
**Frontend**: Chrome DevTools, Lighthouse, React Profiler
|
|
14
|
+
**Backend**: APM (New Relic, Datadog), query explain plans
|
|
15
|
+
|
|
16
|
+
### Metrics
|
|
17
|
+
**Frontend**: FCP < 1.8s, LCP < 2.5s, CLS < 0.1, FID < 100ms, TTI < 3.5s
|
|
18
|
+
**Backend**: Response < 200ms (p95), error rate < 1%, DB query < 100ms
|
|
19
|
+
|
|
20
|
+
## Frontend Optimization
|
|
21
|
+
|
|
22
|
+
### Bundle Size
|
|
23
|
+
**Analyze**: webpack-bundle-analyzer
|
|
24
|
+
**Reduce**: Tree-shaking, code splitting, lighter alternatives, remove unused deps
|
|
25
|
+
|
|
26
|
+
### Loading Strategy
|
|
27
|
+
1. Inline critical CSS
|
|
28
|
+
2. Defer non-critical CSS
|
|
29
|
+
3. Async non-critical JS
|
|
30
|
+
4. Lazy load below-fold images
|
|
31
|
+
5. Code splitting: `lazy(() => import('./Heavy'))`
|
|
32
|
+
|
|
33
|
+
### Images
|
|
34
|
+
- WebP format (smaller, better quality)
|
|
35
|
+
- Responsive (srcset)
|
|
36
|
+
- Lazy loading (loading="lazy")
|
|
37
|
+
- CDN delivery
|
|
38
|
+
- Optimize: compress, resize, correct format
|
|
39
|
+
|
|
40
|
+
### Caching
|
|
41
|
+
**Browser**: Cache-Control headers, versioned assets (hash), service worker
|
|
42
|
+
**CDN**: Static assets, edge caching, geographic distribution
|
|
43
|
+
|
|
44
|
+
## React Performance
|
|
45
|
+
|
|
46
|
+
### Avoid Re-renders
|
|
47
|
+
**Identify**: React DevTools Profiler, "Why did you render" library
|
|
48
|
+
|
|
49
|
+
**Fix**: React.memo (pure components), useMemo (expensive computations), useCallback (function props), move static data outside component
|
|
50
|
+
|
|
51
|
+
### Virtualization
|
|
52
|
+
**Problem**: 10,000+ items
|
|
53
|
+
**Solution**: react-window / react-virtualized (render only visible)
|
|
54
|
+
|
|
55
|
+
### Debounce/Throttle
|
|
56
|
+
- **Debounce**: Wait for user to stop (search input)
|
|
57
|
+
- **Throttle**: Limit frequency (scroll handler)
|
|
58
|
+
|
|
59
|
+
## Backend Performance
|
|
60
|
+
|
|
61
|
+
### Database Optimization
|
|
62
|
+
|
|
63
|
+
**Indexes**: Index WHERE/JOIN/ORDER BY columns, composite for multi-column, don't over-index (slows writes)
|
|
64
|
+
|
|
65
|
+
**Queries**: EXPLAIN to analyze, avoid SELECT *, LIMIT for pagination, connection pooling, batch operations
|
|
66
|
+
|
|
67
|
+
**N+1 Problem**: See sql.md for patterns
|
|
68
|
+
|
|
69
|
+
### Caching Strategy
|
|
70
|
+
|
|
71
|
+
**What**: Query results, API responses, computed values, sessions
|
|
72
|
+
**Invalidation**: Time-based (TTL), event-based (on update), hybrid
|
|
73
|
+
**Layers**: App memory (fastest) → Redis → DB cache → CDN
|
|
74
|
+
|
|
75
|
+
### Async Processing
|
|
76
|
+
|
|
77
|
+
**Background**: Email, image processing, reports, aggregation
|
|
78
|
+
**Tools**: Job queues (Bull, BullMQ), message queues (RabbitMQ, Kafka), serverless
|
|
79
|
+
|
|
80
|
+
### Response Time
|
|
81
|
+
- Gzip compression
|
|
82
|
+
- HTTP/2 multiplexing
|
|
83
|
+
- Keep-alive connections
|
|
84
|
+
- Parallel requests
|
|
85
|
+
|
|
86
|
+
## Database Performance
|
|
87
|
+
|
|
88
|
+
### Connection Management
|
|
89
|
+
- Connection pooling (reuse)
|
|
90
|
+
- Configure pool size
|
|
91
|
+
- Monitor usage
|
|
92
|
+
- Close idle connections
|
|
93
|
+
|
|
94
|
+
### Query Performance
|
|
95
|
+
**Slow query log**: Identify > 100ms, add indexes, rewrite, consider denormalization
|
|
96
|
+
|
|
97
|
+
**Pagination**: See sql.md for cursor-based vs offset patterns
|
|
98
|
+
|
|
99
|
+
### Scaling
|
|
100
|
+
**Vertical**: Bigger server (limited)
|
|
101
|
+
**Horizontal**: Read replicas (scale reads), sharding (partition data), DB-per-service
|
|
102
|
+
|
|
103
|
+
## Monitoring
|
|
104
|
+
|
|
105
|
+
**Frontend**: RUM, synthetic monitoring, Core Web Vitals, error tracking (Sentry)
|
|
106
|
+
**Backend**: APM, log aggregation (ELK, Datadog), alerting, distributed tracing
|
|
107
|
+
|
|
108
|
+
**Continuous**:
|
|
109
|
+
1. Set budgets
|
|
110
|
+
2. Monitor metrics
|
|
111
|
+
3. Alert on regression
|
|
112
|
+
4. Profile bottlenecks
|
|
113
|
+
5. Optimize
|
|
114
|
+
6. Measure impact
|
|
115
|
+
|
|
116
|
+
## Common Pitfalls
|
|
117
|
+
|
|
118
|
+
❌ **Premature optimization**: Optimize AFTER measuring, focus on biggest bottlenecks, 80/20 rule
|
|
119
|
+
❌ **Over-caching**: Invalidation is hard, stale data bugs, memory limits → Cache stable, expensive data only
|
|
120
|
+
❌ **Ignoring network**: Minimize requests, reduce payload, use HTTP/2, consider latency
|
|
121
|
+
❌ **Blocking operations**: Never block event loop (Node), use async for I/O, worker threads for CPU tasks
|