agentic-team-templates 0.9.2 → 0.10.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.
- package/package.json +1 -1
- package/src/index.js +32 -0
- package/src/index.test.js +8 -0
- package/templates/javascript-expert/.cursorrules/language-deep-dive.md +245 -0
- package/templates/javascript-expert/.cursorrules/node-patterns.md +184 -0
- package/templates/javascript-expert/.cursorrules/overview.md +130 -0
- package/templates/javascript-expert/.cursorrules/performance.md +203 -0
- package/templates/javascript-expert/.cursorrules/react-patterns.md +249 -0
- package/templates/javascript-expert/.cursorrules/testing.md +403 -0
- package/templates/javascript-expert/.cursorrules/tooling.md +176 -0
- package/templates/javascript-expert/CLAUDE.md +448 -0
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# JavaScript Performance
|
|
2
|
+
|
|
3
|
+
Expert-level performance patterns for JavaScript across browser and server environments.
|
|
4
|
+
|
|
5
|
+
## Profiling First
|
|
6
|
+
|
|
7
|
+
Never optimize without data. Always profile before and after changes.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// Node.js profiling
|
|
11
|
+
// node --prof app.js
|
|
12
|
+
// node --prof-process isolate-*.log
|
|
13
|
+
|
|
14
|
+
// Browser: use Performance API
|
|
15
|
+
performance.mark('operation-start');
|
|
16
|
+
doExpensiveWork();
|
|
17
|
+
performance.mark('operation-end');
|
|
18
|
+
performance.measure('operation', 'operation-start', 'operation-end');
|
|
19
|
+
const duration = performance.getEntriesByName('operation')[0]!.duration;
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## V8 Optimization Patterns
|
|
23
|
+
|
|
24
|
+
### Monomorphic Functions
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
// Good: Consistent argument shapes (monomorphic — V8 optimizes)
|
|
28
|
+
function processItem(item: { id: string; value: number }) {
|
|
29
|
+
return item.id + item.value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
items.forEach(processItem); // All same shape — fast
|
|
33
|
+
|
|
34
|
+
// Bad: Polymorphic — different shapes deoptimize
|
|
35
|
+
function processAnything(item: any) {
|
|
36
|
+
return item.id + item.value;
|
|
37
|
+
}
|
|
38
|
+
// Called with { id, value }, { id, value, extra }, { id, value, other }
|
|
39
|
+
// V8 can't optimize — megamorphic IC
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Hidden Classes
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// Good: Initialize all properties in the same order
|
|
46
|
+
class Point {
|
|
47
|
+
x: number;
|
|
48
|
+
y: number;
|
|
49
|
+
constructor(x: number, y: number) {
|
|
50
|
+
this.x = x; // Always x first
|
|
51
|
+
this.y = y; // Always y second
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Bad: Conditional property assignment
|
|
56
|
+
function createPoint(x: number, y: number, z?: number) {
|
|
57
|
+
const p: any = {};
|
|
58
|
+
p.x = x;
|
|
59
|
+
if (z) p.z = z; // Different hidden class!
|
|
60
|
+
p.y = y;
|
|
61
|
+
return p;
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Data Structures
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
// Map vs Object
|
|
69
|
+
// Use Map when: keys are dynamic, non-string keys, frequent add/delete
|
|
70
|
+
const cache = new Map<string, CacheEntry>();
|
|
71
|
+
|
|
72
|
+
// Use Object when: static shape, string keys, serialization needed
|
|
73
|
+
const config = { host: 'localhost', port: 3000 };
|
|
74
|
+
|
|
75
|
+
// Set vs Array for membership tests
|
|
76
|
+
// O(1) lookup vs O(n)
|
|
77
|
+
const allowedIds = new Set(['a', 'b', 'c']);
|
|
78
|
+
if (allowedIds.has(id)) { ... }
|
|
79
|
+
|
|
80
|
+
// TypedArrays for numeric data
|
|
81
|
+
const buffer = new Float64Array(1000);
|
|
82
|
+
// 8x more memory efficient than number[]
|
|
83
|
+
// Faster iteration for numeric operations
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Memory Management
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
// Avoid memory leaks in event listeners
|
|
90
|
+
class Component {
|
|
91
|
+
#controller = new AbortController();
|
|
92
|
+
|
|
93
|
+
mount() {
|
|
94
|
+
window.addEventListener('resize', this.#onResize, {
|
|
95
|
+
signal: this.#controller.signal,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
unmount() {
|
|
100
|
+
this.#controller.abort(); // Removes all listeners at once
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
#onResize = () => { ... };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// WeakMap for metadata on objects without preventing GC
|
|
107
|
+
const metadata = new WeakMap<object, Metadata>();
|
|
108
|
+
|
|
109
|
+
function annotate(obj: object, data: Metadata) {
|
|
110
|
+
metadata.set(obj, data); // Won't prevent obj from being GC'd
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Avoid closures that capture large scopes
|
|
114
|
+
// Bad: Entire scope captured
|
|
115
|
+
function createHandler(largeData: BigObject[]) {
|
|
116
|
+
const id = largeData[0]!.id; // Only need id
|
|
117
|
+
return () => console.log(largeData); // Captures entire array!
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Good: Capture only what's needed
|
|
121
|
+
function createHandler(largeData: BigObject[]) {
|
|
122
|
+
const id = largeData[0]!.id;
|
|
123
|
+
return () => console.log(id); // Only captures id
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Async Performance
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
// Batch concurrent operations with limits
|
|
131
|
+
async function processInBatches<T, R>(
|
|
132
|
+
items: T[],
|
|
133
|
+
fn: (item: T) => Promise<R>,
|
|
134
|
+
batchSize: number,
|
|
135
|
+
): Promise<R[]> {
|
|
136
|
+
const results: R[] = [];
|
|
137
|
+
for (let i = 0; i < items.length; i += batchSize) {
|
|
138
|
+
const batch = items.slice(i, i + batchSize);
|
|
139
|
+
const batchResults = await Promise.all(batch.map(fn));
|
|
140
|
+
results.push(...batchResults);
|
|
141
|
+
}
|
|
142
|
+
return results;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Use streams for large data — don't load everything into memory
|
|
146
|
+
// See node-patterns.md for stream examples
|
|
147
|
+
|
|
148
|
+
// requestAnimationFrame for visual updates (browser)
|
|
149
|
+
function animateValue(element: HTMLElement, from: number, to: number) {
|
|
150
|
+
const start = performance.now();
|
|
151
|
+
const duration = 300;
|
|
152
|
+
|
|
153
|
+
const tick = (now: number) => {
|
|
154
|
+
const progress = Math.min((now - start) / duration, 1);
|
|
155
|
+
const value = from + (to - from) * progress;
|
|
156
|
+
element.textContent = String(Math.round(value));
|
|
157
|
+
if (progress < 1) requestAnimationFrame(tick);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
requestAnimationFrame(tick);
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Bundle Size (Client-Side)
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
// Dynamic imports for code splitting
|
|
168
|
+
const AdminPanel = lazy(() => import('./AdminPanel.js'));
|
|
169
|
+
|
|
170
|
+
// Tree-shakeable exports (named, not default)
|
|
171
|
+
export { validateEmail } from './validators.js';
|
|
172
|
+
|
|
173
|
+
// Avoid barrel file re-exports for large packages
|
|
174
|
+
// Bad: import { Button } from './components'; // Pulls everything
|
|
175
|
+
// Good: import { Button } from './components/Button.js';
|
|
176
|
+
|
|
177
|
+
// Check bundle impact before adding dependencies
|
|
178
|
+
// npx bundlephobia <package-name>
|
|
179
|
+
|
|
180
|
+
// Prefer native APIs over libraries
|
|
181
|
+
// Use Intl.DateTimeFormat instead of moment/date-fns for formatting
|
|
182
|
+
// Use structuredClone instead of lodash.cloneDeep
|
|
183
|
+
// Use URL instead of query-string
|
|
184
|
+
// Use AbortController instead of custom cancellation
|
|
185
|
+
// Use crypto.randomUUID() instead of uuid
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Anti-Patterns
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
// Never: Premature optimization
|
|
192
|
+
// Measure first, optimize the bottleneck, measure again
|
|
193
|
+
|
|
194
|
+
// Never: Micro-optimizations that harm readability
|
|
195
|
+
// arr[arr.length - 1] vs arr.at(-1) — the difference is negligible
|
|
196
|
+
|
|
197
|
+
// Never: Caching without invalidation strategy
|
|
198
|
+
// Every cache needs a TTL, max size, or explicit invalidation
|
|
199
|
+
|
|
200
|
+
// Never: Synchronous operations blocking the event loop
|
|
201
|
+
// Use worker threads for CPU work
|
|
202
|
+
// Use async I/O for file/network operations
|
|
203
|
+
```
|
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# React Patterns
|
|
2
|
+
|
|
3
|
+
Expert-level React patterns for building production UI applications.
|
|
4
|
+
|
|
5
|
+
## Component Design
|
|
6
|
+
|
|
7
|
+
### Server Components First (React 19+)
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
// Default: Server Components — no 'use client' directive
|
|
11
|
+
// They run on the server, have zero client bundle cost
|
|
12
|
+
async function ProjectList() {
|
|
13
|
+
const projects = await db.projects.findMany();
|
|
14
|
+
return (
|
|
15
|
+
<ul>
|
|
16
|
+
{projects.map(p => <ProjectCard key={p.id} project={p} />)}
|
|
17
|
+
</ul>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Only add 'use client' when you need:
|
|
22
|
+
// - useState, useEffect, useReducer
|
|
23
|
+
// - Browser APIs (window, document)
|
|
24
|
+
// - Event handlers (onClick, onChange)
|
|
25
|
+
// - Custom hooks that use client features
|
|
26
|
+
'use client';
|
|
27
|
+
function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
|
|
28
|
+
const [query, setQuery] = useState('');
|
|
29
|
+
return <input value={query} onChange={e => setQuery(e.target.value)} />;
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Composition Patterns
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
// Compound components for related UI
|
|
37
|
+
function Tabs({ children }: { children: React.ReactNode }) {
|
|
38
|
+
const [active, setActive] = useState(0);
|
|
39
|
+
return (
|
|
40
|
+
<TabsContext.Provider value={{ active, setActive }}>
|
|
41
|
+
{children}
|
|
42
|
+
</TabsContext.Provider>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
Tabs.List = function TabList({ children }: { children: React.ReactNode }) { ... };
|
|
47
|
+
Tabs.Panel = function TabPanel({ children, index }: { children: React.ReactNode; index: number }) { ... };
|
|
48
|
+
|
|
49
|
+
// Usage — clean, declarative API
|
|
50
|
+
<Tabs>
|
|
51
|
+
<Tabs.List>
|
|
52
|
+
<Tab>Overview</Tab>
|
|
53
|
+
<Tab>Details</Tab>
|
|
54
|
+
</Tabs.List>
|
|
55
|
+
<Tabs.Panel index={0}>Overview content</Tabs.Panel>
|
|
56
|
+
<Tabs.Panel index={1}>Details content</Tabs.Panel>
|
|
57
|
+
</Tabs>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Render Props and Children as Function
|
|
61
|
+
|
|
62
|
+
```tsx
|
|
63
|
+
// For maximum flexibility in rendering
|
|
64
|
+
interface DataLoaderProps<T> {
|
|
65
|
+
load: () => Promise<T>;
|
|
66
|
+
children: (data: T) => React.ReactNode;
|
|
67
|
+
fallback?: React.ReactNode;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function DataLoader<T>({ load, children, fallback }: DataLoaderProps<T>) {
|
|
71
|
+
const data = use(load());
|
|
72
|
+
return <>{children(data)}</>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Usage
|
|
76
|
+
<Suspense fallback={<Spinner />}>
|
|
77
|
+
<DataLoader load={fetchUsers}>
|
|
78
|
+
{users => <UserList users={users} />}
|
|
79
|
+
</DataLoader>
|
|
80
|
+
</Suspense>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Hooks
|
|
84
|
+
|
|
85
|
+
### Custom Hook Patterns
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
// Encapsulate complex state logic
|
|
89
|
+
function useOptimisticUpdate<T>(
|
|
90
|
+
current: T,
|
|
91
|
+
updateFn: (value: T) => Promise<void>,
|
|
92
|
+
) {
|
|
93
|
+
const [optimistic, setOptimistic] = useState(current);
|
|
94
|
+
const [isPending, startTransition] = useTransition();
|
|
95
|
+
|
|
96
|
+
const update = (next: T) => {
|
|
97
|
+
setOptimistic(next);
|
|
98
|
+
startTransition(async () => {
|
|
99
|
+
try {
|
|
100
|
+
await updateFn(next);
|
|
101
|
+
} catch {
|
|
102
|
+
setOptimistic(current); // Rollback
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
return [optimistic, update, isPending] as const;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Debounced value hook
|
|
111
|
+
function useDebouncedValue<T>(value: T, delayMs: number): T {
|
|
112
|
+
const [debounced, setDebounced] = useState(value);
|
|
113
|
+
|
|
114
|
+
useEffect(() => {
|
|
115
|
+
const timer = setTimeout(() => setDebounced(value), delayMs);
|
|
116
|
+
return () => clearTimeout(timer);
|
|
117
|
+
}, [value, delayMs]);
|
|
118
|
+
|
|
119
|
+
return debounced;
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Hook Rules (Enforced, Not Suggested)
|
|
124
|
+
|
|
125
|
+
- Only call hooks at the top level — never inside conditions, loops, or nested functions
|
|
126
|
+
- Only call hooks from React function components or custom hooks
|
|
127
|
+
- Custom hooks must start with `use`
|
|
128
|
+
- Always include correct dependencies in `useEffect`/`useMemo`/`useCallback`
|
|
129
|
+
|
|
130
|
+
## State Management
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
// Rule: Use the simplest tool that works
|
|
134
|
+
// 1. useState — local state
|
|
135
|
+
// 2. useReducer — complex local state with actions
|
|
136
|
+
// 3. Context — low-frequency shared state (theme, auth, locale)
|
|
137
|
+
// 4. External store (Zustand, Jotai) — high-frequency shared state
|
|
138
|
+
|
|
139
|
+
// useReducer for complex state machines
|
|
140
|
+
type Action =
|
|
141
|
+
| { type: 'FETCH_START' }
|
|
142
|
+
| { type: 'FETCH_SUCCESS'; payload: User[] }
|
|
143
|
+
| { type: 'FETCH_ERROR'; error: Error };
|
|
144
|
+
|
|
145
|
+
interface State {
|
|
146
|
+
status: 'idle' | 'loading' | 'success' | 'error';
|
|
147
|
+
data: User[];
|
|
148
|
+
error: Error | null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const reducer = (state: State, action: Action): State => {
|
|
152
|
+
switch (action.type) {
|
|
153
|
+
case 'FETCH_START':
|
|
154
|
+
return { ...state, status: 'loading', error: null };
|
|
155
|
+
case 'FETCH_SUCCESS':
|
|
156
|
+
return { status: 'success', data: action.payload, error: null };
|
|
157
|
+
case 'FETCH_ERROR':
|
|
158
|
+
return { ...state, status: 'error', error: action.error };
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Performance
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
// React Compiler (React 19+) handles most memoization automatically
|
|
167
|
+
// Only manually optimize when profiling shows a bottleneck
|
|
168
|
+
|
|
169
|
+
// Virtualize large lists
|
|
170
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
171
|
+
|
|
172
|
+
function VirtualList({ items }: { items: Item[] }) {
|
|
173
|
+
const parentRef = useRef<HTMLDivElement>(null);
|
|
174
|
+
const virtualizer = useVirtualizer({
|
|
175
|
+
count: items.length,
|
|
176
|
+
getScrollElement: () => parentRef.current,
|
|
177
|
+
estimateSize: () => 50,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<div ref={parentRef} style={{ overflow: 'auto', height: '500px' }}>
|
|
182
|
+
<div style={{ height: virtualizer.getTotalSize() }}>
|
|
183
|
+
{virtualizer.getVirtualItems().map(row => (
|
|
184
|
+
<div key={row.key} style={{
|
|
185
|
+
position: 'absolute',
|
|
186
|
+
top: row.start,
|
|
187
|
+
height: row.size,
|
|
188
|
+
}}>
|
|
189
|
+
<ItemRow item={items[row.index]!} />
|
|
190
|
+
</div>
|
|
191
|
+
))}
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Error Boundaries
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
// Every route and major feature section needs an error boundary
|
|
202
|
+
'use client';
|
|
203
|
+
|
|
204
|
+
interface ErrorBoundaryProps {
|
|
205
|
+
children: React.ReactNode;
|
|
206
|
+
fallback: (error: Error, reset: () => void) => React.ReactNode;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
class ErrorBoundary extends React.Component<ErrorBoundaryProps, { error: Error | null }> {
|
|
210
|
+
state = { error: null as Error | null };
|
|
211
|
+
|
|
212
|
+
static getDerivedStateFromError(error: Error) {
|
|
213
|
+
return { error };
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
render() {
|
|
217
|
+
if (this.state.error) {
|
|
218
|
+
return this.props.fallback(this.state.error, () => this.setState({ error: null }));
|
|
219
|
+
}
|
|
220
|
+
return this.props.children;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## Anti-Patterns
|
|
226
|
+
|
|
227
|
+
```tsx
|
|
228
|
+
// Never: useEffect for derived state
|
|
229
|
+
// Bad
|
|
230
|
+
const [fullName, setFullName] = useState('');
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
setFullName(`${firstName} ${lastName}`);
|
|
233
|
+
}, [firstName, lastName]);
|
|
234
|
+
// Good
|
|
235
|
+
const fullName = `${firstName} ${lastName}`;
|
|
236
|
+
|
|
237
|
+
// Never: Object/array literals as default props (new reference every render)
|
|
238
|
+
// Bad
|
|
239
|
+
function List({ items = [] }) { ... }
|
|
240
|
+
// Good
|
|
241
|
+
const EMPTY: readonly Item[] = [];
|
|
242
|
+
function List({ items = EMPTY }: { items?: readonly Item[] }) { ... }
|
|
243
|
+
|
|
244
|
+
// Never: index as key for dynamic lists
|
|
245
|
+
// Bad
|
|
246
|
+
{items.map((item, i) => <Item key={i} item={item} />)}
|
|
247
|
+
// Good
|
|
248
|
+
{items.map(item => <Item key={item.id} item={item} />)}
|
|
249
|
+
```
|