start-vibing 2.0.11 → 2.0.13
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/README.md +177 -177
- package/dist/cli.js +19 -2
- package/package.json +42 -42
- package/template/.claude/CLAUDE.md +174 -174
- package/template/.claude/agents/01-orchestration/agent-selector.md +130 -130
- package/template/.claude/agents/01-orchestration/checkpoint-manager.md +142 -142
- package/template/.claude/agents/01-orchestration/context-manager.md +138 -138
- package/template/.claude/agents/01-orchestration/error-recovery.md +182 -182
- package/template/.claude/agents/01-orchestration/orchestrator.md +114 -114
- package/template/.claude/agents/01-orchestration/parallel-coordinator.md +141 -141
- package/template/.claude/agents/01-orchestration/task-decomposer.md +121 -121
- package/template/.claude/agents/01-orchestration/workflow-router.md +114 -114
- package/template/.claude/agents/02-typescript/bun-runtime-expert.md +197 -197
- package/template/.claude/agents/02-typescript/esm-resolver.md +193 -193
- package/template/.claude/agents/02-typescript/import-alias-enforcer.md +158 -158
- package/template/.claude/agents/02-typescript/ts-generics-helper.md +183 -183
- package/template/.claude/agents/02-typescript/ts-migration-helper.md +238 -238
- package/template/.claude/agents/02-typescript/ts-strict-checker.md +180 -180
- package/template/.claude/agents/02-typescript/ts-types-analyzer.md +199 -199
- package/template/.claude/agents/02-typescript/type-definition-writer.md +187 -187
- package/template/.claude/agents/02-typescript/zod-schema-designer.md +212 -212
- package/template/.claude/agents/02-typescript/zod-validator.md +158 -158
- package/template/.claude/agents/03-testing/playwright-assertions.md +265 -265
- package/template/.claude/agents/03-testing/playwright-e2e.md +247 -247
- package/template/.claude/agents/03-testing/playwright-fixtures.md +234 -234
- package/template/.claude/agents/03-testing/playwright-multi-viewport.md +256 -256
- package/template/.claude/agents/03-testing/playwright-page-objects.md +247 -247
- package/template/.claude/agents/03-testing/test-cleanup-manager.md +248 -248
- package/template/.claude/agents/03-testing/test-data-generator.md +254 -254
- package/template/.claude/agents/03-testing/tester-integration.md +278 -278
- package/template/.claude/agents/03-testing/tester-unit.md +207 -207
- package/template/.claude/agents/03-testing/vitest-config.md +287 -287
- package/template/.claude/agents/04-docker/container-health.md +255 -255
- package/template/.claude/agents/04-docker/deployment-validator.md +225 -225
- package/template/.claude/agents/04-docker/docker-compose-designer.md +281 -281
- package/template/.claude/agents/04-docker/docker-env-manager.md +235 -235
- package/template/.claude/agents/04-docker/docker-multi-stage.md +241 -241
- package/template/.claude/agents/04-docker/dockerfile-optimizer.md +208 -208
- package/template/.claude/agents/05-database/database-seeder.md +273 -273
- package/template/.claude/agents/05-database/mongodb-query-optimizer.md +230 -230
- package/template/.claude/agents/05-database/mongoose-aggregation.md +306 -306
- package/template/.claude/agents/05-database/mongoose-index-optimizer.md +182 -182
- package/template/.claude/agents/05-database/mongoose-schema-designer.md +267 -267
- package/template/.claude/agents/06-security/auth-session-validator.md +68 -68
- package/template/.claude/agents/06-security/input-sanitizer.md +80 -80
- package/template/.claude/agents/06-security/owasp-checker.md +97 -97
- package/template/.claude/agents/06-security/permission-auditor.md +100 -100
- package/template/.claude/agents/06-security/security-auditor.md +84 -84
- package/template/.claude/agents/06-security/sensitive-data-scanner.md +83 -83
- package/template/.claude/agents/07-documentation/api-documenter.md +136 -136
- package/template/.claude/agents/07-documentation/changelog-manager.md +105 -105
- package/template/.claude/agents/07-documentation/documenter.md +76 -76
- package/template/.claude/agents/07-documentation/domain-updater.md +81 -81
- package/template/.claude/agents/07-documentation/jsdoc-generator.md +114 -114
- package/template/.claude/agents/07-documentation/readme-generator.md +135 -135
- package/template/.claude/agents/08-git/branch-manager.md +58 -58
- package/template/.claude/agents/08-git/commit-manager.md +63 -63
- package/template/.claude/agents/08-git/pr-creator.md +76 -76
- package/template/.claude/agents/09-quality/code-reviewer.md +71 -71
- package/template/.claude/agents/09-quality/quality-checker.md +67 -67
- package/template/.claude/agents/10-research/best-practices-finder.md +89 -89
- package/template/.claude/agents/10-research/competitor-analyzer.md +106 -106
- package/template/.claude/agents/10-research/pattern-researcher.md +93 -93
- package/template/.claude/agents/10-research/research-cache-manager.md +76 -76
- package/template/.claude/agents/10-research/research-web.md +98 -98
- package/template/.claude/agents/10-research/tech-evaluator.md +101 -101
- package/template/.claude/agents/11-ui-ux/accessibility-auditor.md +136 -136
- package/template/.claude/agents/11-ui-ux/design-system-enforcer.md +125 -125
- package/template/.claude/agents/11-ui-ux/skeleton-generator.md +118 -118
- package/template/.claude/agents/11-ui-ux/ui-desktop.md +132 -132
- package/template/.claude/agents/11-ui-ux/ui-mobile.md +98 -98
- package/template/.claude/agents/11-ui-ux/ui-tablet.md +110 -110
- package/template/.claude/agents/12-performance/api-latency-analyzer.md +156 -156
- package/template/.claude/agents/12-performance/bundle-analyzer.md +113 -113
- package/template/.claude/agents/12-performance/memory-leak-detector.md +137 -137
- package/template/.claude/agents/12-performance/performance-profiler.md +115 -115
- package/template/.claude/agents/12-performance/query-optimizer.md +124 -124
- package/template/.claude/agents/12-performance/render-optimizer.md +154 -154
- package/template/.claude/agents/13-debugging/build-error-fixer.md +207 -207
- package/template/.claude/agents/13-debugging/debugger.md +149 -149
- package/template/.claude/agents/13-debugging/error-stack-analyzer.md +141 -141
- package/template/.claude/agents/13-debugging/network-debugger.md +208 -208
- package/template/.claude/agents/13-debugging/runtime-error-fixer.md +181 -181
- package/template/.claude/agents/13-debugging/type-error-resolver.md +185 -185
- package/template/.claude/agents/14-validation/final-validator.md +93 -93
- package/template/.claude/agents/_backup/analyzer.md +134 -134
- package/template/.claude/agents/_backup/code-reviewer.md +279 -279
- package/template/.claude/agents/_backup/commit-manager.md +219 -219
- package/template/.claude/agents/_backup/debugger.md +280 -280
- package/template/.claude/agents/_backup/documenter.md +237 -237
- package/template/.claude/agents/_backup/domain-updater.md +197 -197
- package/template/.claude/agents/_backup/final-validator.md +169 -169
- package/template/.claude/agents/_backup/orchestrator.md +149 -149
- package/template/.claude/agents/_backup/performance.md +232 -232
- package/template/.claude/agents/_backup/quality-checker.md +240 -240
- package/template/.claude/agents/_backup/research.md +315 -315
- package/template/.claude/agents/_backup/security-auditor.md +192 -192
- package/template/.claude/agents/_backup/tester.md +566 -566
- package/template/.claude/agents/_backup/ui-ux-reviewer.md +247 -247
- package/template/.claude/config/README.md +30 -30
- package/template/.claude/config/mcp-config.json +344 -344
- package/template/.claude/config/project-config.json +53 -53
- package/template/.claude/config/quality-gates.json +46 -46
- package/template/.claude/config/security-rules.json +45 -45
- package/template/.claude/config/testing-config.json +164 -164
- package/template/.claude/hooks/SETUP.md +126 -126
- package/template/.claude/hooks/run-hook.ts +176 -176
- package/template/.claude/hooks/stop-validator.ts +914 -824
- package/template/.claude/hooks/user-prompt-submit.ts +886 -886
- package/template/.claude/scripts/mcp-quick-install.ts +151 -151
- package/template/.claude/scripts/setup-mcps.ts +651 -651
- package/template/.claude/settings.json +275 -275
- package/template/.claude/skills/bun-runtime/SKILL.md +430 -430
- package/template/.claude/skills/codebase-knowledge/domains/claude-system.md +431 -431
- package/template/.claude/skills/codebase-knowledge/domains/mcp-integration.md +295 -295
- package/template/.claude/skills/debugging-patterns/SKILL.md +485 -485
- package/template/.claude/skills/docker-patterns/SKILL.md +555 -555
- package/template/.claude/skills/git-workflow/SKILL.md +454 -454
- package/template/.claude/skills/mongoose-patterns/SKILL.md +499 -499
- package/template/.claude/skills/nextjs-app-router/SKILL.md +327 -327
- package/template/.claude/skills/performance-patterns/SKILL.md +547 -547
- package/template/.claude/skills/playwright-automation/SKILL.md +438 -438
- package/template/.claude/skills/react-patterns/SKILL.md +389 -389
- package/template/.claude/skills/research-cache/SKILL.md +222 -222
- package/template/.claude/skills/shadcn-ui/SKILL.md +511 -511
- package/template/.claude/skills/tailwind-patterns/SKILL.md +465 -465
- package/template/.claude/skills/test-coverage/SKILL.md +467 -467
- package/template/.claude/skills/trpc-api/SKILL.md +434 -434
- package/template/.claude/skills/typescript-strict/SKILL.md +367 -367
- package/template/.claude/skills/zod-validation/SKILL.md +403 -403
- package/template/CLAUDE.md +117 -117
|
@@ -1,547 +1,547 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: performance-patterns
|
|
3
|
-
description: Performance profiling and optimization patterns. React optimization, bundle analysis, memory leaks, API latency, database queries. Use when optimizing application performance.
|
|
4
|
-
allowed-tools: Read, Write, Edit, Bash, Grep, Glob
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Performance Patterns - Optimization Best Practices
|
|
8
|
-
|
|
9
|
-
## Purpose
|
|
10
|
-
|
|
11
|
-
Expert guidance for performance:
|
|
12
|
-
|
|
13
|
-
- **React Optimization** - Re-renders, memoization, lazy loading
|
|
14
|
-
- **Bundle Analysis** - Code splitting, tree shaking
|
|
15
|
-
- **Memory Management** - Leak detection and prevention
|
|
16
|
-
- **API Performance** - Latency reduction, caching
|
|
17
|
-
- **Database Optimization** - Query efficiency, indexing
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## React Performance
|
|
22
|
-
|
|
23
|
-
### Prevent Unnecessary Re-renders
|
|
24
|
-
|
|
25
|
-
```typescript
|
|
26
|
-
// WRONG - New object on every render
|
|
27
|
-
<Component style={{ color: 'red' }} />
|
|
28
|
-
|
|
29
|
-
// CORRECT - Stable reference
|
|
30
|
-
const style = useMemo(() => ({ color: 'red' }), []);
|
|
31
|
-
<Component style={style} />
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### React.memo for Pure Components
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
// Memoize component that receives stable props
|
|
38
|
-
const UserCard = React.memo(function UserCard({ user }: { user: User }) {
|
|
39
|
-
return (
|
|
40
|
-
<div>
|
|
41
|
-
<h2>{user.name}</h2>
|
|
42
|
-
<p>{user.email}</p>
|
|
43
|
-
</div>
|
|
44
|
-
);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
// Custom comparison for complex props
|
|
48
|
-
const ExpensiveList = React.memo(
|
|
49
|
-
function ExpensiveList({ items }: { items: Item[] }) {
|
|
50
|
-
return <>{items.map(item => <Item key={item.id} {...item} />)}</>;
|
|
51
|
-
},
|
|
52
|
-
(prev, next) => prev.items.length === next.items.length
|
|
53
|
-
);
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### useMemo for Expensive Computations
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
59
|
-
function Analytics({ data }: { data: DataPoint[] }) {
|
|
60
|
-
// Memoize expensive calculation
|
|
61
|
-
const statistics = useMemo(() => {
|
|
62
|
-
return {
|
|
63
|
-
total: data.reduce((sum, d) => sum + d.value, 0),
|
|
64
|
-
average: data.reduce((sum, d) => sum + d.value, 0) / data.length,
|
|
65
|
-
max: Math.max(...data.map(d => d.value)),
|
|
66
|
-
};
|
|
67
|
-
}, [data]);
|
|
68
|
-
|
|
69
|
-
return <StatsDisplay stats={statistics} />;
|
|
70
|
-
}
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### useCallback for Event Handlers
|
|
74
|
-
|
|
75
|
-
```typescript
|
|
76
|
-
function TodoList({ todos, onToggle }: Props) {
|
|
77
|
-
// Stable callback reference
|
|
78
|
-
const handleToggle = useCallback((id: string) => {
|
|
79
|
-
onToggle(id);
|
|
80
|
-
}, [onToggle]);
|
|
81
|
-
|
|
82
|
-
return todos.map(todo => (
|
|
83
|
-
<TodoItem key={todo.id} todo={todo} onToggle={handleToggle} />
|
|
84
|
-
));
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
### Lazy Loading Components
|
|
89
|
-
|
|
90
|
-
```typescript
|
|
91
|
-
import { lazy, Suspense } from 'react';
|
|
92
|
-
|
|
93
|
-
// Lazy load heavy components
|
|
94
|
-
const HeavyChart = lazy(() => import('./components/HeavyChart'));
|
|
95
|
-
const AdminPanel = lazy(() => import('./components/AdminPanel'));
|
|
96
|
-
|
|
97
|
-
function App() {
|
|
98
|
-
return (
|
|
99
|
-
<Suspense fallback={<LoadingSpinner />}>
|
|
100
|
-
<HeavyChart data={chartData} />
|
|
101
|
-
</Suspense>
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### Virtual Lists for Large Data
|
|
107
|
-
|
|
108
|
-
```typescript
|
|
109
|
-
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
110
|
-
|
|
111
|
-
function VirtualList({ items }: { items: Item[] }) {
|
|
112
|
-
const parentRef = useRef<HTMLDivElement>(null);
|
|
113
|
-
|
|
114
|
-
const virtualizer = useVirtualizer({
|
|
115
|
-
count: items.length,
|
|
116
|
-
getScrollElement: () => parentRef.current,
|
|
117
|
-
estimateSize: () => 50,
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
return (
|
|
121
|
-
<div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
|
|
122
|
-
<div style={{ height: `${virtualizer.getTotalSize()}px`, position: 'relative' }}>
|
|
123
|
-
{virtualizer.getVirtualItems().map((virtualItem) => (
|
|
124
|
-
<div
|
|
125
|
-
key={virtualItem.key}
|
|
126
|
-
style={{
|
|
127
|
-
position: 'absolute',
|
|
128
|
-
top: 0,
|
|
129
|
-
left: 0,
|
|
130
|
-
width: '100%',
|
|
131
|
-
height: `${virtualItem.size}px`,
|
|
132
|
-
transform: `translateY(${virtualItem.start}px)`,
|
|
133
|
-
}}
|
|
134
|
-
>
|
|
135
|
-
<ItemRow item={items[virtualItem.index]} />
|
|
136
|
-
</div>
|
|
137
|
-
))}
|
|
138
|
-
</div>
|
|
139
|
-
</div>
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
---
|
|
145
|
-
|
|
146
|
-
## Bundle Optimization
|
|
147
|
-
|
|
148
|
-
### Code Splitting
|
|
149
|
-
|
|
150
|
-
```typescript
|
|
151
|
-
// Route-based splitting
|
|
152
|
-
const routes = [
|
|
153
|
-
{
|
|
154
|
-
path: '/dashboard',
|
|
155
|
-
component: lazy(() => import('./pages/Dashboard')),
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
path: '/settings',
|
|
159
|
-
component: lazy(() => import('./pages/Settings')),
|
|
160
|
-
},
|
|
161
|
-
];
|
|
162
|
-
|
|
163
|
-
// Feature-based splitting
|
|
164
|
-
const HeavyEditor = lazy(() => import(/* webpackChunkName: "editor" */ './components/HeavyEditor'));
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
### Bundle Analysis
|
|
168
|
-
|
|
169
|
-
```bash
|
|
170
|
-
# Analyze bundle size
|
|
171
|
-
bunx vite-bundle-analyzer
|
|
172
|
-
|
|
173
|
-
# Alternative: source-map-explorer
|
|
174
|
-
bunx source-map-explorer dist/assets/*.js
|
|
175
|
-
|
|
176
|
-
# Check specific package size
|
|
177
|
-
bunx bundlephobia zod
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### Tree Shaking
|
|
181
|
-
|
|
182
|
-
```typescript
|
|
183
|
-
// WRONG - Imports entire library
|
|
184
|
-
import _ from 'lodash';
|
|
185
|
-
const result = _.debounce(fn, 300);
|
|
186
|
-
|
|
187
|
-
// CORRECT - Import only what you need
|
|
188
|
-
import debounce from 'lodash/debounce';
|
|
189
|
-
const result = debounce(fn, 300);
|
|
190
|
-
|
|
191
|
-
// BEST - Use native or smaller alternative
|
|
192
|
-
function debounce<T extends (...args: any[]) => any>(fn: T, ms: number) {
|
|
193
|
-
let timeoutId: ReturnType<typeof setTimeout>;
|
|
194
|
-
return (...args: Parameters<T>) => {
|
|
195
|
-
clearTimeout(timeoutId);
|
|
196
|
-
timeoutId = setTimeout(() => fn(...args), ms);
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
---
|
|
202
|
-
|
|
203
|
-
## Memory Leak Prevention
|
|
204
|
-
|
|
205
|
-
### Common Leak Patterns
|
|
206
|
-
|
|
207
|
-
```typescript
|
|
208
|
-
// LEAK - Event listener not removed
|
|
209
|
-
useEffect(() => {
|
|
210
|
-
window.addEventListener('resize', handleResize);
|
|
211
|
-
// Missing cleanup!
|
|
212
|
-
}, []);
|
|
213
|
-
|
|
214
|
-
// FIXED - Proper cleanup
|
|
215
|
-
useEffect(() => {
|
|
216
|
-
window.addEventListener('resize', handleResize);
|
|
217
|
-
return () => window.removeEventListener('resize', handleResize);
|
|
218
|
-
}, []);
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
### Abort Controllers for Fetch
|
|
222
|
-
|
|
223
|
-
```typescript
|
|
224
|
-
useEffect(() => {
|
|
225
|
-
const controller = new AbortController();
|
|
226
|
-
|
|
227
|
-
async function fetchData() {
|
|
228
|
-
try {
|
|
229
|
-
const response = await fetch('/api/data', {
|
|
230
|
-
signal: controller.signal,
|
|
231
|
-
});
|
|
232
|
-
const data = await response.json();
|
|
233
|
-
setData(data);
|
|
234
|
-
} catch (error) {
|
|
235
|
-
if (error instanceof Error && error.name === 'AbortError') {
|
|
236
|
-
return; // Ignore abort errors
|
|
237
|
-
}
|
|
238
|
-
throw error;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
fetchData();
|
|
243
|
-
|
|
244
|
-
return () => controller.abort();
|
|
245
|
-
}, []);
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
### Closure Leaks
|
|
249
|
-
|
|
250
|
-
```typescript
|
|
251
|
-
// LEAK - Timer holds reference after unmount
|
|
252
|
-
function Component() {
|
|
253
|
-
const [count, setCount] = useState(0);
|
|
254
|
-
|
|
255
|
-
useEffect(() => {
|
|
256
|
-
const id = setInterval(() => {
|
|
257
|
-
setCount((c) => c + 1); // Uses stale closure
|
|
258
|
-
}, 1000);
|
|
259
|
-
return () => clearInterval(id); // MUST cleanup
|
|
260
|
-
}, []);
|
|
261
|
-
}
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
### WeakMap for Object References
|
|
265
|
-
|
|
266
|
-
```typescript
|
|
267
|
-
// Use WeakMap to avoid holding strong references
|
|
268
|
-
const cache = new WeakMap<object, ComputedValue>();
|
|
269
|
-
|
|
270
|
-
function getComputed(obj: object): ComputedValue {
|
|
271
|
-
if (cache.has(obj)) {
|
|
272
|
-
return cache.get(obj)!;
|
|
273
|
-
}
|
|
274
|
-
const computed = expensiveComputation(obj);
|
|
275
|
-
cache.set(obj, computed);
|
|
276
|
-
return computed;
|
|
277
|
-
}
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
---
|
|
281
|
-
|
|
282
|
-
## API Latency Optimization
|
|
283
|
-
|
|
284
|
-
### Response Caching
|
|
285
|
-
|
|
286
|
-
```typescript
|
|
287
|
-
// In-memory cache with TTL
|
|
288
|
-
const cache = new Map<string, { data: unknown; expires: number }>();
|
|
289
|
-
|
|
290
|
-
async function cachedFetch<T>(url: string, ttl = 60000): Promise<T> {
|
|
291
|
-
const cached = cache.get(url);
|
|
292
|
-
if (cached && cached.expires > Date.now()) {
|
|
293
|
-
return cached.data as T;
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const response = await fetch(url);
|
|
297
|
-
const data = await response.json();
|
|
298
|
-
|
|
299
|
-
cache.set(url, { data, expires: Date.now() + ttl });
|
|
300
|
-
return data;
|
|
301
|
-
}
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
### Request Deduplication
|
|
305
|
-
|
|
306
|
-
```typescript
|
|
307
|
-
const pending = new Map<string, Promise<Response>>();
|
|
308
|
-
|
|
309
|
-
async function dedupedFetch(url: string): Promise<Response> {
|
|
310
|
-
if (pending.has(url)) {
|
|
311
|
-
return pending.get(url)!;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
const promise = fetch(url).finally(() => {
|
|
315
|
-
pending.delete(url);
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
pending.set(url, promise);
|
|
319
|
-
return promise;
|
|
320
|
-
}
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
### Parallel Requests
|
|
324
|
-
|
|
325
|
-
```typescript
|
|
326
|
-
// SLOW - Sequential requests
|
|
327
|
-
const user = await fetchUser(id);
|
|
328
|
-
const posts = await fetchPosts(id);
|
|
329
|
-
const comments = await fetchComments(id);
|
|
330
|
-
|
|
331
|
-
// FAST - Parallel requests
|
|
332
|
-
const [user, posts, comments] = await Promise.all([
|
|
333
|
-
fetchUser(id),
|
|
334
|
-
fetchPosts(id),
|
|
335
|
-
fetchComments(id),
|
|
336
|
-
]);
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
### Response Compression
|
|
340
|
-
|
|
341
|
-
```typescript
|
|
342
|
-
// Enable compression in server
|
|
343
|
-
import compression from 'compression';
|
|
344
|
-
app.use(compression());
|
|
345
|
-
|
|
346
|
-
// Or in Bun
|
|
347
|
-
Bun.serve({
|
|
348
|
-
fetch(request) {
|
|
349
|
-
const response = Response.json(largeData);
|
|
350
|
-
// Bun auto-compresses based on Accept-Encoding
|
|
351
|
-
return response;
|
|
352
|
-
},
|
|
353
|
-
});
|
|
354
|
-
```
|
|
355
|
-
|
|
356
|
-
---
|
|
357
|
-
|
|
358
|
-
## MongoDB Query Optimization
|
|
359
|
-
|
|
360
|
-
### Use Indexes
|
|
361
|
-
|
|
362
|
-
```typescript
|
|
363
|
-
// Create indexes for frequent queries
|
|
364
|
-
const userSchema = new Schema({
|
|
365
|
-
email: { type: String, unique: true, index: true },
|
|
366
|
-
createdAt: { type: Date, index: true },
|
|
367
|
-
status: { type: String, index: true },
|
|
368
|
-
});
|
|
369
|
-
|
|
370
|
-
// Compound index for common query pattern
|
|
371
|
-
userSchema.index({ status: 1, createdAt: -1 });
|
|
372
|
-
```
|
|
373
|
-
|
|
374
|
-
### Avoid N+1 Queries
|
|
375
|
-
|
|
376
|
-
```typescript
|
|
377
|
-
// WRONG - N+1 problem
|
|
378
|
-
const posts = await Post.find();
|
|
379
|
-
for (const post of posts) {
|
|
380
|
-
post.author = await User.findById(post.authorId);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// CORRECT - Use populate
|
|
384
|
-
const posts = await Post.find().populate('author');
|
|
385
|
-
|
|
386
|
-
// CORRECT - Manual batch fetch
|
|
387
|
-
const posts = await Post.find();
|
|
388
|
-
const authorIds = [...new Set(posts.map((p) => p.authorId))];
|
|
389
|
-
const authors = await User.find({ _id: { $in: authorIds } });
|
|
390
|
-
const authorMap = new Map(authors.map((a) => [a._id.toString(), a]));
|
|
391
|
-
posts.forEach((p) => (p.author = authorMap.get(p.authorId.toString())));
|
|
392
|
-
```
|
|
393
|
-
|
|
394
|
-
### Projection - Select Only Needed Fields
|
|
395
|
-
|
|
396
|
-
```typescript
|
|
397
|
-
// WRONG - Fetches all fields
|
|
398
|
-
const users = await User.find({ status: 'active' });
|
|
399
|
-
|
|
400
|
-
// CORRECT - Select only needed fields
|
|
401
|
-
const users = await User.find({ status: 'active' }).select('name email avatar').lean();
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
### Use .lean() for Read-Only
|
|
405
|
-
|
|
406
|
-
```typescript
|
|
407
|
-
// Returns plain JS objects (faster)
|
|
408
|
-
const users = await User.find().lean();
|
|
409
|
-
|
|
410
|
-
// vs Mongoose documents (slower, but has methods)
|
|
411
|
-
const users = await User.find();
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
### Aggregation Pipeline
|
|
415
|
-
|
|
416
|
-
```typescript
|
|
417
|
-
// Efficient aggregation
|
|
418
|
-
const stats = await Order.aggregate([
|
|
419
|
-
{ $match: { status: 'completed' } },
|
|
420
|
-
{
|
|
421
|
-
$group: {
|
|
422
|
-
_id: '$userId',
|
|
423
|
-
totalOrders: { $sum: 1 },
|
|
424
|
-
totalSpent: { $sum: '$amount' },
|
|
425
|
-
},
|
|
426
|
-
},
|
|
427
|
-
{ $sort: { totalSpent: -1 } },
|
|
428
|
-
{ $limit: 10 },
|
|
429
|
-
]);
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
---
|
|
433
|
-
|
|
434
|
-
## Profiling Tools
|
|
435
|
-
|
|
436
|
-
### React DevTools Profiler
|
|
437
|
-
|
|
438
|
-
```typescript
|
|
439
|
-
// Wrap component to profile
|
|
440
|
-
import { Profiler } from 'react';
|
|
441
|
-
|
|
442
|
-
function onRender(
|
|
443
|
-
id: string,
|
|
444
|
-
phase: 'mount' | 'update',
|
|
445
|
-
actualDuration: number,
|
|
446
|
-
baseDuration: number,
|
|
447
|
-
) {
|
|
448
|
-
console.log(`${id} ${phase}: ${actualDuration.toFixed(2)}ms`);
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
<Profiler id="ExpensiveComponent" onRender={onRender}>
|
|
452
|
-
<ExpensiveComponent />
|
|
453
|
-
</Profiler>
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
### Performance API
|
|
457
|
-
|
|
458
|
-
```typescript
|
|
459
|
-
// Measure operation time
|
|
460
|
-
performance.mark('fetch-start');
|
|
461
|
-
await fetchData();
|
|
462
|
-
performance.mark('fetch-end');
|
|
463
|
-
|
|
464
|
-
performance.measure('fetch-duration', 'fetch-start', 'fetch-end');
|
|
465
|
-
const measure = performance.getEntriesByName('fetch-duration')[0];
|
|
466
|
-
console.log(`Fetch took ${measure?.duration.toFixed(2)}ms`);
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
### MongoDB Query Explain
|
|
470
|
-
|
|
471
|
-
```bash
|
|
472
|
-
# In MongoDB shell
|
|
473
|
-
db.users.find({ email: "test@example.com" }).explain("executionStats")
|
|
474
|
-
|
|
475
|
-
# Check if using index
|
|
476
|
-
# "winningPlan.inputStage.stage" should be "IXSCAN" not "COLLSCAN"
|
|
477
|
-
```
|
|
478
|
-
|
|
479
|
-
---
|
|
480
|
-
|
|
481
|
-
## Core Web Vitals
|
|
482
|
-
|
|
483
|
-
### LCP (Largest Contentful Paint) < 2.5s
|
|
484
|
-
|
|
485
|
-
```typescript
|
|
486
|
-
// Preload critical resources
|
|
487
|
-
<link rel="preload" href="/hero-image.webp" as="image" />
|
|
488
|
-
|
|
489
|
-
// Use priority hints
|
|
490
|
-
<img src="/hero.webp" fetchpriority="high" />
|
|
491
|
-
```
|
|
492
|
-
|
|
493
|
-
### FID (First Input Delay) < 100ms
|
|
494
|
-
|
|
495
|
-
```typescript
|
|
496
|
-
// Break up long tasks
|
|
497
|
-
async function processLargeArray(items: Item[]) {
|
|
498
|
-
for (let i = 0; i < items.length; i += 100) {
|
|
499
|
-
const chunk = items.slice(i, i + 100);
|
|
500
|
-
await processChunk(chunk);
|
|
501
|
-
// Yield to main thread
|
|
502
|
-
await new Promise((r) => setTimeout(r, 0));
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
```
|
|
506
|
-
|
|
507
|
-
### CLS (Cumulative Layout Shift) < 0.1
|
|
508
|
-
|
|
509
|
-
```typescript
|
|
510
|
-
// Always set dimensions on images
|
|
511
|
-
<img src="/photo.jpg" width={800} height={600} alt="Photo" />
|
|
512
|
-
|
|
513
|
-
// Use aspect-ratio CSS
|
|
514
|
-
<div style={{ aspectRatio: '16/9' }}>
|
|
515
|
-
<img src="/video-thumb.jpg" />
|
|
516
|
-
</div>
|
|
517
|
-
```
|
|
518
|
-
|
|
519
|
-
---
|
|
520
|
-
|
|
521
|
-
## Agent Integration
|
|
522
|
-
|
|
523
|
-
This skill is used by:
|
|
524
|
-
|
|
525
|
-
- **performance-profiler** agent
|
|
526
|
-
- **bundle-analyzer** agent
|
|
527
|
-
- **memory-leak-detector** agent
|
|
528
|
-
- **api-latency-analyzer** agent
|
|
529
|
-
- **query-optimizer** agent
|
|
530
|
-
- **render-optimizer** agent
|
|
531
|
-
|
|
532
|
-
---
|
|
533
|
-
|
|
534
|
-
## FORBIDDEN
|
|
535
|
-
|
|
536
|
-
1. **Premature optimization** - Measure first, optimize second
|
|
537
|
-
2. **Missing cleanup in useEffect** - Always return cleanup function
|
|
538
|
-
3. **N+1 queries** - Use batch fetching or populate
|
|
539
|
-
4. **Fetching all fields** - Use projection/select
|
|
540
|
-
5. **Blocking main thread** - Use web workers for heavy computation
|
|
541
|
-
6. **Ignoring Core Web Vitals** - Monitor LCP, FID, CLS
|
|
542
|
-
|
|
543
|
-
---
|
|
544
|
-
|
|
545
|
-
## Version
|
|
546
|
-
|
|
547
|
-
- **v1.0.0** - Initial implementation based on 2024-2025 performance patterns
|
|
1
|
+
---
|
|
2
|
+
name: performance-patterns
|
|
3
|
+
description: Performance profiling and optimization patterns. React optimization, bundle analysis, memory leaks, API latency, database queries. Use when optimizing application performance.
|
|
4
|
+
allowed-tools: Read, Write, Edit, Bash, Grep, Glob
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Performance Patterns - Optimization Best Practices
|
|
8
|
+
|
|
9
|
+
## Purpose
|
|
10
|
+
|
|
11
|
+
Expert guidance for performance:
|
|
12
|
+
|
|
13
|
+
- **React Optimization** - Re-renders, memoization, lazy loading
|
|
14
|
+
- **Bundle Analysis** - Code splitting, tree shaking
|
|
15
|
+
- **Memory Management** - Leak detection and prevention
|
|
16
|
+
- **API Performance** - Latency reduction, caching
|
|
17
|
+
- **Database Optimization** - Query efficiency, indexing
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## React Performance
|
|
22
|
+
|
|
23
|
+
### Prevent Unnecessary Re-renders
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// WRONG - New object on every render
|
|
27
|
+
<Component style={{ color: 'red' }} />
|
|
28
|
+
|
|
29
|
+
// CORRECT - Stable reference
|
|
30
|
+
const style = useMemo(() => ({ color: 'red' }), []);
|
|
31
|
+
<Component style={style} />
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### React.memo for Pure Components
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// Memoize component that receives stable props
|
|
38
|
+
const UserCard = React.memo(function UserCard({ user }: { user: User }) {
|
|
39
|
+
return (
|
|
40
|
+
<div>
|
|
41
|
+
<h2>{user.name}</h2>
|
|
42
|
+
<p>{user.email}</p>
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Custom comparison for complex props
|
|
48
|
+
const ExpensiveList = React.memo(
|
|
49
|
+
function ExpensiveList({ items }: { items: Item[] }) {
|
|
50
|
+
return <>{items.map(item => <Item key={item.id} {...item} />)}</>;
|
|
51
|
+
},
|
|
52
|
+
(prev, next) => prev.items.length === next.items.length
|
|
53
|
+
);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### useMemo for Expensive Computations
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
function Analytics({ data }: { data: DataPoint[] }) {
|
|
60
|
+
// Memoize expensive calculation
|
|
61
|
+
const statistics = useMemo(() => {
|
|
62
|
+
return {
|
|
63
|
+
total: data.reduce((sum, d) => sum + d.value, 0),
|
|
64
|
+
average: data.reduce((sum, d) => sum + d.value, 0) / data.length,
|
|
65
|
+
max: Math.max(...data.map(d => d.value)),
|
|
66
|
+
};
|
|
67
|
+
}, [data]);
|
|
68
|
+
|
|
69
|
+
return <StatsDisplay stats={statistics} />;
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### useCallback for Event Handlers
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
function TodoList({ todos, onToggle }: Props) {
|
|
77
|
+
// Stable callback reference
|
|
78
|
+
const handleToggle = useCallback((id: string) => {
|
|
79
|
+
onToggle(id);
|
|
80
|
+
}, [onToggle]);
|
|
81
|
+
|
|
82
|
+
return todos.map(todo => (
|
|
83
|
+
<TodoItem key={todo.id} todo={todo} onToggle={handleToggle} />
|
|
84
|
+
));
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Lazy Loading Components
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
import { lazy, Suspense } from 'react';
|
|
92
|
+
|
|
93
|
+
// Lazy load heavy components
|
|
94
|
+
const HeavyChart = lazy(() => import('./components/HeavyChart'));
|
|
95
|
+
const AdminPanel = lazy(() => import('./components/AdminPanel'));
|
|
96
|
+
|
|
97
|
+
function App() {
|
|
98
|
+
return (
|
|
99
|
+
<Suspense fallback={<LoadingSpinner />}>
|
|
100
|
+
<HeavyChart data={chartData} />
|
|
101
|
+
</Suspense>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Virtual Lists for Large Data
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
110
|
+
|
|
111
|
+
function VirtualList({ items }: { items: Item[] }) {
|
|
112
|
+
const parentRef = useRef<HTMLDivElement>(null);
|
|
113
|
+
|
|
114
|
+
const virtualizer = useVirtualizer({
|
|
115
|
+
count: items.length,
|
|
116
|
+
getScrollElement: () => parentRef.current,
|
|
117
|
+
estimateSize: () => 50,
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
return (
|
|
121
|
+
<div ref={parentRef} style={{ height: '400px', overflow: 'auto' }}>
|
|
122
|
+
<div style={{ height: `${virtualizer.getTotalSize()}px`, position: 'relative' }}>
|
|
123
|
+
{virtualizer.getVirtualItems().map((virtualItem) => (
|
|
124
|
+
<div
|
|
125
|
+
key={virtualItem.key}
|
|
126
|
+
style={{
|
|
127
|
+
position: 'absolute',
|
|
128
|
+
top: 0,
|
|
129
|
+
left: 0,
|
|
130
|
+
width: '100%',
|
|
131
|
+
height: `${virtualItem.size}px`,
|
|
132
|
+
transform: `translateY(${virtualItem.start}px)`,
|
|
133
|
+
}}
|
|
134
|
+
>
|
|
135
|
+
<ItemRow item={items[virtualItem.index]} />
|
|
136
|
+
</div>
|
|
137
|
+
))}
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Bundle Optimization
|
|
147
|
+
|
|
148
|
+
### Code Splitting
|
|
149
|
+
|
|
150
|
+
```typescript
|
|
151
|
+
// Route-based splitting
|
|
152
|
+
const routes = [
|
|
153
|
+
{
|
|
154
|
+
path: '/dashboard',
|
|
155
|
+
component: lazy(() => import('./pages/Dashboard')),
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
path: '/settings',
|
|
159
|
+
component: lazy(() => import('./pages/Settings')),
|
|
160
|
+
},
|
|
161
|
+
];
|
|
162
|
+
|
|
163
|
+
// Feature-based splitting
|
|
164
|
+
const HeavyEditor = lazy(() => import(/* webpackChunkName: "editor" */ './components/HeavyEditor'));
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Bundle Analysis
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# Analyze bundle size
|
|
171
|
+
bunx vite-bundle-analyzer
|
|
172
|
+
|
|
173
|
+
# Alternative: source-map-explorer
|
|
174
|
+
bunx source-map-explorer dist/assets/*.js
|
|
175
|
+
|
|
176
|
+
# Check specific package size
|
|
177
|
+
bunx bundlephobia zod
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### Tree Shaking
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
// WRONG - Imports entire library
|
|
184
|
+
import _ from 'lodash';
|
|
185
|
+
const result = _.debounce(fn, 300);
|
|
186
|
+
|
|
187
|
+
// CORRECT - Import only what you need
|
|
188
|
+
import debounce from 'lodash/debounce';
|
|
189
|
+
const result = debounce(fn, 300);
|
|
190
|
+
|
|
191
|
+
// BEST - Use native or smaller alternative
|
|
192
|
+
function debounce<T extends (...args: any[]) => any>(fn: T, ms: number) {
|
|
193
|
+
let timeoutId: ReturnType<typeof setTimeout>;
|
|
194
|
+
return (...args: Parameters<T>) => {
|
|
195
|
+
clearTimeout(timeoutId);
|
|
196
|
+
timeoutId = setTimeout(() => fn(...args), ms);
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## Memory Leak Prevention
|
|
204
|
+
|
|
205
|
+
### Common Leak Patterns
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// LEAK - Event listener not removed
|
|
209
|
+
useEffect(() => {
|
|
210
|
+
window.addEventListener('resize', handleResize);
|
|
211
|
+
// Missing cleanup!
|
|
212
|
+
}, []);
|
|
213
|
+
|
|
214
|
+
// FIXED - Proper cleanup
|
|
215
|
+
useEffect(() => {
|
|
216
|
+
window.addEventListener('resize', handleResize);
|
|
217
|
+
return () => window.removeEventListener('resize', handleResize);
|
|
218
|
+
}, []);
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Abort Controllers for Fetch
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
useEffect(() => {
|
|
225
|
+
const controller = new AbortController();
|
|
226
|
+
|
|
227
|
+
async function fetchData() {
|
|
228
|
+
try {
|
|
229
|
+
const response = await fetch('/api/data', {
|
|
230
|
+
signal: controller.signal,
|
|
231
|
+
});
|
|
232
|
+
const data = await response.json();
|
|
233
|
+
setData(data);
|
|
234
|
+
} catch (error) {
|
|
235
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
236
|
+
return; // Ignore abort errors
|
|
237
|
+
}
|
|
238
|
+
throw error;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
fetchData();
|
|
243
|
+
|
|
244
|
+
return () => controller.abort();
|
|
245
|
+
}, []);
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### Closure Leaks
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
// LEAK - Timer holds reference after unmount
|
|
252
|
+
function Component() {
|
|
253
|
+
const [count, setCount] = useState(0);
|
|
254
|
+
|
|
255
|
+
useEffect(() => {
|
|
256
|
+
const id = setInterval(() => {
|
|
257
|
+
setCount((c) => c + 1); // Uses stale closure
|
|
258
|
+
}, 1000);
|
|
259
|
+
return () => clearInterval(id); // MUST cleanup
|
|
260
|
+
}, []);
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### WeakMap for Object References
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
// Use WeakMap to avoid holding strong references
|
|
268
|
+
const cache = new WeakMap<object, ComputedValue>();
|
|
269
|
+
|
|
270
|
+
function getComputed(obj: object): ComputedValue {
|
|
271
|
+
if (cache.has(obj)) {
|
|
272
|
+
return cache.get(obj)!;
|
|
273
|
+
}
|
|
274
|
+
const computed = expensiveComputation(obj);
|
|
275
|
+
cache.set(obj, computed);
|
|
276
|
+
return computed;
|
|
277
|
+
}
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
---
|
|
281
|
+
|
|
282
|
+
## API Latency Optimization
|
|
283
|
+
|
|
284
|
+
### Response Caching
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
// In-memory cache with TTL
|
|
288
|
+
const cache = new Map<string, { data: unknown; expires: number }>();
|
|
289
|
+
|
|
290
|
+
async function cachedFetch<T>(url: string, ttl = 60000): Promise<T> {
|
|
291
|
+
const cached = cache.get(url);
|
|
292
|
+
if (cached && cached.expires > Date.now()) {
|
|
293
|
+
return cached.data as T;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const response = await fetch(url);
|
|
297
|
+
const data = await response.json();
|
|
298
|
+
|
|
299
|
+
cache.set(url, { data, expires: Date.now() + ttl });
|
|
300
|
+
return data;
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Request Deduplication
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
const pending = new Map<string, Promise<Response>>();
|
|
308
|
+
|
|
309
|
+
async function dedupedFetch(url: string): Promise<Response> {
|
|
310
|
+
if (pending.has(url)) {
|
|
311
|
+
return pending.get(url)!;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const promise = fetch(url).finally(() => {
|
|
315
|
+
pending.delete(url);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
pending.set(url, promise);
|
|
319
|
+
return promise;
|
|
320
|
+
}
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Parallel Requests
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
// SLOW - Sequential requests
|
|
327
|
+
const user = await fetchUser(id);
|
|
328
|
+
const posts = await fetchPosts(id);
|
|
329
|
+
const comments = await fetchComments(id);
|
|
330
|
+
|
|
331
|
+
// FAST - Parallel requests
|
|
332
|
+
const [user, posts, comments] = await Promise.all([
|
|
333
|
+
fetchUser(id),
|
|
334
|
+
fetchPosts(id),
|
|
335
|
+
fetchComments(id),
|
|
336
|
+
]);
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
### Response Compression
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
// Enable compression in server
|
|
343
|
+
import compression from 'compression';
|
|
344
|
+
app.use(compression());
|
|
345
|
+
|
|
346
|
+
// Or in Bun
|
|
347
|
+
Bun.serve({
|
|
348
|
+
fetch(request) {
|
|
349
|
+
const response = Response.json(largeData);
|
|
350
|
+
// Bun auto-compresses based on Accept-Encoding
|
|
351
|
+
return response;
|
|
352
|
+
},
|
|
353
|
+
});
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## MongoDB Query Optimization
|
|
359
|
+
|
|
360
|
+
### Use Indexes
|
|
361
|
+
|
|
362
|
+
```typescript
|
|
363
|
+
// Create indexes for frequent queries
|
|
364
|
+
const userSchema = new Schema({
|
|
365
|
+
email: { type: String, unique: true, index: true },
|
|
366
|
+
createdAt: { type: Date, index: true },
|
|
367
|
+
status: { type: String, index: true },
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
// Compound index for common query pattern
|
|
371
|
+
userSchema.index({ status: 1, createdAt: -1 });
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Avoid N+1 Queries
|
|
375
|
+
|
|
376
|
+
```typescript
|
|
377
|
+
// WRONG - N+1 problem
|
|
378
|
+
const posts = await Post.find();
|
|
379
|
+
for (const post of posts) {
|
|
380
|
+
post.author = await User.findById(post.authorId);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
// CORRECT - Use populate
|
|
384
|
+
const posts = await Post.find().populate('author');
|
|
385
|
+
|
|
386
|
+
// CORRECT - Manual batch fetch
|
|
387
|
+
const posts = await Post.find();
|
|
388
|
+
const authorIds = [...new Set(posts.map((p) => p.authorId))];
|
|
389
|
+
const authors = await User.find({ _id: { $in: authorIds } });
|
|
390
|
+
const authorMap = new Map(authors.map((a) => [a._id.toString(), a]));
|
|
391
|
+
posts.forEach((p) => (p.author = authorMap.get(p.authorId.toString())));
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
### Projection - Select Only Needed Fields
|
|
395
|
+
|
|
396
|
+
```typescript
|
|
397
|
+
// WRONG - Fetches all fields
|
|
398
|
+
const users = await User.find({ status: 'active' });
|
|
399
|
+
|
|
400
|
+
// CORRECT - Select only needed fields
|
|
401
|
+
const users = await User.find({ status: 'active' }).select('name email avatar').lean();
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Use .lean() for Read-Only
|
|
405
|
+
|
|
406
|
+
```typescript
|
|
407
|
+
// Returns plain JS objects (faster)
|
|
408
|
+
const users = await User.find().lean();
|
|
409
|
+
|
|
410
|
+
// vs Mongoose documents (slower, but has methods)
|
|
411
|
+
const users = await User.find();
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Aggregation Pipeline
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
// Efficient aggregation
|
|
418
|
+
const stats = await Order.aggregate([
|
|
419
|
+
{ $match: { status: 'completed' } },
|
|
420
|
+
{
|
|
421
|
+
$group: {
|
|
422
|
+
_id: '$userId',
|
|
423
|
+
totalOrders: { $sum: 1 },
|
|
424
|
+
totalSpent: { $sum: '$amount' },
|
|
425
|
+
},
|
|
426
|
+
},
|
|
427
|
+
{ $sort: { totalSpent: -1 } },
|
|
428
|
+
{ $limit: 10 },
|
|
429
|
+
]);
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## Profiling Tools
|
|
435
|
+
|
|
436
|
+
### React DevTools Profiler
|
|
437
|
+
|
|
438
|
+
```typescript
|
|
439
|
+
// Wrap component to profile
|
|
440
|
+
import { Profiler } from 'react';
|
|
441
|
+
|
|
442
|
+
function onRender(
|
|
443
|
+
id: string,
|
|
444
|
+
phase: 'mount' | 'update',
|
|
445
|
+
actualDuration: number,
|
|
446
|
+
baseDuration: number,
|
|
447
|
+
) {
|
|
448
|
+
console.log(`${id} ${phase}: ${actualDuration.toFixed(2)}ms`);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
<Profiler id="ExpensiveComponent" onRender={onRender}>
|
|
452
|
+
<ExpensiveComponent />
|
|
453
|
+
</Profiler>
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
### Performance API
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
// Measure operation time
|
|
460
|
+
performance.mark('fetch-start');
|
|
461
|
+
await fetchData();
|
|
462
|
+
performance.mark('fetch-end');
|
|
463
|
+
|
|
464
|
+
performance.measure('fetch-duration', 'fetch-start', 'fetch-end');
|
|
465
|
+
const measure = performance.getEntriesByName('fetch-duration')[0];
|
|
466
|
+
console.log(`Fetch took ${measure?.duration.toFixed(2)}ms`);
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### MongoDB Query Explain
|
|
470
|
+
|
|
471
|
+
```bash
|
|
472
|
+
# In MongoDB shell
|
|
473
|
+
db.users.find({ email: "test@example.com" }).explain("executionStats")
|
|
474
|
+
|
|
475
|
+
# Check if using index
|
|
476
|
+
# "winningPlan.inputStage.stage" should be "IXSCAN" not "COLLSCAN"
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## Core Web Vitals
|
|
482
|
+
|
|
483
|
+
### LCP (Largest Contentful Paint) < 2.5s
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
// Preload critical resources
|
|
487
|
+
<link rel="preload" href="/hero-image.webp" as="image" />
|
|
488
|
+
|
|
489
|
+
// Use priority hints
|
|
490
|
+
<img src="/hero.webp" fetchpriority="high" />
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
### FID (First Input Delay) < 100ms
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
// Break up long tasks
|
|
497
|
+
async function processLargeArray(items: Item[]) {
|
|
498
|
+
for (let i = 0; i < items.length; i += 100) {
|
|
499
|
+
const chunk = items.slice(i, i + 100);
|
|
500
|
+
await processChunk(chunk);
|
|
501
|
+
// Yield to main thread
|
|
502
|
+
await new Promise((r) => setTimeout(r, 0));
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
### CLS (Cumulative Layout Shift) < 0.1
|
|
508
|
+
|
|
509
|
+
```typescript
|
|
510
|
+
// Always set dimensions on images
|
|
511
|
+
<img src="/photo.jpg" width={800} height={600} alt="Photo" />
|
|
512
|
+
|
|
513
|
+
// Use aspect-ratio CSS
|
|
514
|
+
<div style={{ aspectRatio: '16/9' }}>
|
|
515
|
+
<img src="/video-thumb.jpg" />
|
|
516
|
+
</div>
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
## Agent Integration
|
|
522
|
+
|
|
523
|
+
This skill is used by:
|
|
524
|
+
|
|
525
|
+
- **performance-profiler** agent
|
|
526
|
+
- **bundle-analyzer** agent
|
|
527
|
+
- **memory-leak-detector** agent
|
|
528
|
+
- **api-latency-analyzer** agent
|
|
529
|
+
- **query-optimizer** agent
|
|
530
|
+
- **render-optimizer** agent
|
|
531
|
+
|
|
532
|
+
---
|
|
533
|
+
|
|
534
|
+
## FORBIDDEN
|
|
535
|
+
|
|
536
|
+
1. **Premature optimization** - Measure first, optimize second
|
|
537
|
+
2. **Missing cleanup in useEffect** - Always return cleanup function
|
|
538
|
+
3. **N+1 queries** - Use batch fetching or populate
|
|
539
|
+
4. **Fetching all fields** - Use projection/select
|
|
540
|
+
5. **Blocking main thread** - Use web workers for heavy computation
|
|
541
|
+
6. **Ignoring Core Web Vitals** - Monitor LCP, FID, CLS
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
## Version
|
|
546
|
+
|
|
547
|
+
- **v1.0.0** - Initial implementation based on 2024-2025 performance patterns
|