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,448 @@
|
|
|
1
|
+
# JavaScript Expert Development Guide
|
|
2
|
+
|
|
3
|
+
Comprehensive guidelines for principal-level JavaScript engineering across all runtimes, frameworks, and paradigms.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
This guide applies to:
|
|
10
|
+
- Node.js services, CLIs, and tooling
|
|
11
|
+
- React, Vue, Angular, Svelte, and other UI frameworks
|
|
12
|
+
- Vanilla JavaScript and Web APIs
|
|
13
|
+
- TypeScript (strict mode, always)
|
|
14
|
+
- Build tools, bundlers, and transpilers
|
|
15
|
+
- Testing at every level (unit, integration, E2E, performance)
|
|
16
|
+
|
|
17
|
+
### Core Philosophy
|
|
18
|
+
|
|
19
|
+
JavaScript is the runtime. Know it deeply:
|
|
20
|
+
- The event loop is not optional knowledge — it's the foundation
|
|
21
|
+
- Prototypes, closures, and the module system are first principles
|
|
22
|
+
- The spec (ECMAScript) is the source of truth, not blog posts
|
|
23
|
+
- Every abstraction leaks — understand what's underneath
|
|
24
|
+
|
|
25
|
+
### Key Principles
|
|
26
|
+
|
|
27
|
+
1. **Language Mastery Over Framework Dependence** - Frameworks come and go, JS fundamentals are permanent
|
|
28
|
+
2. **Type Safety Is Non-Negotiable** - TypeScript strict mode, always
|
|
29
|
+
3. **Functional by Default** - Pure functions and composition, classes when lifecycle demands it
|
|
30
|
+
4. **Performance Is a Feature** - Profile before optimizing, measure don't guess
|
|
31
|
+
5. **Error Handling Is Control Flow** - Result types over thrown exceptions
|
|
32
|
+
|
|
33
|
+
### Project Structure
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
src/
|
|
37
|
+
├── core/ # Pure business logic, zero dependencies
|
|
38
|
+
├── adapters/ # External integrations (DB, HTTP, FS)
|
|
39
|
+
├── services/ # Orchestration layer
|
|
40
|
+
├── utils/ # Pure utility functions
|
|
41
|
+
├── types/ # TypeScript type definitions
|
|
42
|
+
├── __tests__/ # Test files
|
|
43
|
+
└── index.ts # Public API surface
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Language Deep Dive
|
|
49
|
+
|
|
50
|
+
### The Event Loop
|
|
51
|
+
|
|
52
|
+
Understanding execution order is fundamental:
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
console.log('1 - sync');
|
|
56
|
+
setTimeout(() => console.log('2 - macrotask'), 0);
|
|
57
|
+
Promise.resolve().then(() => console.log('3 - microtask'));
|
|
58
|
+
queueMicrotask(() => console.log('4 - microtask'));
|
|
59
|
+
console.log('5 - sync');
|
|
60
|
+
// Output: 1, 5, 3, 4, 2
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Type Safety
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// Discriminated unions over optional fields
|
|
67
|
+
type Result<T, E = Error> =
|
|
68
|
+
| { ok: true; value: T }
|
|
69
|
+
| { ok: false; error: E };
|
|
70
|
+
|
|
71
|
+
// Branded types for domain safety
|
|
72
|
+
type UserId = string & { readonly __brand: unique symbol };
|
|
73
|
+
|
|
74
|
+
// Const assertions and template literal types
|
|
75
|
+
const EVENTS = ['click', 'keydown', 'submit'] as const;
|
|
76
|
+
type EventName = (typeof EVENTS)[number];
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Functional Patterns
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
const pipe = <T>(...fns: Array<(arg: T) => T>) =>
|
|
83
|
+
(value: T): T => fns.reduce((acc, fn) => fn(acc), value);
|
|
84
|
+
|
|
85
|
+
const processUser = pipe(validateInput, normalizeEmail, hashPassword);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Async Patterns
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
// Parallel independent operations
|
|
92
|
+
const [users, posts] = await Promise.all([fetchUsers(), fetchPosts()]);
|
|
93
|
+
|
|
94
|
+
// Race with timeout
|
|
95
|
+
const withTimeout = <T>(promise: Promise<T>, ms: number): Promise<T> =>
|
|
96
|
+
Promise.race([
|
|
97
|
+
promise,
|
|
98
|
+
new Promise<never>((_, reject) =>
|
|
99
|
+
setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms)
|
|
100
|
+
),
|
|
101
|
+
]);
|
|
102
|
+
|
|
103
|
+
// Async iteration for streams
|
|
104
|
+
async function* readChunks(stream: ReadableStream<Uint8Array>) {
|
|
105
|
+
const reader = stream.getReader();
|
|
106
|
+
try {
|
|
107
|
+
while (true) {
|
|
108
|
+
const { done, value } = await reader.read();
|
|
109
|
+
if (done) break;
|
|
110
|
+
yield value;
|
|
111
|
+
}
|
|
112
|
+
} finally {
|
|
113
|
+
reader.releaseLock();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Generators and Lazy Evaluation
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
function* filter<T>(iterable: Iterable<T>, predicate: (item: T) => boolean) {
|
|
122
|
+
for (const item of iterable) {
|
|
123
|
+
if (predicate(item)) yield item;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function* take<T>(iterable: Iterable<T>, count: number) {
|
|
128
|
+
let i = 0;
|
|
129
|
+
for (const item of iterable) {
|
|
130
|
+
if (i++ >= count) break;
|
|
131
|
+
yield item;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Error Handling
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
// Result types for expected failures
|
|
140
|
+
function parseConfig(raw: string): Result<Config> {
|
|
141
|
+
try {
|
|
142
|
+
const parsed = JSON.parse(raw);
|
|
143
|
+
const validated = ConfigSchema.safeParse(parsed);
|
|
144
|
+
if (!validated.success) {
|
|
145
|
+
return { ok: false, error: new ValidationError(validated.error) };
|
|
146
|
+
}
|
|
147
|
+
return { ok: true, value: validated.data };
|
|
148
|
+
} catch {
|
|
149
|
+
return { ok: false, error: new ParseError('Invalid JSON') };
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Reserve throw for invariant violations
|
|
154
|
+
function assertNonNull<T>(value: T | null | undefined, msg: string): asserts value is T {
|
|
155
|
+
if (value == null) throw new Error(`Invariant: ${msg}`);
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## Node.js Patterns
|
|
162
|
+
|
|
163
|
+
### Graceful Shutdown
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
const shutdown = async (signal: string) => {
|
|
167
|
+
console.log(`Received ${signal}, shutting down...`);
|
|
168
|
+
server.close();
|
|
169
|
+
await db.disconnect();
|
|
170
|
+
process.exit(0);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
174
|
+
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Streams
|
|
178
|
+
|
|
179
|
+
```typescript
|
|
180
|
+
import { pipeline } from 'node:stream/promises';
|
|
181
|
+
|
|
182
|
+
await pipeline(
|
|
183
|
+
createReadStream('input.log'),
|
|
184
|
+
new Transform({
|
|
185
|
+
transform(chunk, _encoding, callback) {
|
|
186
|
+
const filtered = chunk.toString()
|
|
187
|
+
.split('\n')
|
|
188
|
+
.filter((line: string) => line.includes('ERROR'))
|
|
189
|
+
.join('\n');
|
|
190
|
+
callback(null, filtered);
|
|
191
|
+
},
|
|
192
|
+
}),
|
|
193
|
+
createGzip(),
|
|
194
|
+
createWriteStream('errors.log.gz'),
|
|
195
|
+
);
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
### Worker Threads
|
|
199
|
+
|
|
200
|
+
```typescript
|
|
201
|
+
import { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads';
|
|
202
|
+
|
|
203
|
+
if (isMainThread) {
|
|
204
|
+
const runWorker = <T>(data: unknown): Promise<T> =>
|
|
205
|
+
new Promise((resolve, reject) => {
|
|
206
|
+
const worker = new Worker(new URL(import.meta.url), { workerData: data });
|
|
207
|
+
worker.on('message', resolve);
|
|
208
|
+
worker.on('error', reject);
|
|
209
|
+
});
|
|
210
|
+
} else {
|
|
211
|
+
const result = performExpensiveComputation(workerData);
|
|
212
|
+
parentPort!.postMessage(result);
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
### Configuration
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
import { z } from 'zod';
|
|
220
|
+
|
|
221
|
+
const EnvSchema = z.object({
|
|
222
|
+
NODE_ENV: z.enum(['development', 'production', 'test']),
|
|
223
|
+
PORT: z.coerce.number().int().positive().default(3000),
|
|
224
|
+
DATABASE_URL: z.string().url(),
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
export const env = EnvSchema.parse(process.env);
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
## React Patterns
|
|
233
|
+
|
|
234
|
+
### Server Components First (React 19+)
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
// Default: Server Component — no 'use client'
|
|
238
|
+
async function ProjectList() {
|
|
239
|
+
const projects = await db.projects.findMany();
|
|
240
|
+
return <ul>{projects.map(p => <ProjectCard key={p.id} project={p} />)}</ul>;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Only add 'use client' when you need interactivity
|
|
244
|
+
'use client';
|
|
245
|
+
function SearchInput({ onSearch }: { onSearch: (q: string) => void }) {
|
|
246
|
+
const [query, setQuery] = useState('');
|
|
247
|
+
return <input value={query} onChange={e => setQuery(e.target.value)} />;
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
### Composition Patterns
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
// Compound components
|
|
255
|
+
function Tabs({ children }: { children: React.ReactNode }) {
|
|
256
|
+
const [active, setActive] = useState(0);
|
|
257
|
+
return <TabsContext.Provider value={{ active, setActive }}>{children}</TabsContext.Provider>;
|
|
258
|
+
}
|
|
259
|
+
Tabs.List = function TabList({ children }) { ... };
|
|
260
|
+
Tabs.Panel = function TabPanel({ children, index }) { ... };
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### State Management Hierarchy
|
|
264
|
+
|
|
265
|
+
1. `useState` — local state
|
|
266
|
+
2. `useReducer` — complex local state with actions
|
|
267
|
+
3. Context — low-frequency shared state (theme, auth, locale)
|
|
268
|
+
4. External store (Zustand, Jotai) — high-frequency shared state
|
|
269
|
+
|
|
270
|
+
### Anti-Patterns
|
|
271
|
+
|
|
272
|
+
```tsx
|
|
273
|
+
// Never: useEffect for derived state
|
|
274
|
+
const fullName = `${firstName} ${lastName}`; // Just compute it
|
|
275
|
+
|
|
276
|
+
// Never: index as key for dynamic lists
|
|
277
|
+
{items.map(item => <Item key={item.id} item={item} />)}
|
|
278
|
+
|
|
279
|
+
// Never: Object literals as default props
|
|
280
|
+
const EMPTY: readonly Item[] = [];
|
|
281
|
+
function List({ items = EMPTY }: { items?: readonly Item[] }) { ... }
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
## Testing
|
|
287
|
+
|
|
288
|
+
### Philosophy
|
|
289
|
+
|
|
290
|
+
- Tests are production code — same quality standards
|
|
291
|
+
- Test behavior and contracts, never implementation details
|
|
292
|
+
- Every bug fix starts with a failing test
|
|
293
|
+
- If it's hard to test, the design is wrong
|
|
294
|
+
|
|
295
|
+
### Unit Tests
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
describe('clamp', () => {
|
|
299
|
+
it('returns value when within range', () => {
|
|
300
|
+
expect(clamp(5, 0, 10)).toBe(5);
|
|
301
|
+
});
|
|
302
|
+
it('returns min when value is below range', () => {
|
|
303
|
+
expect(clamp(-5, 0, 10)).toBe(0);
|
|
304
|
+
});
|
|
305
|
+
it('returns max when value is above range', () => {
|
|
306
|
+
expect(clamp(15, 0, 10)).toBe(10);
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Parameterized Tests
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
it.each([
|
|
315
|
+
['Hello World', 'hello-world'],
|
|
316
|
+
[' spaces ', 'spaces'],
|
|
317
|
+
['Special!@#Characters', 'specialcharacters'],
|
|
318
|
+
])('converts "%s" to "%s"', (input, expected) => {
|
|
319
|
+
expect(slugify(input)).toBe(expected);
|
|
320
|
+
});
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Component Tests
|
|
324
|
+
|
|
325
|
+
```tsx
|
|
326
|
+
import { render, screen } from '@testing-library/react';
|
|
327
|
+
import userEvent from '@testing-library/user-event';
|
|
328
|
+
|
|
329
|
+
it('adds a new todo when form is submitted', async () => {
|
|
330
|
+
const user = userEvent.setup();
|
|
331
|
+
render(<TodoList />);
|
|
332
|
+
await user.type(screen.getByRole('textbox', { name: /new todo/i }), 'Buy milk');
|
|
333
|
+
await user.click(screen.getByRole('button', { name: /add/i }));
|
|
334
|
+
expect(screen.getByText('Buy milk')).toBeInTheDocument();
|
|
335
|
+
});
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### API Mocking with MSW
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
import { http, HttpResponse } from 'msw';
|
|
342
|
+
import { setupServer } from 'msw/node';
|
|
343
|
+
|
|
344
|
+
const server = setupServer(
|
|
345
|
+
http.get('/api/users/:id', ({ params }) => {
|
|
346
|
+
return HttpResponse.json({ id: params.id, name: 'Test User' });
|
|
347
|
+
}),
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
|
|
351
|
+
afterEach(() => server.resetHandlers());
|
|
352
|
+
afterAll(() => server.close());
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
### E2E Tests
|
|
356
|
+
|
|
357
|
+
```typescript
|
|
358
|
+
import { test, expect } from '@playwright/test';
|
|
359
|
+
|
|
360
|
+
test('login flow', async ({ page }) => {
|
|
361
|
+
await page.goto('/login');
|
|
362
|
+
await page.getByLabel('Email').fill('user@example.com');
|
|
363
|
+
await page.getByLabel('Password').fill('password');
|
|
364
|
+
await page.getByRole('button', { name: 'Sign in' }).click();
|
|
365
|
+
await expect(page).toHaveURL('/dashboard');
|
|
366
|
+
});
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Test Anti-Patterns
|
|
370
|
+
|
|
371
|
+
- Never test implementation details (internal state, class names)
|
|
372
|
+
- Never use `setTimeout` for waiting — use `waitFor` or `findBy`
|
|
373
|
+
- Never let tests depend on execution order
|
|
374
|
+
- Never ignore flaky tests — a flaky test is a bug
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Performance
|
|
379
|
+
|
|
380
|
+
### Profiling First
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
performance.mark('operation-start');
|
|
384
|
+
doExpensiveWork();
|
|
385
|
+
performance.mark('operation-end');
|
|
386
|
+
performance.measure('operation', 'operation-start', 'operation-end');
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
### V8 Optimization
|
|
390
|
+
|
|
391
|
+
- Keep function argument shapes consistent (monomorphic)
|
|
392
|
+
- Initialize all object properties in the same order
|
|
393
|
+
- Use `Map`/`Set` for dynamic collections, plain objects for static shapes
|
|
394
|
+
- Use `TypedArray` for numeric data
|
|
395
|
+
|
|
396
|
+
### Memory Management
|
|
397
|
+
|
|
398
|
+
- Use `AbortController` signals for event listener cleanup
|
|
399
|
+
- Use `WeakMap`/`WeakRef` for caches that shouldn't prevent GC
|
|
400
|
+
- Avoid closures that capture large scopes — extract only what's needed
|
|
401
|
+
|
|
402
|
+
### Bundle Size
|
|
403
|
+
|
|
404
|
+
- Dynamic imports for code splitting
|
|
405
|
+
- Named exports for tree-shaking
|
|
406
|
+
- Prefer native APIs (`structuredClone`, `Intl`, `URL`, `crypto.randomUUID`)
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
## Tooling
|
|
411
|
+
|
|
412
|
+
### TypeScript Config
|
|
413
|
+
|
|
414
|
+
```jsonc
|
|
415
|
+
{
|
|
416
|
+
"compilerOptions": {
|
|
417
|
+
"strict": true,
|
|
418
|
+
"noUncheckedIndexedAccess": true,
|
|
419
|
+
"exactOptionalPropertyTypes": true,
|
|
420
|
+
"verbatimModuleSyntax": true,
|
|
421
|
+
"module": "ESNext",
|
|
422
|
+
"target": "ES2022"
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Dependency Hygiene
|
|
428
|
+
|
|
429
|
+
- Pin exact versions for applications
|
|
430
|
+
- Audit regularly (`npm audit`)
|
|
431
|
+
- Prefer zero-dependency packages
|
|
432
|
+
- Check bundle size before adding (`bundlephobia.com`)
|
|
433
|
+
|
|
434
|
+
---
|
|
435
|
+
|
|
436
|
+
## Definition of Done
|
|
437
|
+
|
|
438
|
+
A JavaScript feature is complete when:
|
|
439
|
+
|
|
440
|
+
- [ ] TypeScript strict mode passes with zero errors
|
|
441
|
+
- [ ] All code paths have explicit types (no `any`)
|
|
442
|
+
- [ ] Unit tests cover logic branches and edge cases
|
|
443
|
+
- [ ] Integration tests verify external boundaries
|
|
444
|
+
- [ ] Error cases are tested, not just happy paths
|
|
445
|
+
- [ ] No floating promises
|
|
446
|
+
- [ ] No memory leaks in long-running paths
|
|
447
|
+
- [ ] Bundle impact assessed (client-side code)
|
|
448
|
+
- [ ] Code reviewed and approved
|