agentic-team-templates 0.9.2 → 0.11.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.
@@ -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
@@ -0,0 +1,250 @@
1
+ # Rust Concurrency
2
+
3
+ Rust prevents data races at compile time through ownership and the `Send`/`Sync` marker traits. Fearless concurrency is real — but it doesn't mean you can ignore deadlocks, race conditions at the logic level, or performance pitfalls.
4
+
5
+ ## Send and Sync
6
+
7
+ ```rust
8
+ // Send: safe to transfer ownership to another thread
9
+ // Sync: safe to share references (&T) between threads
10
+ // These are auto-traits — the compiler derives them automatically
11
+
12
+ // Most types are Send + Sync
13
+ // Rc<T> is NOT Send or Sync — use Arc<T> for multi-threaded code
14
+ // RefCell<T> is Send but NOT Sync — use Mutex<T> for multi-threaded code
15
+ // Raw pointers are neither — wrap them in types that uphold invariants
16
+ ```
17
+
18
+ ## Threading
19
+
20
+ ### std::thread
21
+
22
+ ```rust
23
+ use std::thread;
24
+
25
+ // Scoped threads (Go 1.63+) — borrows from the parent stack are allowed
26
+ let mut data = vec![1, 2, 3, 4];
27
+ thread::scope(|s| {
28
+ let (left, right) = data.split_at_mut(2);
29
+
30
+ s.spawn(|| {
31
+ left.iter_mut().for_each(|x| *x *= 2);
32
+ });
33
+ s.spawn(|| {
34
+ right.iter_mut().for_each(|x| *x *= 3);
35
+ });
36
+ });
37
+ // Both threads are guaranteed to finish before scope exits
38
+ // No Arc, no clone, no join handles to manage
39
+ assert_eq!(data, [2, 4, 9, 12]);
40
+ ```
41
+
42
+ ### Shared State with Arc + Mutex
43
+
44
+ ```rust
45
+ use std::sync::{Arc, Mutex};
46
+
47
+ let counter = Arc::new(Mutex::new(0));
48
+ let mut handles = vec![];
49
+
50
+ for _ in 0..10 {
51
+ let counter = Arc::clone(&counter);
52
+ handles.push(thread::spawn(move || {
53
+ let mut num = counter.lock().unwrap();
54
+ *num += 1;
55
+ }));
56
+ }
57
+
58
+ for handle in handles {
59
+ handle.join().unwrap();
60
+ }
61
+ ```
62
+
63
+ ### RwLock for Read-Heavy Workloads
64
+
65
+ ```rust
66
+ use std::sync::RwLock;
67
+
68
+ let config = Arc::new(RwLock::new(Config::default()));
69
+
70
+ // Multiple readers — no blocking
71
+ let cfg = config.read().unwrap();
72
+ println!("{}", cfg.setting);
73
+
74
+ // Single writer — exclusive access
75
+ let mut cfg = config.write().unwrap();
76
+ cfg.setting = new_value;
77
+ ```
78
+
79
+ ## Channels
80
+
81
+ ```rust
82
+ use std::sync::mpsc;
83
+
84
+ // Multiple producers, single consumer
85
+ let (tx, rx) = mpsc::channel();
86
+
87
+ let tx2 = tx.clone(); // Clone sender for second producer
88
+
89
+ thread::spawn(move || {
90
+ tx.send(Message::Data(vec![1, 2, 3])).unwrap();
91
+ });
92
+
93
+ thread::spawn(move || {
94
+ tx2.send(Message::Data(vec![4, 5, 6])).unwrap();
95
+ });
96
+
97
+ // Receive all messages
98
+ for msg in rx {
99
+ process(msg);
100
+ }
101
+
102
+ // crossbeam-channel for multi-producer multi-consumer, select!, bounded channels
103
+ use crossbeam_channel::{bounded, select};
104
+
105
+ let (tx, rx) = bounded(100); // Backpressure at 100 items
106
+
107
+ select! {
108
+ recv(rx) -> msg => handle(msg.unwrap()),
109
+ default(Duration::from_secs(1)) => println!("timeout"),
110
+ }
111
+ ```
112
+
113
+ ## Async/Await
114
+
115
+ ### Tokio Runtime
116
+
117
+ ```rust
118
+ #[tokio::main]
119
+ async fn main() -> Result<()> {
120
+ let listener = TcpListener::bind("0.0.0.0:8080").await?;
121
+
122
+ loop {
123
+ let (stream, addr) = listener.accept().await?;
124
+ tokio::spawn(async move {
125
+ if let Err(e) = handle_connection(stream).await {
126
+ eprintln!("connection error from {addr}: {e}");
127
+ }
128
+ });
129
+ }
130
+ }
131
+ ```
132
+
133
+ ### Async Patterns
134
+
135
+ ```rust
136
+ // Concurrent execution with join
137
+ let (users, posts) = tokio::join!(
138
+ fetch_users(&client),
139
+ fetch_posts(&client),
140
+ );
141
+
142
+ // Race — first to complete wins
143
+ tokio::select! {
144
+ result = fetch_data() => handle_data(result),
145
+ _ = tokio::time::sleep(Duration::from_secs(5)) => {
146
+ return Err(anyhow!("fetch timed out"));
147
+ }
148
+ }
149
+
150
+ // Bounded concurrency with semaphore
151
+ use tokio::sync::Semaphore;
152
+
153
+ let semaphore = Arc::new(Semaphore::new(10));
154
+ let mut handles = vec![];
155
+
156
+ for url in urls {
157
+ let permit = semaphore.clone().acquire_owned().await?;
158
+ handles.push(tokio::spawn(async move {
159
+ let result = fetch(url).await;
160
+ drop(permit); // Release when done
161
+ result
162
+ }));
163
+ }
164
+
165
+ // Stream processing
166
+ use tokio_stream::StreamExt;
167
+
168
+ let mut stream = tokio_stream::iter(items)
169
+ .map(|item| async move { process(item).await })
170
+ .buffer_unordered(10); // Process up to 10 concurrently
171
+
172
+ while let Some(result) = stream.next().await {
173
+ handle(result?);
174
+ }
175
+ ```
176
+
177
+ ### Async Traits
178
+
179
+ ```rust
180
+ // Since Rust 1.75: async fn in traits works natively
181
+ pub trait Repository {
182
+ async fn find_by_id(&self, id: &str) -> Result<Option<Entity>>;
183
+ async fn save(&self, entity: &Entity) -> Result<()>;
184
+ }
185
+
186
+ // For trait objects (dyn), use the async-trait crate or
187
+ // return Pin<Box<dyn Future>> manually
188
+ ```
189
+
190
+ ### Cancellation Safety
191
+
192
+ ```rust
193
+ // tokio::select! can cancel futures — understand what that means
194
+ // A future dropped mid-execution won't run its remaining code
195
+
196
+ // Cancellation-safe: reading from a channel (no partial state)
197
+ // NOT cancellation-safe: reading into a buffer (partial read lost)
198
+
199
+ tokio::select! {
200
+ // Safe: mpsc::Receiver::recv is cancellation-safe
201
+ msg = rx.recv() => { ... }
202
+
203
+ // DANGEROUS if buf has partial state from a previous iteration
204
+ n = reader.read(&mut buf) => { ... }
205
+ }
206
+
207
+ // When in doubt, consult tokio's docs for each method's cancellation safety
208
+ ```
209
+
210
+ ## Atomics
211
+
212
+ ```rust
213
+ use std::sync::atomic::{AtomicU64, Ordering};
214
+
215
+ static REQUEST_COUNT: AtomicU64 = AtomicU64::new(0);
216
+
217
+ fn handle_request() {
218
+ REQUEST_COUNT.fetch_add(1, Ordering::Relaxed);
219
+ }
220
+
221
+ // Ordering guide:
222
+ // Relaxed — no ordering guarantees, just atomicity (counters)
223
+ // Acquire — subsequent reads see writes before the Release
224
+ // Release — previous writes are visible to Acquire loads
225
+ // SeqCst — total order, strongest guarantee, rarely needed
226
+ // If unsure, use SeqCst and optimize later with profiling data
227
+ ```
228
+
229
+ ## Anti-Patterns
230
+
231
+ ```rust
232
+ // Never: Holding a MutexGuard across an await point
233
+ let guard = mutex.lock().await;
234
+ do_async_work().await; // Deadlock risk — guard is held across await
235
+ drop(guard);
236
+ // Instead: lock, extract data, drop guard, then await
237
+
238
+ // Never: Spawning tasks without cancellation or shutdown strategy
239
+ tokio::spawn(async { loop { do_work().await; } }); // How does this stop?
240
+
241
+ // Never: Blocking in async context
242
+ std::thread::sleep(Duration::from_secs(1)); // Blocks the executor thread!
243
+ tokio::time::sleep(Duration::from_secs(1)).await; // Use this instead
244
+
245
+ // For CPU-bound work in async context:
246
+ tokio::task::spawn_blocking(|| expensive_computation()).await?;
247
+
248
+ // Never: Using std::sync::Mutex in async code (blocks the executor)
249
+ // Use tokio::sync::Mutex instead when you must hold across awaits
250
+ ```