omgkit 2.1.0 → 2.2.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/plugin/skills/SKILL_STANDARDS.md +743 -0
- package/plugin/skills/databases/mongodb/SKILL.md +797 -28
- package/plugin/skills/databases/postgresql/SKILL.md +494 -18
- package/plugin/skills/databases/prisma/SKILL.md +776 -30
- package/plugin/skills/databases/redis/SKILL.md +885 -25
- package/plugin/skills/devops/aws/SKILL.md +686 -28
- package/plugin/skills/devops/docker/SKILL.md +466 -18
- package/plugin/skills/devops/github-actions/SKILL.md +684 -29
- package/plugin/skills/devops/kubernetes/SKILL.md +621 -24
- package/plugin/skills/frameworks/django/SKILL.md +920 -20
- package/plugin/skills/frameworks/express/SKILL.md +1361 -35
- package/plugin/skills/frameworks/fastapi/SKILL.md +1260 -33
- package/plugin/skills/frameworks/laravel/SKILL.md +1244 -31
- package/plugin/skills/frameworks/nestjs/SKILL.md +1005 -26
- package/plugin/skills/frameworks/nextjs/SKILL.md +407 -44
- package/plugin/skills/frameworks/rails/SKILL.md +594 -28
- package/plugin/skills/frameworks/react/SKILL.md +1006 -32
- package/plugin/skills/frameworks/spring/SKILL.md +528 -35
- package/plugin/skills/frameworks/vue/SKILL.md +1296 -27
- package/plugin/skills/frontend/accessibility/SKILL.md +1108 -34
- package/plugin/skills/frontend/frontend-design/SKILL.md +1304 -26
- package/plugin/skills/frontend/responsive/SKILL.md +847 -21
- package/plugin/skills/frontend/shadcn-ui/SKILL.md +976 -38
- package/plugin/skills/frontend/tailwindcss/SKILL.md +831 -35
- package/plugin/skills/frontend/threejs/SKILL.md +1298 -29
- package/plugin/skills/languages/javascript/SKILL.md +935 -31
- package/plugin/skills/languages/python/SKILL.md +489 -25
- package/plugin/skills/languages/typescript/SKILL.md +379 -30
- package/plugin/skills/methodology/brainstorming/SKILL.md +597 -23
- package/plugin/skills/methodology/defense-in-depth/SKILL.md +832 -34
- package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +665 -31
- package/plugin/skills/methodology/executing-plans/SKILL.md +556 -24
- package/plugin/skills/methodology/finishing-development-branch/SKILL.md +595 -25
- package/plugin/skills/methodology/problem-solving/SKILL.md +429 -61
- package/plugin/skills/methodology/receiving-code-review/SKILL.md +536 -24
- package/plugin/skills/methodology/requesting-code-review/SKILL.md +632 -21
- package/plugin/skills/methodology/root-cause-tracing/SKILL.md +641 -30
- package/plugin/skills/methodology/sequential-thinking/SKILL.md +262 -3
- package/plugin/skills/methodology/systematic-debugging/SKILL.md +571 -32
- package/plugin/skills/methodology/test-driven-development/SKILL.md +779 -24
- package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +691 -29
- package/plugin/skills/methodology/token-optimization/SKILL.md +598 -29
- package/plugin/skills/methodology/verification-before-completion/SKILL.md +543 -22
- package/plugin/skills/methodology/writing-plans/SKILL.md +590 -18
- package/plugin/skills/omega/omega-architecture/SKILL.md +838 -39
- package/plugin/skills/omega/omega-coding/SKILL.md +636 -39
- package/plugin/skills/omega/omega-sprint/SKILL.md +855 -48
- package/plugin/skills/omega/omega-testing/SKILL.md +940 -41
- package/plugin/skills/omega/omega-thinking/SKILL.md +703 -50
- package/plugin/skills/security/better-auth/SKILL.md +1065 -28
- package/plugin/skills/security/oauth/SKILL.md +968 -31
- package/plugin/skills/security/owasp/SKILL.md +894 -33
- package/plugin/skills/testing/playwright/SKILL.md +764 -38
- package/plugin/skills/testing/pytest/SKILL.md +873 -36
- package/plugin/skills/testing/vitest/SKILL.md +980 -35
|
@@ -1,62 +1,966 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: javascript
|
|
3
|
-
description: JavaScript development
|
|
3
|
+
description: Modern JavaScript development with ES2024+, async patterns, functional programming, and Node.js
|
|
4
|
+
category: languages
|
|
5
|
+
triggers:
|
|
6
|
+
- javascript
|
|
7
|
+
- js
|
|
8
|
+
- es6
|
|
9
|
+
- es2024
|
|
10
|
+
- ecmascript
|
|
11
|
+
- node
|
|
12
|
+
- nodejs
|
|
13
|
+
- npm
|
|
4
14
|
---
|
|
5
15
|
|
|
6
|
-
# JavaScript
|
|
16
|
+
# JavaScript
|
|
7
17
|
|
|
8
|
-
|
|
18
|
+
Modern **JavaScript development** following industry best practices. This skill covers ES2024+ features, async patterns, functional programming, error handling, testing, and production-ready patterns used by top engineering teams.
|
|
19
|
+
|
|
20
|
+
## Purpose
|
|
21
|
+
|
|
22
|
+
Write clean, maintainable JavaScript code:
|
|
23
|
+
|
|
24
|
+
- Master modern ES2024+ syntax and features
|
|
25
|
+
- Implement robust async/await patterns
|
|
26
|
+
- Use functional programming effectively
|
|
27
|
+
- Handle errors properly
|
|
28
|
+
- Structure projects for maintainability
|
|
29
|
+
- Write comprehensive tests
|
|
30
|
+
- Build for performance
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
### 1. Modern Syntax (ES2024+)
|
|
35
|
+
|
|
36
|
+
```javascript
|
|
37
|
+
// Destructuring - Objects and Arrays
|
|
38
|
+
const user = { name: 'John', email: 'john@example.com', role: 'admin' };
|
|
39
|
+
const { name, email, role = 'user' } = user;
|
|
40
|
+
|
|
41
|
+
const numbers = [1, 2, 3, 4, 5];
|
|
42
|
+
const [first, second, ...rest] = numbers;
|
|
43
|
+
|
|
44
|
+
// Nested destructuring
|
|
45
|
+
const response = {
|
|
46
|
+
data: {
|
|
47
|
+
user: { id: 1, profile: { avatar: 'url' } }
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const { data: { user: { profile: { avatar } } } } = response;
|
|
51
|
+
|
|
52
|
+
// Spread operator - Objects and Arrays
|
|
53
|
+
const defaults = { theme: 'light', language: 'en' };
|
|
54
|
+
const settings = { ...defaults, theme: 'dark', notifications: true };
|
|
55
|
+
|
|
56
|
+
const combined = [...numbers, 6, 7, 8];
|
|
57
|
+
|
|
58
|
+
// Rest parameters
|
|
59
|
+
function sum(...numbers) {
|
|
60
|
+
return numbers.reduce((acc, n) => acc + n, 0);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Optional chaining and nullish coalescing
|
|
64
|
+
const street = user?.address?.street ?? 'Unknown';
|
|
65
|
+
const callback = options?.onComplete;
|
|
66
|
+
callback?.();
|
|
67
|
+
|
|
68
|
+
// Logical assignment operators
|
|
69
|
+
let config = {};
|
|
70
|
+
config.timeout ??= 5000;
|
|
71
|
+
config.retries ||= 3;
|
|
72
|
+
config.debug &&= process.env.NODE_ENV !== 'production';
|
|
73
|
+
|
|
74
|
+
// Array methods - at(), findLast(), toSorted(), toReversed()
|
|
75
|
+
const lastItem = numbers.at(-1);
|
|
76
|
+
const lastEven = numbers.findLast(n => n % 2 === 0);
|
|
77
|
+
const sorted = numbers.toSorted((a, b) => b - a); // Non-mutating
|
|
78
|
+
const reversed = numbers.toReversed(); // Non-mutating
|
|
79
|
+
|
|
80
|
+
// Object.groupBy() (ES2024)
|
|
81
|
+
const items = [
|
|
82
|
+
{ type: 'fruit', name: 'apple' },
|
|
83
|
+
{ type: 'vegetable', name: 'carrot' },
|
|
84
|
+
{ type: 'fruit', name: 'banana' },
|
|
85
|
+
];
|
|
86
|
+
const grouped = Object.groupBy(items, item => item.type);
|
|
87
|
+
|
|
88
|
+
// Promise.withResolvers() (ES2024)
|
|
89
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
90
|
+
|
|
91
|
+
// Template literals with tags
|
|
92
|
+
function highlight(strings, ...values) {
|
|
93
|
+
return strings.reduce((result, str, i) =>
|
|
94
|
+
`${result}${str}${values[i] ? `<mark>${values[i]}</mark>` : ''}`, ''
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
const message = highlight`Hello ${name}, your role is ${role}`;
|
|
98
|
+
|
|
99
|
+
// Private class fields
|
|
100
|
+
class Counter {
|
|
101
|
+
#count = 0;
|
|
102
|
+
|
|
103
|
+
increment() {
|
|
104
|
+
this.#count++;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
get value() {
|
|
108
|
+
return this.#count;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### 2. Async Patterns
|
|
9
114
|
|
|
10
|
-
### Async/Await
|
|
11
115
|
```javascript
|
|
12
|
-
|
|
116
|
+
// Basic async/await
|
|
117
|
+
async function fetchUser(id) {
|
|
13
118
|
try {
|
|
14
|
-
const response = await fetch(
|
|
15
|
-
|
|
16
|
-
|
|
119
|
+
const response = await fetch(`/api/users/${id}`);
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
|
122
|
+
}
|
|
123
|
+
return await response.json();
|
|
17
124
|
} catch (error) {
|
|
18
|
-
console.error('
|
|
125
|
+
console.error('Failed to fetch user:', error);
|
|
19
126
|
throw error;
|
|
20
127
|
}
|
|
21
128
|
}
|
|
129
|
+
|
|
130
|
+
// Parallel execution with Promise.all
|
|
131
|
+
async function fetchUserWithPosts(userId) {
|
|
132
|
+
const [user, posts] = await Promise.all([
|
|
133
|
+
fetchUser(userId),
|
|
134
|
+
fetchPosts(userId),
|
|
135
|
+
]);
|
|
136
|
+
return { user, posts };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Promise.allSettled for handling mixed results
|
|
140
|
+
async function fetchAllUsers(ids) {
|
|
141
|
+
const results = await Promise.allSettled(
|
|
142
|
+
ids.map(id => fetchUser(id))
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const successful = results
|
|
146
|
+
.filter(r => r.status === 'fulfilled')
|
|
147
|
+
.map(r => r.value);
|
|
148
|
+
|
|
149
|
+
const failed = results
|
|
150
|
+
.filter(r => r.status === 'rejected')
|
|
151
|
+
.map(r => r.reason);
|
|
152
|
+
|
|
153
|
+
return { successful, failed };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Promise.race for timeouts
|
|
157
|
+
async function fetchWithTimeout(url, timeout = 5000) {
|
|
158
|
+
const controller = new AbortController();
|
|
159
|
+
|
|
160
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
161
|
+
setTimeout(() => {
|
|
162
|
+
controller.abort();
|
|
163
|
+
reject(new Error('Request timeout'));
|
|
164
|
+
}, timeout);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
const fetchPromise = fetch(url, { signal: controller.signal });
|
|
168
|
+
|
|
169
|
+
return Promise.race([fetchPromise, timeoutPromise]);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Async iteration
|
|
173
|
+
async function* fetchPages(baseUrl) {
|
|
174
|
+
let page = 1;
|
|
175
|
+
let hasMore = true;
|
|
176
|
+
|
|
177
|
+
while (hasMore) {
|
|
178
|
+
const response = await fetch(`${baseUrl}?page=${page}`);
|
|
179
|
+
const data = await response.json();
|
|
180
|
+
|
|
181
|
+
yield data.items;
|
|
182
|
+
|
|
183
|
+
hasMore = data.hasMore;
|
|
184
|
+
page++;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Using async iterator
|
|
189
|
+
async function processAllPages() {
|
|
190
|
+
for await (const items of fetchPages('/api/items')) {
|
|
191
|
+
for (const item of items) {
|
|
192
|
+
await processItem(item);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Retry pattern with exponential backoff
|
|
198
|
+
async function withRetry(fn, maxRetries = 3, baseDelay = 1000) {
|
|
199
|
+
let lastError;
|
|
200
|
+
|
|
201
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
202
|
+
try {
|
|
203
|
+
return await fn();
|
|
204
|
+
} catch (error) {
|
|
205
|
+
lastError = error;
|
|
206
|
+
|
|
207
|
+
if (attempt < maxRetries - 1) {
|
|
208
|
+
const delay = baseDelay * Math.pow(2, attempt);
|
|
209
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
throw lastError;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Concurrent execution with limit
|
|
218
|
+
async function mapWithConcurrency(items, fn, limit = 5) {
|
|
219
|
+
const results = [];
|
|
220
|
+
const executing = new Set();
|
|
221
|
+
|
|
222
|
+
for (const item of items) {
|
|
223
|
+
const promise = fn(item).then(result => {
|
|
224
|
+
executing.delete(promise);
|
|
225
|
+
return result;
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
executing.add(promise);
|
|
229
|
+
results.push(promise);
|
|
230
|
+
|
|
231
|
+
if (executing.size >= limit) {
|
|
232
|
+
await Promise.race(executing);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return Promise.all(results);
|
|
237
|
+
}
|
|
22
238
|
```
|
|
23
239
|
|
|
24
|
-
###
|
|
240
|
+
### 3. Functional Programming
|
|
241
|
+
|
|
25
242
|
```javascript
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
const
|
|
243
|
+
// Pure functions
|
|
244
|
+
const add = (a, b) => a + b;
|
|
245
|
+
const multiply = (a, b) => a * b;
|
|
246
|
+
|
|
247
|
+
// Function composition
|
|
248
|
+
const compose = (...fns) => x =>
|
|
249
|
+
fns.reduceRight((acc, fn) => fn(acc), x);
|
|
250
|
+
|
|
251
|
+
const pipe = (...fns) => x =>
|
|
252
|
+
fns.reduce((acc, fn) => fn(acc), x);
|
|
253
|
+
|
|
254
|
+
// Currying
|
|
255
|
+
const curry = fn => {
|
|
256
|
+
return function curried(...args) {
|
|
257
|
+
if (args.length >= fn.length) {
|
|
258
|
+
return fn.apply(this, args);
|
|
259
|
+
}
|
|
260
|
+
return (...nextArgs) => curried(...args, ...nextArgs);
|
|
261
|
+
};
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const addCurried = curry((a, b, c) => a + b + c);
|
|
265
|
+
const add5 = addCurried(5);
|
|
266
|
+
const add5and3 = add5(3);
|
|
267
|
+
const result = add5and3(2); // 10
|
|
268
|
+
|
|
269
|
+
// Partial application
|
|
270
|
+
const partial = (fn, ...presetArgs) =>
|
|
271
|
+
(...laterArgs) => fn(...presetArgs, ...laterArgs);
|
|
272
|
+
|
|
273
|
+
const greet = (greeting, name) => `${greeting}, ${name}!`;
|
|
274
|
+
const sayHello = partial(greet, 'Hello');
|
|
275
|
+
sayHello('World'); // "Hello, World!"
|
|
276
|
+
|
|
277
|
+
// Higher-order functions
|
|
278
|
+
const map = fn => arr => arr.map(fn);
|
|
279
|
+
const filter = pred => arr => arr.filter(pred);
|
|
280
|
+
const reduce = (fn, initial) => arr => arr.reduce(fn, initial);
|
|
281
|
+
|
|
282
|
+
// Point-free style
|
|
283
|
+
const double = x => x * 2;
|
|
284
|
+
const isEven = x => x % 2 === 0;
|
|
285
|
+
const sum = (a, b) => a + b;
|
|
286
|
+
|
|
287
|
+
const processNumbers = pipe(
|
|
288
|
+
filter(isEven),
|
|
289
|
+
map(double),
|
|
290
|
+
reduce(sum, 0)
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
processNumbers([1, 2, 3, 4, 5]); // 12
|
|
294
|
+
|
|
295
|
+
// Immutable operations
|
|
296
|
+
const updateUser = (user, updates) => ({
|
|
297
|
+
...user,
|
|
298
|
+
...updates,
|
|
299
|
+
updatedAt: new Date().toISOString(),
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
const addItem = (array, item) => [...array, item];
|
|
303
|
+
const removeItem = (array, index) => [
|
|
304
|
+
...array.slice(0, index),
|
|
305
|
+
...array.slice(index + 1),
|
|
306
|
+
];
|
|
307
|
+
const updateItem = (array, index, item) => [
|
|
308
|
+
...array.slice(0, index),
|
|
309
|
+
item,
|
|
310
|
+
...array.slice(index + 1),
|
|
311
|
+
];
|
|
312
|
+
|
|
313
|
+
// Memoization
|
|
314
|
+
const memoize = fn => {
|
|
315
|
+
const cache = new Map();
|
|
316
|
+
|
|
317
|
+
return (...args) => {
|
|
318
|
+
const key = JSON.stringify(args);
|
|
319
|
+
|
|
320
|
+
if (cache.has(key)) {
|
|
321
|
+
return cache.get(key);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const result = fn(...args);
|
|
325
|
+
cache.set(key, result);
|
|
326
|
+
return result;
|
|
327
|
+
};
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
const expensiveCalculation = memoize((n) => {
|
|
331
|
+
console.log('Computing...');
|
|
332
|
+
return n * n;
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// Option/Maybe pattern
|
|
336
|
+
const Option = {
|
|
337
|
+
some: value => ({
|
|
338
|
+
isSome: true,
|
|
339
|
+
isNone: false,
|
|
340
|
+
map: fn => Option.some(fn(value)),
|
|
341
|
+
flatMap: fn => fn(value),
|
|
342
|
+
getOrElse: () => value,
|
|
343
|
+
filter: pred => pred(value) ? Option.some(value) : Option.none(),
|
|
344
|
+
}),
|
|
345
|
+
|
|
346
|
+
none: () => ({
|
|
347
|
+
isSome: false,
|
|
348
|
+
isNone: true,
|
|
349
|
+
map: () => Option.none(),
|
|
350
|
+
flatMap: () => Option.none(),
|
|
351
|
+
getOrElse: defaultValue => defaultValue,
|
|
352
|
+
filter: () => Option.none(),
|
|
353
|
+
}),
|
|
354
|
+
|
|
355
|
+
fromNullable: value =>
|
|
356
|
+
value != null ? Option.some(value) : Option.none(),
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
// Usage
|
|
360
|
+
const userName = Option.fromNullable(user?.name)
|
|
361
|
+
.map(name => name.toUpperCase())
|
|
362
|
+
.getOrElse('Anonymous');
|
|
29
363
|
```
|
|
30
364
|
|
|
31
|
-
###
|
|
365
|
+
### 4. Error Handling
|
|
366
|
+
|
|
32
367
|
```javascript
|
|
33
|
-
|
|
34
|
-
|
|
368
|
+
// Custom error classes
|
|
369
|
+
class AppError extends Error {
|
|
370
|
+
constructor(message, code, statusCode = 500) {
|
|
371
|
+
super(message);
|
|
372
|
+
this.name = this.constructor.name;
|
|
373
|
+
this.code = code;
|
|
374
|
+
this.statusCode = statusCode;
|
|
375
|
+
Error.captureStackTrace(this, this.constructor);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
toJSON() {
|
|
379
|
+
return {
|
|
380
|
+
name: this.name,
|
|
381
|
+
message: this.message,
|
|
382
|
+
code: this.code,
|
|
383
|
+
statusCode: this.statusCode,
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
class ValidationError extends AppError {
|
|
389
|
+
constructor(field, message) {
|
|
390
|
+
super(message, 'VALIDATION_ERROR', 400);
|
|
391
|
+
this.field = field;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
class NotFoundError extends AppError {
|
|
396
|
+
constructor(resource, id) {
|
|
397
|
+
super(`${resource} with id ${id} not found`, 'NOT_FOUND', 404);
|
|
398
|
+
this.resource = resource;
|
|
399
|
+
this.resourceId = id;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
class UnauthorizedError extends AppError {
|
|
404
|
+
constructor(message = 'Unauthorized') {
|
|
405
|
+
super(message, 'UNAUTHORIZED', 401);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// Result type pattern
|
|
410
|
+
class Result {
|
|
411
|
+
constructor(value, error) {
|
|
412
|
+
this.value = value;
|
|
413
|
+
this.error = error;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
static ok(value) {
|
|
417
|
+
return new Result(value, null);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
static err(error) {
|
|
421
|
+
return new Result(null, error);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
isOk() {
|
|
425
|
+
return this.error === null;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
isErr() {
|
|
429
|
+
return this.error !== null;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
map(fn) {
|
|
433
|
+
return this.isOk() ? Result.ok(fn(this.value)) : this;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
flatMap(fn) {
|
|
437
|
+
return this.isOk() ? fn(this.value) : this;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
mapErr(fn) {
|
|
441
|
+
return this.isErr() ? Result.err(fn(this.error)) : this;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
unwrap() {
|
|
445
|
+
if (this.isErr()) throw this.error;
|
|
446
|
+
return this.value;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
unwrapOr(defaultValue) {
|
|
450
|
+
return this.isOk() ? this.value : defaultValue;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
match({ ok, err }) {
|
|
454
|
+
return this.isOk() ? ok(this.value) : err(this.error);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Usage
|
|
459
|
+
async function safeParseJSON(text) {
|
|
460
|
+
try {
|
|
461
|
+
return Result.ok(JSON.parse(text));
|
|
462
|
+
} catch (error) {
|
|
463
|
+
return Result.err(new ValidationError('json', 'Invalid JSON'));
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
async function safeFetch(url) {
|
|
468
|
+
try {
|
|
469
|
+
const response = await fetch(url);
|
|
470
|
+
if (!response.ok) {
|
|
471
|
+
return Result.err(new AppError(`HTTP ${response.status}`, 'HTTP_ERROR'));
|
|
472
|
+
}
|
|
473
|
+
const data = await response.json();
|
|
474
|
+
return Result.ok(data);
|
|
475
|
+
} catch (error) {
|
|
476
|
+
return Result.err(error);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Error boundary pattern
|
|
481
|
+
async function withErrorBoundary(fn, fallback) {
|
|
482
|
+
try {
|
|
483
|
+
return await fn();
|
|
484
|
+
} catch (error) {
|
|
485
|
+
console.error('Error caught in boundary:', error);
|
|
486
|
+
|
|
487
|
+
if (typeof fallback === 'function') {
|
|
488
|
+
return fallback(error);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
return fallback;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Global error handling
|
|
496
|
+
process.on('uncaughtException', (error) => {
|
|
497
|
+
console.error('Uncaught Exception:', error);
|
|
498
|
+
process.exit(1);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
502
|
+
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
503
|
+
});
|
|
35
504
|
```
|
|
36
505
|
|
|
37
|
-
### Modules
|
|
506
|
+
### 5. Modules and Project Structure
|
|
507
|
+
|
|
38
508
|
```javascript
|
|
39
509
|
// Named exports
|
|
40
|
-
|
|
41
|
-
export
|
|
510
|
+
// utils/string.js
|
|
511
|
+
export const capitalize = str =>
|
|
512
|
+
str.charAt(0).toUpperCase() + str.slice(1);
|
|
513
|
+
|
|
514
|
+
export const slugify = str =>
|
|
515
|
+
str.toLowerCase().replace(/\s+/g, '-').replace(/[^a-z0-9-]/g, '');
|
|
516
|
+
|
|
517
|
+
export const truncate = (str, length, suffix = '...') =>
|
|
518
|
+
str.length > length ? str.slice(0, length) + suffix : str;
|
|
519
|
+
|
|
520
|
+
// Default export with named exports
|
|
521
|
+
// services/api.js
|
|
522
|
+
class ApiService {
|
|
523
|
+
constructor(baseUrl) {
|
|
524
|
+
this.baseUrl = baseUrl;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
async get(path) {
|
|
528
|
+
const response = await fetch(`${this.baseUrl}${path}`);
|
|
529
|
+
return response.json();
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
async post(path, data) {
|
|
533
|
+
const response = await fetch(`${this.baseUrl}${path}`, {
|
|
534
|
+
method: 'POST',
|
|
535
|
+
headers: { 'Content-Type': 'application/json' },
|
|
536
|
+
body: JSON.stringify(data),
|
|
537
|
+
});
|
|
538
|
+
return response.json();
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
export default ApiService;
|
|
543
|
+
export const createApi = baseUrl => new ApiService(baseUrl);
|
|
544
|
+
|
|
545
|
+
// Re-exports (barrel exports)
|
|
546
|
+
// services/index.js
|
|
547
|
+
export { default as ApiService, createApi } from './api.js';
|
|
548
|
+
export { default as AuthService } from './auth.js';
|
|
549
|
+
export { default as UserService } from './user.js';
|
|
550
|
+
export * from './constants.js';
|
|
551
|
+
|
|
552
|
+
// Dynamic imports
|
|
553
|
+
async function loadModule(moduleName) {
|
|
554
|
+
try {
|
|
555
|
+
const module = await import(`./modules/${moduleName}.js`);
|
|
556
|
+
return module.default;
|
|
557
|
+
} catch (error) {
|
|
558
|
+
console.error(`Failed to load module: ${moduleName}`);
|
|
559
|
+
return null;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
42
562
|
|
|
43
|
-
//
|
|
44
|
-
|
|
563
|
+
// Conditional imports
|
|
564
|
+
async function loadFeature() {
|
|
565
|
+
if (process.env.FEATURE_FLAG) {
|
|
566
|
+
const { enableFeature } = await import('./features/newFeature.js');
|
|
567
|
+
enableFeature();
|
|
568
|
+
}
|
|
569
|
+
}
|
|
45
570
|
|
|
46
|
-
//
|
|
47
|
-
|
|
571
|
+
// Project structure example
|
|
572
|
+
/*
|
|
573
|
+
src/
|
|
574
|
+
├── index.js # Entry point
|
|
575
|
+
├── config/
|
|
576
|
+
│ ├── index.js # Configuration exports
|
|
577
|
+
│ ├── database.js
|
|
578
|
+
│ └── server.js
|
|
579
|
+
├── services/
|
|
580
|
+
│ ├── index.js # Barrel exports
|
|
581
|
+
│ ├── api.js
|
|
582
|
+
│ ├── auth.js
|
|
583
|
+
│ └── user.js
|
|
584
|
+
├── utils/
|
|
585
|
+
│ ├── index.js
|
|
586
|
+
│ ├── string.js
|
|
587
|
+
│ ├── date.js
|
|
588
|
+
│ └── validation.js
|
|
589
|
+
├── models/
|
|
590
|
+
│ ├── index.js
|
|
591
|
+
│ ├── User.js
|
|
592
|
+
│ └── Post.js
|
|
593
|
+
└── middleware/
|
|
594
|
+
├── index.js
|
|
595
|
+
├── auth.js
|
|
596
|
+
└── validation.js
|
|
597
|
+
*/
|
|
48
598
|
```
|
|
49
599
|
|
|
50
|
-
###
|
|
600
|
+
### 6. Data Structures and Collections
|
|
601
|
+
|
|
51
602
|
```javascript
|
|
52
|
-
|
|
53
|
-
const
|
|
603
|
+
// Map - key-value with any type keys
|
|
604
|
+
const userCache = new Map();
|
|
605
|
+
userCache.set('user:1', { id: 1, name: 'John' });
|
|
606
|
+
userCache.set('user:2', { id: 2, name: 'Jane' });
|
|
607
|
+
|
|
608
|
+
// Map iteration
|
|
609
|
+
for (const [key, value] of userCache) {
|
|
610
|
+
console.log(key, value);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// Map with object keys
|
|
614
|
+
const objectMap = new Map();
|
|
615
|
+
const key1 = { id: 1 };
|
|
616
|
+
const key2 = { id: 2 };
|
|
617
|
+
objectMap.set(key1, 'value1');
|
|
618
|
+
objectMap.set(key2, 'value2');
|
|
619
|
+
|
|
620
|
+
// Set - unique values
|
|
621
|
+
const uniqueIds = new Set([1, 2, 3, 1, 2]); // Set(3) {1, 2, 3}
|
|
622
|
+
uniqueIds.add(4);
|
|
623
|
+
uniqueIds.has(1); // true
|
|
624
|
+
uniqueIds.delete(1);
|
|
625
|
+
|
|
626
|
+
// Set operations
|
|
627
|
+
const setA = new Set([1, 2, 3, 4]);
|
|
628
|
+
const setB = new Set([3, 4, 5, 6]);
|
|
629
|
+
|
|
630
|
+
const union = new Set([...setA, ...setB]);
|
|
631
|
+
const intersection = new Set([...setA].filter(x => setB.has(x)));
|
|
632
|
+
const difference = new Set([...setA].filter(x => !setB.has(x)));
|
|
633
|
+
|
|
634
|
+
// WeakMap - garbage-collected keys
|
|
635
|
+
const privateData = new WeakMap();
|
|
636
|
+
|
|
637
|
+
class User {
|
|
638
|
+
constructor(name, secret) {
|
|
639
|
+
privateData.set(this, { secret });
|
|
640
|
+
this.name = name;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
getSecret() {
|
|
644
|
+
return privateData.get(this).secret;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
// WeakSet - garbage-collected values
|
|
649
|
+
const visitedNodes = new WeakSet();
|
|
650
|
+
|
|
651
|
+
function traverseOnce(node) {
|
|
652
|
+
if (visitedNodes.has(node)) return;
|
|
653
|
+
visitedNodes.add(node);
|
|
654
|
+
// Process node
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// Typed Arrays for binary data
|
|
658
|
+
const buffer = new ArrayBuffer(16);
|
|
659
|
+
const int32View = new Int32Array(buffer);
|
|
660
|
+
const float64View = new Float64Array(buffer);
|
|
661
|
+
|
|
662
|
+
int32View[0] = 42;
|
|
663
|
+
float64View[1] = 3.14;
|
|
664
|
+
|
|
665
|
+
// Proxy for reactive objects
|
|
666
|
+
function reactive(target) {
|
|
667
|
+
return new Proxy(target, {
|
|
668
|
+
get(obj, prop) {
|
|
669
|
+
console.log(`Getting ${prop}`);
|
|
670
|
+
return obj[prop];
|
|
671
|
+
},
|
|
672
|
+
set(obj, prop, value) {
|
|
673
|
+
console.log(`Setting ${prop} to ${value}`);
|
|
674
|
+
obj[prop] = value;
|
|
675
|
+
return true;
|
|
676
|
+
},
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const state = reactive({ count: 0 });
|
|
681
|
+
state.count++; // Logs: Getting count, Setting count to 1
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
### 7. Testing Patterns
|
|
685
|
+
|
|
686
|
+
```javascript
|
|
687
|
+
// Jest/Vitest test structure
|
|
688
|
+
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
689
|
+
import { UserService } from './user.service.js';
|
|
690
|
+
import { ApiService } from './api.service.js';
|
|
691
|
+
|
|
692
|
+
// Mock the API service
|
|
693
|
+
vi.mock('./api.service.js');
|
|
694
|
+
|
|
695
|
+
describe('UserService', () => {
|
|
696
|
+
let userService;
|
|
697
|
+
let mockApi;
|
|
698
|
+
|
|
699
|
+
beforeEach(() => {
|
|
700
|
+
mockApi = {
|
|
701
|
+
get: vi.fn(),
|
|
702
|
+
post: vi.fn(),
|
|
703
|
+
patch: vi.fn(),
|
|
704
|
+
delete: vi.fn(),
|
|
705
|
+
};
|
|
706
|
+
userService = new UserService(mockApi);
|
|
707
|
+
});
|
|
708
|
+
|
|
709
|
+
afterEach(() => {
|
|
710
|
+
vi.clearAllMocks();
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
describe('getUser', () => {
|
|
714
|
+
it('should return user when found', async () => {
|
|
715
|
+
const mockUser = { id: '1', name: 'John', email: 'john@example.com' };
|
|
716
|
+
mockApi.get.mockResolvedValue(mockUser);
|
|
717
|
+
|
|
718
|
+
const user = await userService.getUser('1');
|
|
719
|
+
|
|
720
|
+
expect(mockApi.get).toHaveBeenCalledWith('/users/1');
|
|
721
|
+
expect(user).toEqual(mockUser);
|
|
722
|
+
});
|
|
723
|
+
|
|
724
|
+
it('should throw NotFoundError when user not found', async () => {
|
|
725
|
+
mockApi.get.mockRejectedValue({ status: 404 });
|
|
726
|
+
|
|
727
|
+
await expect(userService.getUser('999'))
|
|
728
|
+
.rejects.toThrow('User not found');
|
|
729
|
+
});
|
|
730
|
+
});
|
|
731
|
+
|
|
732
|
+
describe('createUser', () => {
|
|
733
|
+
it('should create user with valid data', async () => {
|
|
734
|
+
const userData = { name: 'John', email: 'john@example.com' };
|
|
735
|
+
const createdUser = { id: '1', ...userData };
|
|
736
|
+
mockApi.post.mockResolvedValue(createdUser);
|
|
737
|
+
|
|
738
|
+
const user = await userService.createUser(userData);
|
|
739
|
+
|
|
740
|
+
expect(mockApi.post).toHaveBeenCalledWith('/users', userData);
|
|
741
|
+
expect(user).toEqual(createdUser);
|
|
742
|
+
});
|
|
743
|
+
|
|
744
|
+
it('should throw ValidationError for invalid email', async () => {
|
|
745
|
+
const userData = { name: 'John', email: 'invalid' };
|
|
746
|
+
|
|
747
|
+
await expect(userService.createUser(userData))
|
|
748
|
+
.rejects.toThrow('Invalid email format');
|
|
749
|
+
});
|
|
750
|
+
});
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
// Integration test example
|
|
754
|
+
describe('UserService Integration', () => {
|
|
755
|
+
it('should create and fetch user', async () => {
|
|
756
|
+
const userService = new UserService(new ApiService('/api'));
|
|
757
|
+
|
|
758
|
+
const created = await userService.createUser({
|
|
759
|
+
name: 'Test User',
|
|
760
|
+
email: 'test@example.com',
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
const fetched = await userService.getUser(created.id);
|
|
764
|
+
|
|
765
|
+
expect(fetched.name).toBe('Test User');
|
|
766
|
+
expect(fetched.email).toBe('test@example.com');
|
|
767
|
+
});
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
// Testing async code
|
|
771
|
+
describe('Async operations', () => {
|
|
772
|
+
it('should handle promises', async () => {
|
|
773
|
+
const result = await asyncOperation();
|
|
774
|
+
expect(result).toBe('expected');
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
it('should handle rejected promises', async () => {
|
|
778
|
+
await expect(failingOperation()).rejects.toThrow('error message');
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
it('should use fake timers', () => {
|
|
782
|
+
vi.useFakeTimers();
|
|
783
|
+
|
|
784
|
+
const callback = vi.fn();
|
|
785
|
+
setTimeout(callback, 1000);
|
|
786
|
+
|
|
787
|
+
vi.advanceTimersByTime(1000);
|
|
788
|
+
|
|
789
|
+
expect(callback).toHaveBeenCalled();
|
|
790
|
+
|
|
791
|
+
vi.useRealTimers();
|
|
792
|
+
});
|
|
793
|
+
});
|
|
794
|
+
```
|
|
795
|
+
|
|
796
|
+
## Use Cases
|
|
797
|
+
|
|
798
|
+
### Event Emitter Pattern
|
|
799
|
+
|
|
800
|
+
```javascript
|
|
801
|
+
class EventEmitter {
|
|
802
|
+
constructor() {
|
|
803
|
+
this.events = new Map();
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
on(event, listener) {
|
|
807
|
+
if (!this.events.has(event)) {
|
|
808
|
+
this.events.set(event, new Set());
|
|
809
|
+
}
|
|
810
|
+
this.events.get(event).add(listener);
|
|
811
|
+
return () => this.off(event, listener);
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
off(event, listener) {
|
|
815
|
+
const listeners = this.events.get(event);
|
|
816
|
+
if (listeners) {
|
|
817
|
+
listeners.delete(listener);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
once(event, listener) {
|
|
822
|
+
const wrapper = (...args) => {
|
|
823
|
+
listener(...args);
|
|
824
|
+
this.off(event, wrapper);
|
|
825
|
+
};
|
|
826
|
+
return this.on(event, wrapper);
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
emit(event, ...args) {
|
|
830
|
+
const listeners = this.events.get(event);
|
|
831
|
+
if (listeners) {
|
|
832
|
+
for (const listener of listeners) {
|
|
833
|
+
listener(...args);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
// Usage
|
|
840
|
+
const emitter = new EventEmitter();
|
|
841
|
+
|
|
842
|
+
const unsubscribe = emitter.on('user:created', user => {
|
|
843
|
+
console.log('User created:', user);
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
emitter.emit('user:created', { id: 1, name: 'John' });
|
|
847
|
+
unsubscribe();
|
|
848
|
+
```
|
|
849
|
+
|
|
850
|
+
### State Machine
|
|
851
|
+
|
|
852
|
+
```javascript
|
|
853
|
+
function createStateMachine(config) {
|
|
854
|
+
let currentState = config.initial;
|
|
855
|
+
|
|
856
|
+
return {
|
|
857
|
+
get state() {
|
|
858
|
+
return currentState;
|
|
859
|
+
},
|
|
860
|
+
|
|
861
|
+
can(event) {
|
|
862
|
+
const stateConfig = config.states[currentState];
|
|
863
|
+
return stateConfig?.on?.[event] !== undefined;
|
|
864
|
+
},
|
|
865
|
+
|
|
866
|
+
transition(event, payload) {
|
|
867
|
+
const stateConfig = config.states[currentState];
|
|
868
|
+
const transition = stateConfig?.on?.[event];
|
|
869
|
+
|
|
870
|
+
if (!transition) {
|
|
871
|
+
throw new Error(`Invalid transition: ${event} from ${currentState}`);
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
const nextState = typeof transition === 'string'
|
|
875
|
+
? transition
|
|
876
|
+
: transition.target;
|
|
877
|
+
|
|
878
|
+
// Execute exit action
|
|
879
|
+
stateConfig?.exit?.();
|
|
880
|
+
|
|
881
|
+
// Execute transition action
|
|
882
|
+
if (typeof transition === 'object' && transition.action) {
|
|
883
|
+
transition.action(payload);
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
currentState = nextState;
|
|
887
|
+
|
|
888
|
+
// Execute entry action
|
|
889
|
+
config.states[nextState]?.entry?.();
|
|
890
|
+
|
|
891
|
+
return currentState;
|
|
892
|
+
},
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// Usage
|
|
897
|
+
const orderMachine = createStateMachine({
|
|
898
|
+
initial: 'pending',
|
|
899
|
+
states: {
|
|
900
|
+
pending: {
|
|
901
|
+
on: {
|
|
902
|
+
CONFIRM: 'confirmed',
|
|
903
|
+
CANCEL: 'cancelled',
|
|
904
|
+
},
|
|
905
|
+
},
|
|
906
|
+
confirmed: {
|
|
907
|
+
entry: () => console.log('Order confirmed!'),
|
|
908
|
+
on: {
|
|
909
|
+
SHIP: 'shipped',
|
|
910
|
+
CANCEL: 'cancelled',
|
|
911
|
+
},
|
|
912
|
+
},
|
|
913
|
+
shipped: {
|
|
914
|
+
on: {
|
|
915
|
+
DELIVER: 'delivered',
|
|
916
|
+
},
|
|
917
|
+
},
|
|
918
|
+
delivered: {
|
|
919
|
+
entry: () => console.log('Order delivered!'),
|
|
920
|
+
},
|
|
921
|
+
cancelled: {
|
|
922
|
+
entry: () => console.log('Order cancelled'),
|
|
923
|
+
},
|
|
924
|
+
},
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
orderMachine.transition('CONFIRM');
|
|
928
|
+
orderMachine.transition('SHIP');
|
|
929
|
+
orderMachine.transition('DELIVER');
|
|
54
930
|
```
|
|
55
931
|
|
|
56
932
|
## Best Practices
|
|
57
|
-
|
|
933
|
+
|
|
934
|
+
### Do's
|
|
935
|
+
|
|
936
|
+
- Use `const` by default, `let` when needed
|
|
58
937
|
- Use arrow functions for callbacks
|
|
59
|
-
- Use template literals
|
|
60
|
-
- Use
|
|
61
|
-
- Use
|
|
62
|
-
- Use
|
|
938
|
+
- Use template literals for string interpolation
|
|
939
|
+
- Use destructuring for cleaner code
|
|
940
|
+
- Use async/await over raw promises
|
|
941
|
+
- Use optional chaining and nullish coalescing
|
|
942
|
+
- Use modules (ESM) for code organization
|
|
943
|
+
- Handle errors explicitly
|
|
944
|
+
- Write pure functions when possible
|
|
945
|
+
- Use meaningful variable and function names
|
|
946
|
+
|
|
947
|
+
### Don'ts
|
|
948
|
+
|
|
949
|
+
- Don't use `var` - use `const` or `let`
|
|
950
|
+
- Don't use `==` - use `===` for comparisons
|
|
951
|
+
- Don't mutate function arguments
|
|
952
|
+
- Don't use `arguments` - use rest parameters
|
|
953
|
+
- Don't nest callbacks deeply (callback hell)
|
|
954
|
+
- Don't ignore promise rejections
|
|
955
|
+
- Don't use `eval()` or `Function()` constructor
|
|
956
|
+
- Don't rely on type coercion
|
|
957
|
+
- Don't pollute the global namespace
|
|
958
|
+
- Don't use synchronous operations in async code
|
|
959
|
+
|
|
960
|
+
## References
|
|
961
|
+
|
|
962
|
+
- [MDN JavaScript Guide](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide)
|
|
963
|
+
- [ECMAScript Specification](https://tc39.es/ecma262/)
|
|
964
|
+
- [JavaScript Info](https://javascript.info/)
|
|
965
|
+
- [Node.js Documentation](https://nodejs.org/docs/)
|
|
966
|
+
- [Clean Code JavaScript](https://github.com/ryanmcdermott/clean-code-javascript)
|