agentic-team-templates 0.9.1 → 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
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-team-templates",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "AI coding assistant templates for Cursor IDE. Pre-configured rules and guidelines that help AI assistants write better code. - use at your own risk",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cursor",
|
package/src/index.js
CHANGED
|
@@ -28,6 +28,14 @@ const TEMPLATES = {
|
|
|
28
28
|
description: 'Command-line applications and developer tools (Cobra, Commander, Click)',
|
|
29
29
|
rules: ['architecture.md', 'arguments.md', 'distribution.md', 'error-handling.md', 'overview.md', 'testing.md', 'user-experience.md']
|
|
30
30
|
},
|
|
31
|
+
'data-engineering': {
|
|
32
|
+
description: 'Data platforms and pipelines (ETL, data modeling, data quality)',
|
|
33
|
+
rules: ['data-modeling.md', 'data-quality.md', 'overview.md', 'performance.md', 'pipeline-design.md', 'security.md', 'testing.md']
|
|
34
|
+
},
|
|
35
|
+
'devops-sre': {
|
|
36
|
+
description: 'DevOps and SRE practices (incident management, observability, SLOs, chaos engineering)',
|
|
37
|
+
rules: ['capacity-planning.md', 'change-management.md', 'chaos-engineering.md', 'disaster-recovery.md', 'incident-management.md', 'observability.md', 'overview.md', 'postmortems.md', 'runbooks.md', 'slo-sli.md', 'toil-reduction.md']
|
|
38
|
+
},
|
|
31
39
|
'documentation': {
|
|
32
40
|
description: 'Technical documentation standards (READMEs, API docs, ADRs, code comments)',
|
|
33
41
|
rules: ['adr.md', 'api-documentation.md', 'code-comments.md', 'maintenance.md', 'overview.md', 'readme-standards.md']
|
|
@@ -36,10 +44,34 @@ const TEMPLATES = {
|
|
|
36
44
|
description: 'Full-stack web applications (Next.js, Nuxt, SvelteKit, Remix)',
|
|
37
45
|
rules: ['api-contracts.md', 'architecture.md', 'overview.md', 'shared-types.md', 'testing.md']
|
|
38
46
|
},
|
|
47
|
+
'javascript-expert': {
|
|
48
|
+
description: 'Principal-level JavaScript engineering across Node.js, React, vanilla JS, and testing',
|
|
49
|
+
rules: ['language-deep-dive.md', 'node-patterns.md', 'overview.md', 'performance.md', 'react-patterns.md', 'testing.md', 'tooling.md']
|
|
50
|
+
},
|
|
51
|
+
'ml-ai': {
|
|
52
|
+
description: 'Machine learning and AI systems (model development, deployment, monitoring)',
|
|
53
|
+
rules: ['data-engineering.md', 'deployment.md', 'model-development.md', 'monitoring.md', 'overview.md', 'security.md', 'testing.md']
|
|
54
|
+
},
|
|
39
55
|
'mobile': {
|
|
40
56
|
description: 'Mobile applications (React Native, Flutter, native iOS/Android)',
|
|
41
57
|
rules: ['navigation.md', 'offline-first.md', 'overview.md', 'performance.md', 'testing.md']
|
|
42
58
|
},
|
|
59
|
+
'platform-engineering': {
|
|
60
|
+
description: 'Internal developer platforms, infrastructure automation, and reliability engineering',
|
|
61
|
+
rules: ['ci-cd.md', 'developer-experience.md', 'infrastructure-as-code.md', 'kubernetes.md', 'observability.md', 'overview.md', 'security.md', 'testing.md']
|
|
62
|
+
},
|
|
63
|
+
'product-manager': {
|
|
64
|
+
description: 'Product management with customer-centric discovery, prioritization, and execution',
|
|
65
|
+
rules: ['communication.md', 'discovery.md', 'metrics.md', 'overview.md', 'prioritization.md', 'requirements.md']
|
|
66
|
+
},
|
|
67
|
+
'qa-engineering': {
|
|
68
|
+
description: 'Quality assurance programs for confident, rapid software delivery',
|
|
69
|
+
rules: ['automation.md', 'metrics.md', 'overview.md', 'quality-gates.md', 'test-design.md', 'test-strategy.md']
|
|
70
|
+
},
|
|
71
|
+
'testing': {
|
|
72
|
+
description: 'Comprehensive testing practices (TDD, test design, CI/CD integration, performance testing)',
|
|
73
|
+
rules: ['advanced-techniques.md', 'ci-cd-integration.md', 'overview.md', 'performance-testing.md', 'quality-metrics.md', 'reliability.md', 'tdd-methodology.md', 'test-data.md', 'test-design.md', 'test-types.md']
|
|
74
|
+
},
|
|
43
75
|
'utility-agent': {
|
|
44
76
|
description: 'AI agent utilities with context management and hallucination prevention',
|
|
45
77
|
rules: ['action-control.md', 'context-management.md', 'hallucination-prevention.md', 'overview.md', 'token-optimization.md']
|
package/src/index.test.js
CHANGED
|
@@ -75,9 +75,17 @@ describe('Constants', () => {
|
|
|
75
75
|
const expectedTemplates = [
|
|
76
76
|
'blockchain',
|
|
77
77
|
'cli-tools',
|
|
78
|
+
'data-engineering',
|
|
79
|
+
'devops-sre',
|
|
78
80
|
'documentation',
|
|
79
81
|
'fullstack',
|
|
82
|
+
'javascript-expert',
|
|
83
|
+
'ml-ai',
|
|
80
84
|
'mobile',
|
|
85
|
+
'platform-engineering',
|
|
86
|
+
'product-manager',
|
|
87
|
+
'qa-engineering',
|
|
88
|
+
'testing',
|
|
81
89
|
'utility-agent',
|
|
82
90
|
'web-backend',
|
|
83
91
|
'web-frontend',
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# JavaScript Language Deep Dive
|
|
2
|
+
|
|
3
|
+
Advanced JavaScript patterns and idioms for expert-level engineering.
|
|
4
|
+
|
|
5
|
+
## The Event Loop
|
|
6
|
+
|
|
7
|
+
Understanding execution order is fundamental:
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// Know the execution order: synchronous > microtasks > macrotasks
|
|
11
|
+
console.log('1 - sync');
|
|
12
|
+
|
|
13
|
+
setTimeout(() => console.log('2 - macrotask'), 0);
|
|
14
|
+
|
|
15
|
+
Promise.resolve().then(() => console.log('3 - microtask'));
|
|
16
|
+
|
|
17
|
+
queueMicrotask(() => console.log('4 - microtask'));
|
|
18
|
+
|
|
19
|
+
console.log('5 - sync');
|
|
20
|
+
|
|
21
|
+
// Output: 1, 5, 3, 4, 2
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### Async Patterns
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
// Prefer async/await over raw promises
|
|
28
|
+
// But know when Promise combinators are the right tool
|
|
29
|
+
|
|
30
|
+
// Parallel independent operations
|
|
31
|
+
const [users, posts] = await Promise.all([
|
|
32
|
+
fetchUsers(),
|
|
33
|
+
fetchPosts(),
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
// Race with timeout
|
|
37
|
+
const withTimeout = <T>(promise: Promise<T>, ms: number): Promise<T> =>
|
|
38
|
+
Promise.race([
|
|
39
|
+
promise,
|
|
40
|
+
new Promise<never>((_, reject) =>
|
|
41
|
+
setTimeout(() => reject(new Error(`Timeout after ${ms}ms`)), ms)
|
|
42
|
+
),
|
|
43
|
+
]);
|
|
44
|
+
|
|
45
|
+
// Settle all, handle individually
|
|
46
|
+
const results = await Promise.allSettled(tasks.map(processTask));
|
|
47
|
+
const failures = results.filter(
|
|
48
|
+
(r): r is PromiseRejectedResult => r.status === 'rejected'
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
// Async iteration for streams
|
|
52
|
+
async function* readChunks(stream: ReadableStream<Uint8Array>) {
|
|
53
|
+
const reader = stream.getReader();
|
|
54
|
+
try {
|
|
55
|
+
while (true) {
|
|
56
|
+
const { done, value } = await reader.read();
|
|
57
|
+
if (done) break;
|
|
58
|
+
yield value;
|
|
59
|
+
}
|
|
60
|
+
} finally {
|
|
61
|
+
reader.releaseLock();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Closures and Scope
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
// Closures for encapsulation
|
|
70
|
+
const createRateLimiter = (maxCalls: number, windowMs: number) => {
|
|
71
|
+
const calls: number[] = [];
|
|
72
|
+
|
|
73
|
+
return <T>(fn: () => T): T | null => {
|
|
74
|
+
const now = Date.now();
|
|
75
|
+
const windowStart = now - windowMs;
|
|
76
|
+
// Remove expired entries
|
|
77
|
+
while (calls.length > 0 && calls[0]! < windowStart) calls.shift();
|
|
78
|
+
|
|
79
|
+
if (calls.length >= maxCalls) return null;
|
|
80
|
+
calls.push(now);
|
|
81
|
+
return fn();
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
// Private state with closures (pre-#private fields pattern)
|
|
86
|
+
// Prefer #private fields in classes, closures in functions
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Proxy and Reflect
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// Observable objects
|
|
93
|
+
const createObservable = <T extends object>(
|
|
94
|
+
target: T,
|
|
95
|
+
onChange: (prop: string | symbol, value: unknown) => void,
|
|
96
|
+
): T =>
|
|
97
|
+
new Proxy(target, {
|
|
98
|
+
set(obj, prop, value, receiver) {
|
|
99
|
+
const result = Reflect.set(obj, prop, value, receiver);
|
|
100
|
+
onChange(prop, value);
|
|
101
|
+
return result;
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// Validation proxies
|
|
106
|
+
const createValidated = <T extends Record<string, unknown>>(
|
|
107
|
+
target: T,
|
|
108
|
+
validators: Partial<Record<keyof T, (v: unknown) => boolean>>,
|
|
109
|
+
): T =>
|
|
110
|
+
new Proxy(target, {
|
|
111
|
+
set(obj, prop, value, receiver) {
|
|
112
|
+
const validate = validators[prop as keyof T];
|
|
113
|
+
if (validate && !validate(value)) {
|
|
114
|
+
throw new TypeError(`Invalid value for ${String(prop)}`);
|
|
115
|
+
}
|
|
116
|
+
return Reflect.set(obj, prop, value, receiver);
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## WeakRef and FinalizationRegistry
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
// Cache that doesn't prevent garbage collection
|
|
125
|
+
class WeakCache<K extends object, V> {
|
|
126
|
+
readonly #cache = new Map<string, WeakRef<V & object>>();
|
|
127
|
+
readonly #registry = new FinalizationRegistry<string>((key) => {
|
|
128
|
+
this.#cache.delete(key);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
set(key: string, value: V & object): void {
|
|
132
|
+
this.#cache.set(key, new WeakRef(value));
|
|
133
|
+
this.#registry.register(value, key);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
get(key: string): V | undefined {
|
|
137
|
+
return this.#cache.get(key)?.deref();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Iterators and Generators
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
// Custom iterable
|
|
146
|
+
class Range implements Iterable<number> {
|
|
147
|
+
constructor(
|
|
148
|
+
private readonly start: number,
|
|
149
|
+
private readonly end: number,
|
|
150
|
+
private readonly step = 1,
|
|
151
|
+
) {}
|
|
152
|
+
|
|
153
|
+
*[Symbol.iterator](): Generator<number> {
|
|
154
|
+
for (let i = this.start; i < this.end; i += this.step) {
|
|
155
|
+
yield i;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Lazy evaluation with generators
|
|
161
|
+
function* filter<T>(iterable: Iterable<T>, predicate: (item: T) => boolean) {
|
|
162
|
+
for (const item of iterable) {
|
|
163
|
+
if (predicate(item)) yield item;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function* map<T, U>(iterable: Iterable<T>, transform: (item: T) => U) {
|
|
168
|
+
for (const item of iterable) {
|
|
169
|
+
yield transform(item);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function* take<T>(iterable: Iterable<T>, count: number) {
|
|
174
|
+
let i = 0;
|
|
175
|
+
for (const item of iterable) {
|
|
176
|
+
if (i++ >= count) break;
|
|
177
|
+
yield item;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Compose lazy operations — no intermediate arrays
|
|
182
|
+
const result = [...take(
|
|
183
|
+
map(
|
|
184
|
+
filter(hugeDataset, item => item.active),
|
|
185
|
+
item => item.name,
|
|
186
|
+
),
|
|
187
|
+
10,
|
|
188
|
+
)];
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Structured Clone and Transfer
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
// Deep clone without JSON.parse(JSON.stringify(...))
|
|
195
|
+
const clone = structuredClone(complexObject);
|
|
196
|
+
|
|
197
|
+
// Transfer ownership (zero-copy for ArrayBuffers)
|
|
198
|
+
const buffer = new ArrayBuffer(1024);
|
|
199
|
+
worker.postMessage({ buffer }, [buffer]);
|
|
200
|
+
// buffer.byteLength is now 0 — ownership transferred
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
## Module Patterns
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
// Named exports for tree-shaking
|
|
207
|
+
export const validateEmail = (email: string): boolean => { ... };
|
|
208
|
+
export const validatePhone = (phone: string): boolean => { ... };
|
|
209
|
+
|
|
210
|
+
// Barrel files only at package boundaries, never internal
|
|
211
|
+
// src/validators/index.ts — public API
|
|
212
|
+
export { validateEmail } from './email.js';
|
|
213
|
+
export { validatePhone } from './phone.js';
|
|
214
|
+
|
|
215
|
+
// Dynamic imports for code splitting
|
|
216
|
+
const module = await import(`./locales/${locale}.js`);
|
|
217
|
+
|
|
218
|
+
// Import attributes (for JSON, CSS modules)
|
|
219
|
+
import config from './config.json' with { type: 'json' };
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Anti-Patterns to Avoid
|
|
223
|
+
|
|
224
|
+
```typescript
|
|
225
|
+
// Never: for...in on arrays (iterates prototype chain)
|
|
226
|
+
for (const key in array) { } // BAD
|
|
227
|
+
|
|
228
|
+
// Instead: for...of, .forEach, .map, .reduce
|
|
229
|
+
for (const item of array) { } // GOOD
|
|
230
|
+
|
|
231
|
+
// Never: == for comparison (type coercion surprises)
|
|
232
|
+
if (value == null) { } // Only acceptable case (null + undefined check)
|
|
233
|
+
|
|
234
|
+
// Never: delete on arrays (creates sparse array)
|
|
235
|
+
delete arr[2]; // BAD — leaves hole
|
|
236
|
+
arr.splice(2, 1); // GOOD
|
|
237
|
+
|
|
238
|
+
// Never: blocking the event loop
|
|
239
|
+
// Use worker_threads for CPU-intensive work in Node.js
|
|
240
|
+
// Use Web Workers in the browser
|
|
241
|
+
|
|
242
|
+
// Never: implicit globals
|
|
243
|
+
function bad() { x = 5; } // Creates global!
|
|
244
|
+
// Always use const/let, always use strict mode
|
|
245
|
+
```
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# Node.js Patterns
|
|
2
|
+
|
|
3
|
+
Expert-level Node.js patterns for building production services, CLIs, and tooling.
|
|
4
|
+
|
|
5
|
+
## Runtime Essentials
|
|
6
|
+
|
|
7
|
+
### Process Lifecycle
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// Graceful shutdown
|
|
11
|
+
const shutdown = async (signal: string) => {
|
|
12
|
+
console.log(`Received ${signal}, shutting down gracefully...`);
|
|
13
|
+
server.close();
|
|
14
|
+
await db.disconnect();
|
|
15
|
+
process.exit(0);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
19
|
+
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
20
|
+
|
|
21
|
+
// Unhandled rejection = crash (don't swallow errors)
|
|
22
|
+
process.on('unhandledRejection', (reason) => {
|
|
23
|
+
console.error('Unhandled rejection:', reason);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Streams
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { pipeline } from 'node:stream/promises';
|
|
32
|
+
import { createReadStream, createWriteStream } from 'node:fs';
|
|
33
|
+
import { createGzip } from 'node:zlib';
|
|
34
|
+
import { Transform } from 'node:stream';
|
|
35
|
+
|
|
36
|
+
// Always use pipeline for proper error handling and cleanup
|
|
37
|
+
await pipeline(
|
|
38
|
+
createReadStream('input.log'),
|
|
39
|
+
new Transform({
|
|
40
|
+
transform(chunk, _encoding, callback) {
|
|
41
|
+
const filtered = chunk
|
|
42
|
+
.toString()
|
|
43
|
+
.split('\n')
|
|
44
|
+
.filter((line: string) => line.includes('ERROR'))
|
|
45
|
+
.join('\n');
|
|
46
|
+
callback(null, filtered);
|
|
47
|
+
},
|
|
48
|
+
}),
|
|
49
|
+
createGzip(),
|
|
50
|
+
createWriteStream('errors.log.gz'),
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
// Web-standard streams in Node.js
|
|
54
|
+
const response = await fetch(url);
|
|
55
|
+
const reader = response.body!.getReader();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Worker Threads
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads';
|
|
62
|
+
|
|
63
|
+
if (isMainThread) {
|
|
64
|
+
const runWorker = <T>(data: unknown): Promise<T> =>
|
|
65
|
+
new Promise((resolve, reject) => {
|
|
66
|
+
const worker = new Worker(new URL(import.meta.url), { workerData: data });
|
|
67
|
+
worker.on('message', resolve);
|
|
68
|
+
worker.on('error', reject);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const result = await runWorker({ task: 'hash', input: largeBuffer });
|
|
72
|
+
} else {
|
|
73
|
+
// Worker thread — CPU-intensive work here
|
|
74
|
+
const result = performExpensiveComputation(workerData);
|
|
75
|
+
parentPort!.postMessage(result);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### File System
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
import { readFile, writeFile, mkdir, access } from 'node:fs/promises';
|
|
83
|
+
import { constants } from 'node:fs';
|
|
84
|
+
|
|
85
|
+
// Always use fs/promises, never sync variants in async code
|
|
86
|
+
const data = await readFile('config.json', 'utf-8');
|
|
87
|
+
|
|
88
|
+
// Check existence without TOCTOU race
|
|
89
|
+
const fileExists = async (path: string): Promise<boolean> => {
|
|
90
|
+
try {
|
|
91
|
+
await access(path, constants.F_OK);
|
|
92
|
+
return true;
|
|
93
|
+
} catch {
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// Atomic writes to prevent corruption
|
|
99
|
+
import { writeFile as atomicWrite } from 'atomically';
|
|
100
|
+
await atomicWrite('important.json', JSON.stringify(data));
|
|
101
|
+
|
|
102
|
+
// Use node:path for all path manipulation
|
|
103
|
+
import { join, resolve, extname, basename } from 'node:path';
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Module System
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// Use node: protocol for builtins (explicit, no ambiguity)
|
|
110
|
+
import { readFile } from 'node:fs/promises';
|
|
111
|
+
import { join } from 'node:path';
|
|
112
|
+
import { createHash } from 'node:crypto';
|
|
113
|
+
|
|
114
|
+
// ESM by default — set "type": "module" in package.json
|
|
115
|
+
// Use .js extensions in imports (required for ESM)
|
|
116
|
+
import { validate } from './validators.js';
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Error Handling in Node.js
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
// Custom error classes with cause chaining
|
|
123
|
+
class DatabaseError extends Error {
|
|
124
|
+
constructor(message: string, options?: ErrorOptions) {
|
|
125
|
+
super(message, options);
|
|
126
|
+
this.name = 'DatabaseError';
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Wrap external errors with context
|
|
131
|
+
try {
|
|
132
|
+
await db.query(sql);
|
|
133
|
+
} catch (err) {
|
|
134
|
+
throw new DatabaseError(`Query failed: ${sql}`, { cause: err });
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// AbortController for cancellation
|
|
138
|
+
const controller = new AbortController();
|
|
139
|
+
setTimeout(() => controller.abort(), 5000);
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
143
|
+
} catch (err) {
|
|
144
|
+
if (err instanceof DOMException && err.name === 'AbortError') {
|
|
145
|
+
// Handle timeout specifically
|
|
146
|
+
}
|
|
147
|
+
throw err;
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Configuration
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
// Validate environment at startup, fail fast
|
|
155
|
+
import { z } from 'zod';
|
|
156
|
+
|
|
157
|
+
const EnvSchema = z.object({
|
|
158
|
+
NODE_ENV: z.enum(['development', 'production', 'test']),
|
|
159
|
+
PORT: z.coerce.number().int().positive().default(3000),
|
|
160
|
+
DATABASE_URL: z.string().url(),
|
|
161
|
+
API_KEY: z.string().min(1),
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Parse once at startup — crash if invalid
|
|
165
|
+
export const env = EnvSchema.parse(process.env);
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Anti-Patterns
|
|
169
|
+
|
|
170
|
+
```typescript
|
|
171
|
+
// Never: require() in ESM (use import or createRequire)
|
|
172
|
+
// Never: sync fs operations in request handlers
|
|
173
|
+
// Never: unbounded concurrency
|
|
174
|
+
// Bad:
|
|
175
|
+
await Promise.all(thousandItems.map(item => processItem(item)));
|
|
176
|
+
// Good: Use p-limit or manual batching
|
|
177
|
+
import pLimit from 'p-limit';
|
|
178
|
+
const limit = pLimit(10);
|
|
179
|
+
await Promise.all(thousandItems.map(item => limit(() => processItem(item))));
|
|
180
|
+
|
|
181
|
+
// Never: string concatenation for SQL (injection risk)
|
|
182
|
+
// Never: eval() or new Function() with user input
|
|
183
|
+
// Never: process.exit() without cleanup
|
|
184
|
+
```
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# JavaScript Expert
|
|
2
|
+
|
|
3
|
+
Guidelines for principal-level JavaScript engineering across all runtimes, frameworks, and paradigms.
|
|
4
|
+
|
|
5
|
+
## Scope
|
|
6
|
+
|
|
7
|
+
This ruleset applies to:
|
|
8
|
+
- Node.js services, CLIs, and tooling
|
|
9
|
+
- React, Vue, Angular, Svelte, and other UI frameworks
|
|
10
|
+
- Vanilla JavaScript and Web APIs
|
|
11
|
+
- TypeScript (strict mode, always)
|
|
12
|
+
- Build tools, bundlers, and transpilers
|
|
13
|
+
- Testing at every level (unit, integration, E2E, performance)
|
|
14
|
+
|
|
15
|
+
## Core Philosophy
|
|
16
|
+
|
|
17
|
+
JavaScript is the runtime. Know it deeply:
|
|
18
|
+
- The event loop is not optional knowledge — it's the foundation
|
|
19
|
+
- Prototypes, closures, and the module system are first principles
|
|
20
|
+
- The spec (ECMAScript) is the source of truth, not blog posts
|
|
21
|
+
- Every abstraction leaks — understand what's underneath
|
|
22
|
+
|
|
23
|
+
## Key Principles
|
|
24
|
+
|
|
25
|
+
### 1. Language Mastery Over Framework Dependence
|
|
26
|
+
|
|
27
|
+
Frameworks come and go. JavaScript fundamentals are permanent:
|
|
28
|
+
- Understand `this` binding rules (default, implicit, explicit, `new`)
|
|
29
|
+
- Know the difference between the task queue, microtask queue, and rendering pipeline
|
|
30
|
+
- Use native APIs before reaching for libraries (`structuredClone`, `AbortController`, `Intl`, `URL`, `crypto.subtle`)
|
|
31
|
+
- Understand generator functions, iterators, and async iteration
|
|
32
|
+
|
|
33
|
+
### 2. Type Safety Is Non-Negotiable
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
// Always: strict TypeScript
|
|
37
|
+
// tsconfig.json: "strict": true, "noUncheckedIndexedAccess": true
|
|
38
|
+
|
|
39
|
+
// Prefer discriminated unions over optional fields
|
|
40
|
+
type Result<T, E = Error> =
|
|
41
|
+
| { ok: true; value: T }
|
|
42
|
+
| { ok: false; error: E };
|
|
43
|
+
|
|
44
|
+
// Use const assertions and template literal types
|
|
45
|
+
const EVENTS = ['click', 'keydown', 'submit'] as const;
|
|
46
|
+
type EventName = (typeof EVENTS)[number];
|
|
47
|
+
|
|
48
|
+
// Branded types for domain safety
|
|
49
|
+
type UserId = string & { readonly __brand: unique symbol };
|
|
50
|
+
type Email = string & { readonly __brand: unique symbol };
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 3. Functional by Default, Object-Oriented When It Fits
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// Prefer pure functions and composition
|
|
57
|
+
const pipe = <T>(...fns: Array<(arg: T) => T>) =>
|
|
58
|
+
(value: T): T => fns.reduce((acc, fn) => fn(acc), value);
|
|
59
|
+
|
|
60
|
+
const processUser = pipe(
|
|
61
|
+
validateInput,
|
|
62
|
+
normalizeEmail,
|
|
63
|
+
hashPassword,
|
|
64
|
+
persistToDb,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// Use classes for stateful services with clear lifecycle
|
|
68
|
+
class ConnectionPool {
|
|
69
|
+
readonly #connections: Map<string, Connection>;
|
|
70
|
+
constructor(private readonly config: PoolConfig) {
|
|
71
|
+
this.#connections = new Map();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### 4. Performance Is a Feature
|
|
77
|
+
|
|
78
|
+
- Profile before optimizing — measure, don't guess
|
|
79
|
+
- Understand V8 optimization patterns (monomorphic calls, hidden classes, inline caches)
|
|
80
|
+
- Know when to use `Map`/`Set` over plain objects and arrays
|
|
81
|
+
- Avoid unnecessary allocations in hot paths
|
|
82
|
+
- Use `WeakRef` and `FinalizationRegistry` when appropriate
|
|
83
|
+
|
|
84
|
+
### 5. Error Handling Is Control Flow
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
// Use Result types, not thrown exceptions for expected failures
|
|
88
|
+
function parseConfig(raw: string): Result<Config> {
|
|
89
|
+
try {
|
|
90
|
+
const parsed = JSON.parse(raw);
|
|
91
|
+
const validated = ConfigSchema.safeParse(parsed);
|
|
92
|
+
if (!validated.success) {
|
|
93
|
+
return { ok: false, error: new ValidationError(validated.error) };
|
|
94
|
+
}
|
|
95
|
+
return { ok: true, value: validated.data };
|
|
96
|
+
} catch {
|
|
97
|
+
return { ok: false, error: new ParseError('Invalid JSON') };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Reserve throw for programmer errors (invariant violations)
|
|
102
|
+
function assertNonNull<T>(value: T | null | undefined, msg: string): asserts value is T {
|
|
103
|
+
if (value == null) throw new Error(`Invariant: ${msg}`);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Project Structure
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
src/
|
|
111
|
+
├── core/ # Pure business logic, zero dependencies
|
|
112
|
+
├── adapters/ # External integrations (DB, HTTP, FS)
|
|
113
|
+
├── services/ # Orchestration layer
|
|
114
|
+
├── utils/ # Pure utility functions
|
|
115
|
+
├── types/ # TypeScript type definitions
|
|
116
|
+
├── __tests__/ # Test files (co-located or centralized)
|
|
117
|
+
└── index.ts # Public API surface
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Definition of Done
|
|
121
|
+
|
|
122
|
+
A JavaScript feature is complete when:
|
|
123
|
+
- [ ] TypeScript strict mode passes with zero errors
|
|
124
|
+
- [ ] All code paths have explicit types (no `any`, no implicit `any`)
|
|
125
|
+
- [ ] Unit tests cover logic branches and edge cases
|
|
126
|
+
- [ ] Integration tests verify external boundaries
|
|
127
|
+
- [ ] Error cases are tested, not just happy paths
|
|
128
|
+
- [ ] No floating promises (all async paths handled)
|
|
129
|
+
- [ ] No memory leaks in long-running code paths
|
|
130
|
+
- [ ] Bundle impact assessed (for client-side code)
|