@mytechtoday/augment-extensions 0.1.0 → 0.1.2
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/AGENTS.md +83 -3
- package/README.md +6 -5
- package/augment-extensions/coding-standards/python/README.md +44 -0
- package/augment-extensions/coding-standards/python/module.json +26 -0
- package/augment-extensions/coding-standards/python/rules/best-practices.md +232 -0
- package/augment-extensions/coding-standards/python/rules/code-organization.md +220 -0
- package/augment-extensions/coding-standards/python/rules/error-handling.md +221 -0
- package/augment-extensions/coding-standards/python/rules/naming-conventions.md +172 -0
- package/augment-extensions/coding-standards/python/rules/type-hints.md +188 -0
- package/augment-extensions/coding-standards/react/README.md +45 -0
- package/augment-extensions/coding-standards/react/module.json +27 -0
- package/augment-extensions/coding-standards/react/rules/component-patterns.md +214 -0
- package/augment-extensions/coding-standards/react/rules/hooks-best-practices.md +235 -0
- package/augment-extensions/coding-standards/react/rules/performance.md +300 -0
- package/augment-extensions/coding-standards/react/rules/state-management.md +265 -0
- package/augment-extensions/coding-standards/react/rules/typescript-react.md +271 -0
- package/augment-extensions/domain-rules/api-design/README.md +41 -0
- package/augment-extensions/domain-rules/api-design/module.json +27 -0
- package/augment-extensions/domain-rules/api-design/rules/authentication.md +263 -0
- package/augment-extensions/domain-rules/api-design/rules/documentation.md +395 -0
- package/augment-extensions/domain-rules/api-design/rules/error-handling.md +290 -0
- package/augment-extensions/domain-rules/api-design/rules/graphql-api.md +313 -0
- package/augment-extensions/domain-rules/api-design/rules/rest-api.md +214 -0
- package/augment-extensions/domain-rules/api-design/rules/versioning.md +268 -0
- package/augment-extensions/domain-rules/security/README.md +41 -0
- package/augment-extensions/domain-rules/security/module.json +28 -0
- package/augment-extensions/domain-rules/security/rules/authentication-security.md +361 -0
- package/augment-extensions/domain-rules/security/rules/encryption.md +208 -0
- package/augment-extensions/domain-rules/security/rules/input-validation.md +294 -0
- package/augment-extensions/domain-rules/security/rules/owasp-top-10.md +339 -0
- package/augment-extensions/domain-rules/security/rules/secure-coding.md +293 -0
- package/augment-extensions/domain-rules/security/rules/web-security.md +268 -0
- package/augment-extensions/examples/design-patterns/README.md +37 -0
- package/augment-extensions/examples/design-patterns/examples/behavioral-patterns.md +370 -0
- package/augment-extensions/examples/design-patterns/examples/creational-patterns.md +250 -0
- package/augment-extensions/examples/design-patterns/examples/structural-patterns.md +264 -0
- package/augment-extensions/examples/design-patterns/module.json +27 -0
- package/{modules → augment-extensions}/workflows/beads/examples/complete-workflow-example.md +5 -5
- package/{modules → augment-extensions}/workflows/beads/rules/file-format.md +45 -1
- package/{modules → augment-extensions}/workflows/beads/rules/workflow.md +41 -0
- package/{modules → augment-extensions}/workflows/openspec/examples/complete-change-example.md +14 -0
- package/{modules → augment-extensions}/workflows/openspec/rules/spec-format.md +44 -1
- package/{modules → augment-extensions}/workflows/openspec/rules/workflow.md +25 -0
- package/cli/dist/cli.js +69 -1
- package/cli/dist/cli.js.map +1 -1
- package/cli/dist/commands/coord.d.ts +30 -0
- package/cli/dist/commands/coord.d.ts.map +1 -0
- package/cli/dist/commands/coord.js +150 -0
- package/cli/dist/commands/coord.js.map +1 -0
- package/cli/dist/commands/link.js +1 -1
- package/cli/dist/commands/link.js.map +1 -1
- package/cli/dist/commands/list.js +1 -1
- package/cli/dist/commands/list.js.map +1 -1
- package/cli/dist/commands/search.d.ts.map +1 -1
- package/cli/dist/commands/search.js +107 -5
- package/cli/dist/commands/search.js.map +1 -1
- package/cli/dist/commands/show.js +1 -1
- package/cli/dist/commands/show.js.map +1 -1
- package/cli/dist/commands/sync.d.ts +26 -0
- package/cli/dist/commands/sync.d.ts.map +1 -0
- package/cli/dist/commands/sync.js +106 -0
- package/cli/dist/commands/sync.js.map +1 -0
- package/cli/dist/commands/update.d.ts.map +1 -1
- package/cli/dist/commands/update.js +132 -7
- package/cli/dist/commands/update.js.map +1 -1
- package/cli/dist/utils/auto-sync.d.ts +34 -0
- package/cli/dist/utils/auto-sync.d.ts.map +1 -0
- package/cli/dist/utils/auto-sync.js +172 -0
- package/cli/dist/utils/auto-sync.js.map +1 -0
- package/cli/dist/utils/beads-sync.d.ts +51 -0
- package/cli/dist/utils/beads-sync.d.ts.map +1 -0
- package/cli/dist/utils/beads-sync.js +171 -0
- package/cli/dist/utils/beads-sync.js.map +1 -0
- package/cli/dist/utils/coordination-queries.d.ts +79 -0
- package/cli/dist/utils/coordination-queries.d.ts.map +1 -0
- package/cli/dist/utils/coordination-queries.js +155 -0
- package/cli/dist/utils/coordination-queries.js.map +1 -0
- package/cli/dist/utils/file-tracking.d.ts +42 -0
- package/cli/dist/utils/file-tracking.d.ts.map +1 -0
- package/cli/dist/utils/file-tracking.js +155 -0
- package/cli/dist/utils/file-tracking.js.map +1 -0
- package/cli/dist/utils/migrate.d.ts +25 -0
- package/cli/dist/utils/migrate.d.ts.map +1 -0
- package/cli/dist/utils/migrate.js +204 -0
- package/cli/dist/utils/migrate.js.map +1 -0
- package/cli/dist/utils/openspec-sync.d.ts +48 -0
- package/cli/dist/utils/openspec-sync.d.ts.map +1 -0
- package/cli/dist/utils/openspec-sync.js +167 -0
- package/cli/dist/utils/openspec-sync.js.map +1 -0
- package/{MODULES.md → modules.md} +1 -1
- package/package.json +9 -7
- /package/{modules → augment-extensions}/coding-standards/typescript/README.md +0 -0
- /package/{modules → augment-extensions}/coding-standards/typescript/module.json +0 -0
- /package/{modules → augment-extensions}/coding-standards/typescript/rules/naming-conventions.md +0 -0
- /package/{modules → augment-extensions}/workflows/beads/README.md +0 -0
- /package/{modules → augment-extensions}/workflows/beads/module.json +0 -0
- /package/{modules → augment-extensions}/workflows/beads/rules/best-practices.md +0 -0
- /package/{modules → augment-extensions}/workflows/beads/rules/manual-setup.md +0 -0
- /package/{modules → augment-extensions}/workflows/openspec/README.md +0 -0
- /package/{modules → augment-extensions}/workflows/openspec/module.json +0 -0
- /package/{modules → augment-extensions}/workflows/openspec/rules/best-practices.md +0 -0
- /package/{modules → augment-extensions}/workflows/openspec/rules/manual-setup.md +0 -0
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# Web Security
|
|
2
|
+
|
|
3
|
+
Web-specific security vulnerabilities and mitigations.
|
|
4
|
+
|
|
5
|
+
## Cross-Site Scripting (XSS)
|
|
6
|
+
|
|
7
|
+
### Reflected XSS
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
// Bad - Reflected XSS
|
|
11
|
+
app.get('/search', (req, res) => {
|
|
12
|
+
res.send(`<h1>Results for: ${req.query.q}</h1>`); // ❌ XSS vulnerability
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
// Good - Escape output
|
|
16
|
+
import escapeHtml from 'escape-html';
|
|
17
|
+
|
|
18
|
+
app.get('/search', (req, res) => {
|
|
19
|
+
res.send(`<h1>Results for: ${escapeHtml(req.query.q)}</h1>`);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Better - Use template engine with auto-escaping
|
|
23
|
+
app.get('/search', (req, res) => {
|
|
24
|
+
res.render('search', { query: req.query.q }); // Handlebars, EJS auto-escape
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Stored XSS
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
// Bad - Storing unsanitized HTML
|
|
32
|
+
app.post('/comments', async (req, res) => {
|
|
33
|
+
await db.comments.create({
|
|
34
|
+
content: req.body.content // ❌ Stores malicious script
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Good - Sanitize HTML
|
|
39
|
+
import DOMPurify from 'isomorphic-dompurify';
|
|
40
|
+
|
|
41
|
+
app.post('/comments', async (req, res) => {
|
|
42
|
+
const sanitized = DOMPurify.sanitize(req.body.content, {
|
|
43
|
+
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
|
|
44
|
+
ALLOWED_ATTR: ['href']
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
await db.comments.create({ content: sanitized });
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Content Security Policy (CSP)
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
// Good - Strict CSP
|
|
55
|
+
app.use((req, res, next) => {
|
|
56
|
+
res.setHeader(
|
|
57
|
+
'Content-Security-Policy',
|
|
58
|
+
[
|
|
59
|
+
"default-src 'self'",
|
|
60
|
+
"script-src 'self'",
|
|
61
|
+
"style-src 'self' 'unsafe-inline'",
|
|
62
|
+
"img-src 'self' data: https:",
|
|
63
|
+
"font-src 'self'",
|
|
64
|
+
"connect-src 'self'",
|
|
65
|
+
"frame-ancestors 'none'",
|
|
66
|
+
"base-uri 'self'",
|
|
67
|
+
"form-action 'self'"
|
|
68
|
+
].join('; ')
|
|
69
|
+
);
|
|
70
|
+
next();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// Good - CSP with nonce for inline scripts
|
|
74
|
+
const nonce = crypto.randomBytes(16).toString('base64');
|
|
75
|
+
res.setHeader('Content-Security-Policy', `script-src 'nonce-${nonce}'`);
|
|
76
|
+
res.render('page', { nonce });
|
|
77
|
+
|
|
78
|
+
// In template:
|
|
79
|
+
// <script nonce="{{nonce}}">...</script>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Cross-Site Request Forgery (CSRF)
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import csrf from 'csurf';
|
|
86
|
+
|
|
87
|
+
// Good - CSRF protection
|
|
88
|
+
const csrfProtection = csrf({ cookie: true });
|
|
89
|
+
|
|
90
|
+
app.get('/form', csrfProtection, (req, res) => {
|
|
91
|
+
res.render('form', { csrfToken: req.csrfToken() });
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
app.post('/form', csrfProtection, (req, res) => {
|
|
95
|
+
// CSRF token validated automatically
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// In HTML form:
|
|
99
|
+
// <input type="hidden" name="_csrf" value="{{csrfToken}}">
|
|
100
|
+
|
|
101
|
+
// Good - SameSite cookies (additional protection)
|
|
102
|
+
app.use(session({
|
|
103
|
+
cookie: {
|
|
104
|
+
sameSite: 'strict', // or 'lax'
|
|
105
|
+
secure: true,
|
|
106
|
+
httpOnly: true
|
|
107
|
+
}
|
|
108
|
+
}));
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## Clickjacking
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
// Good - X-Frame-Options header
|
|
115
|
+
app.use((req, res, next) => {
|
|
116
|
+
res.setHeader('X-Frame-Options', 'DENY');
|
|
117
|
+
next();
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Good - CSP frame-ancestors
|
|
121
|
+
app.use((req, res, next) => {
|
|
122
|
+
res.setHeader('Content-Security-Policy', "frame-ancestors 'none'");
|
|
123
|
+
next();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Allow specific domains
|
|
127
|
+
res.setHeader('Content-Security-Policy', "frame-ancestors 'self' https://trusted.com");
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## CORS (Cross-Origin Resource Sharing)
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
import cors from 'cors';
|
|
134
|
+
|
|
135
|
+
// Bad - Allow all origins
|
|
136
|
+
app.use(cors({ origin: '*' })); // ❌ Too permissive
|
|
137
|
+
|
|
138
|
+
// Good - Whitelist specific origins
|
|
139
|
+
const allowedOrigins = ['https://example.com', 'https://app.example.com'];
|
|
140
|
+
|
|
141
|
+
app.use(cors({
|
|
142
|
+
origin: (origin, callback) => {
|
|
143
|
+
if (!origin || allowedOrigins.includes(origin)) {
|
|
144
|
+
callback(null, true);
|
|
145
|
+
} else {
|
|
146
|
+
callback(new Error('Not allowed by CORS'));
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
credentials: true, // Allow cookies
|
|
150
|
+
methods: ['GET', 'POST', 'PUT', 'DELETE'],
|
|
151
|
+
allowedHeaders: ['Content-Type', 'Authorization']
|
|
152
|
+
}));
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Security Headers
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import helmet from 'helmet';
|
|
159
|
+
|
|
160
|
+
// Good - Use helmet for security headers
|
|
161
|
+
app.use(helmet());
|
|
162
|
+
|
|
163
|
+
// Manual configuration
|
|
164
|
+
app.use((req, res, next) => {
|
|
165
|
+
// Prevent MIME sniffing
|
|
166
|
+
res.setHeader('X-Content-Type-Options', 'nosniff');
|
|
167
|
+
|
|
168
|
+
// XSS protection (legacy browsers)
|
|
169
|
+
res.setHeader('X-XSS-Protection', '1; mode=block');
|
|
170
|
+
|
|
171
|
+
// Clickjacking protection
|
|
172
|
+
res.setHeader('X-Frame-Options', 'DENY');
|
|
173
|
+
|
|
174
|
+
// HTTPS enforcement
|
|
175
|
+
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
|
|
176
|
+
|
|
177
|
+
// Referrer policy
|
|
178
|
+
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
|
|
179
|
+
|
|
180
|
+
// Permissions policy
|
|
181
|
+
res.setHeader('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');
|
|
182
|
+
|
|
183
|
+
next();
|
|
184
|
+
});
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Cookie Security
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
// Good - Secure cookie configuration
|
|
191
|
+
res.cookie('sessionId', sessionId, {
|
|
192
|
+
httpOnly: true, // Prevent JavaScript access
|
|
193
|
+
secure: true, // HTTPS only
|
|
194
|
+
sameSite: 'strict', // CSRF protection
|
|
195
|
+
maxAge: 3600000, // 1 hour
|
|
196
|
+
domain: '.example.com',
|
|
197
|
+
path: '/',
|
|
198
|
+
signed: true // Sign cookie
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Bad - Insecure cookie
|
|
202
|
+
res.cookie('sessionId', sessionId); // ❌ No security flags
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Session Fixation Prevention
|
|
206
|
+
|
|
207
|
+
```typescript
|
|
208
|
+
// Good - Regenerate session on login
|
|
209
|
+
app.post('/login', async (req, res) => {
|
|
210
|
+
const user = await authenticate(req.body.email, req.body.password);
|
|
211
|
+
|
|
212
|
+
if (user) {
|
|
213
|
+
// Regenerate session ID
|
|
214
|
+
req.session.regenerate((err) => {
|
|
215
|
+
if (err) {
|
|
216
|
+
return res.status(500).json({ error: 'Login failed' });
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
req.session.userId = user.id;
|
|
220
|
+
res.json({ success: true });
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Open Redirect Prevention
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
// Bad - Open redirect vulnerability
|
|
230
|
+
app.get('/redirect', (req, res) => {
|
|
231
|
+
res.redirect(req.query.url); // ❌ Redirects to any URL
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// Good - Validate redirect URL
|
|
235
|
+
const allowedRedirects = ['/dashboard', '/profile', '/settings'];
|
|
236
|
+
|
|
237
|
+
app.get('/redirect', (req, res) => {
|
|
238
|
+
const url = req.query.url;
|
|
239
|
+
|
|
240
|
+
if (!allowedRedirects.includes(url)) {
|
|
241
|
+
return res.status(400).json({ error: 'Invalid redirect' });
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
res.redirect(url);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
// Good - Validate external redirects
|
|
248
|
+
const allowedDomains = ['example.com', 'app.example.com'];
|
|
249
|
+
|
|
250
|
+
const url = new URL(req.query.url);
|
|
251
|
+
if (!allowedDomains.includes(url.hostname)) {
|
|
252
|
+
return res.status(400).json({ error: 'Invalid redirect domain' });
|
|
253
|
+
}
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Best Practices
|
|
257
|
+
|
|
258
|
+
1. **Prevent XSS** - Sanitize HTML, use CSP
|
|
259
|
+
2. **CSRF protection** - Use CSRF tokens, SameSite cookies
|
|
260
|
+
3. **Prevent clickjacking** - X-Frame-Options, CSP frame-ancestors
|
|
261
|
+
4. **Configure CORS** - Whitelist specific origins
|
|
262
|
+
5. **Security headers** - Use helmet middleware
|
|
263
|
+
6. **Secure cookies** - httpOnly, secure, sameSite
|
|
264
|
+
7. **Regenerate sessions** - On login/privilege change
|
|
265
|
+
8. **Validate redirects** - Whitelist allowed URLs
|
|
266
|
+
9. **Content-Type** - Set correct Content-Type headers
|
|
267
|
+
10. **HTTPS only** - Enforce HTTPS with HSTS
|
|
268
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Design Patterns Examples
|
|
2
|
+
|
|
3
|
+
Common design patterns with TypeScript/JavaScript implementations and real-world use cases.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This module provides comprehensive examples of classic design patterns, including creational, structural, and behavioral patterns. Each pattern includes TypeScript implementations, use cases, and best practices.
|
|
8
|
+
|
|
9
|
+
## Key Benefits
|
|
10
|
+
|
|
11
|
+
- **Practical Examples**: Real-world implementations
|
|
12
|
+
- **TypeScript Support**: Fully typed examples
|
|
13
|
+
- **Use Cases**: When to use each pattern
|
|
14
|
+
- **Best Practices**: Common pitfalls and solutions
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
augx link examples/design-patterns
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Contents
|
|
23
|
+
|
|
24
|
+
### Examples
|
|
25
|
+
|
|
26
|
+
- **creational-patterns.md** - Singleton, Factory, Builder, Prototype
|
|
27
|
+
- **structural-patterns.md** - Adapter, Decorator, Facade, Proxy
|
|
28
|
+
- **behavioral-patterns.md** - Observer, Strategy, Command, State
|
|
29
|
+
|
|
30
|
+
## Character Count
|
|
31
|
+
|
|
32
|
+
~42,000 characters
|
|
33
|
+
|
|
34
|
+
## Version
|
|
35
|
+
|
|
36
|
+
1.0.0
|
|
37
|
+
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
# Behavioral Design Patterns
|
|
2
|
+
|
|
3
|
+
Patterns for communication between objects.
|
|
4
|
+
|
|
5
|
+
## Observer Pattern
|
|
6
|
+
|
|
7
|
+
Define one-to-many dependency between objects.
|
|
8
|
+
|
|
9
|
+
### Implementation
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
interface Observer {
|
|
13
|
+
update(data: any): void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface Subject {
|
|
17
|
+
attach(observer: Observer): void;
|
|
18
|
+
detach(observer: Observer): void;
|
|
19
|
+
notify(): void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
class NewsAgency implements Subject {
|
|
23
|
+
private observers: Observer[] = [];
|
|
24
|
+
private news: string = '';
|
|
25
|
+
|
|
26
|
+
attach(observer: Observer): void {
|
|
27
|
+
this.observers.push(observer);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
detach(observer: Observer): void {
|
|
31
|
+
const index = this.observers.indexOf(observer);
|
|
32
|
+
if (index > -1) {
|
|
33
|
+
this.observers.splice(index, 1);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
notify(): void {
|
|
38
|
+
for (const observer of this.observers) {
|
|
39
|
+
observer.update(this.news);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
setNews(news: string): void {
|
|
44
|
+
this.news = news;
|
|
45
|
+
this.notify();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
class NewsChannel implements Observer {
|
|
50
|
+
constructor(private name: string) {}
|
|
51
|
+
|
|
52
|
+
update(news: string): void {
|
|
53
|
+
console.log(`${this.name} received news: ${news}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Usage
|
|
58
|
+
const agency = new NewsAgency();
|
|
59
|
+
const cnn = new NewsChannel('CNN');
|
|
60
|
+
const bbc = new NewsChannel('BBC');
|
|
61
|
+
|
|
62
|
+
agency.attach(cnn);
|
|
63
|
+
agency.attach(bbc);
|
|
64
|
+
|
|
65
|
+
agency.setNews('Breaking: New design pattern discovered!');
|
|
66
|
+
// CNN received news: Breaking: New design pattern discovered!
|
|
67
|
+
// BBC received news: Breaking: New design pattern discovered!
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Use Cases
|
|
71
|
+
|
|
72
|
+
- Event handling systems
|
|
73
|
+
- Model-View-Controller (MVC)
|
|
74
|
+
- Pub/Sub messaging
|
|
75
|
+
- Real-time notifications
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Strategy Pattern
|
|
80
|
+
|
|
81
|
+
Define family of algorithms, encapsulate each one, make them interchangeable.
|
|
82
|
+
|
|
83
|
+
### Implementation
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
interface PaymentStrategy {
|
|
87
|
+
pay(amount: number): void;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
class CreditCardPayment implements PaymentStrategy {
|
|
91
|
+
constructor(
|
|
92
|
+
private cardNumber: string,
|
|
93
|
+
private cvv: string
|
|
94
|
+
) {}
|
|
95
|
+
|
|
96
|
+
pay(amount: number): void {
|
|
97
|
+
console.log(`Paid $${amount} using Credit Card ${this.cardNumber}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
class PayPalPayment implements PaymentStrategy {
|
|
102
|
+
constructor(private email: string) {}
|
|
103
|
+
|
|
104
|
+
pay(amount: number): void {
|
|
105
|
+
console.log(`Paid $${amount} using PayPal account ${this.email}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
class CryptoPayment implements PaymentStrategy {
|
|
110
|
+
constructor(private walletAddress: string) {}
|
|
111
|
+
|
|
112
|
+
pay(amount: number): void {
|
|
113
|
+
console.log(`Paid $${amount} using Crypto wallet ${this.walletAddress}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
class ShoppingCart {
|
|
118
|
+
private items: string[] = [];
|
|
119
|
+
private paymentStrategy?: PaymentStrategy;
|
|
120
|
+
|
|
121
|
+
addItem(item: string): void {
|
|
122
|
+
this.items.push(item);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
setPaymentStrategy(strategy: PaymentStrategy): void {
|
|
126
|
+
this.paymentStrategy = strategy;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
checkout(amount: number): void {
|
|
130
|
+
if (!this.paymentStrategy) {
|
|
131
|
+
throw new Error('Payment strategy not set');
|
|
132
|
+
}
|
|
133
|
+
this.paymentStrategy.pay(amount);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Usage
|
|
138
|
+
const cart = new ShoppingCart();
|
|
139
|
+
cart.addItem('Laptop');
|
|
140
|
+
cart.addItem('Mouse');
|
|
141
|
+
|
|
142
|
+
// Pay with credit card
|
|
143
|
+
cart.setPaymentStrategy(new CreditCardPayment('1234-5678', '123'));
|
|
144
|
+
cart.checkout(1500);
|
|
145
|
+
|
|
146
|
+
// Pay with PayPal
|
|
147
|
+
cart.setPaymentStrategy(new PayPalPayment('user@example.com'));
|
|
148
|
+
cart.checkout(1500);
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Use Cases
|
|
152
|
+
|
|
153
|
+
- Payment processing
|
|
154
|
+
- Sorting algorithms
|
|
155
|
+
- Compression algorithms
|
|
156
|
+
- Validation strategies
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Command Pattern
|
|
161
|
+
|
|
162
|
+
Encapsulate request as an object.
|
|
163
|
+
|
|
164
|
+
### Implementation
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
interface Command {
|
|
168
|
+
execute(): void;
|
|
169
|
+
undo(): void;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
class Light {
|
|
173
|
+
turnOn(): void {
|
|
174
|
+
console.log('Light is ON');
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
turnOff(): void {
|
|
178
|
+
console.log('Light is OFF');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
class LightOnCommand implements Command {
|
|
183
|
+
constructor(private light: Light) {}
|
|
184
|
+
|
|
185
|
+
execute(): void {
|
|
186
|
+
this.light.turnOn();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
undo(): void {
|
|
190
|
+
this.light.turnOff();
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
class LightOffCommand implements Command {
|
|
195
|
+
constructor(private light: Light) {}
|
|
196
|
+
|
|
197
|
+
execute(): void {
|
|
198
|
+
this.light.turnOff();
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
undo(): void {
|
|
202
|
+
this.light.turnOn();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
class RemoteControl {
|
|
207
|
+
private history: Command[] = [];
|
|
208
|
+
|
|
209
|
+
executeCommand(command: Command): void {
|
|
210
|
+
command.execute();
|
|
211
|
+
this.history.push(command);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
undo(): void {
|
|
215
|
+
const command = this.history.pop();
|
|
216
|
+
if (command) {
|
|
217
|
+
command.undo();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Usage
|
|
223
|
+
const light = new Light();
|
|
224
|
+
const remote = new RemoteControl();
|
|
225
|
+
|
|
226
|
+
const lightOn = new LightOnCommand(light);
|
|
227
|
+
const lightOff = new LightOffCommand(light);
|
|
228
|
+
|
|
229
|
+
remote.executeCommand(lightOn); // Light is ON
|
|
230
|
+
remote.executeCommand(lightOff); // Light is OFF
|
|
231
|
+
remote.undo(); // Light is ON (undo last command)
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Use Cases
|
|
235
|
+
|
|
236
|
+
- Undo/Redo functionality
|
|
237
|
+
- Transaction systems
|
|
238
|
+
- Job queues
|
|
239
|
+
- Macro recording
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## State Pattern
|
|
244
|
+
|
|
245
|
+
Allow object to alter behavior when internal state changes.
|
|
246
|
+
|
|
247
|
+
### Implementation
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
interface State {
|
|
251
|
+
insertCoin(): void;
|
|
252
|
+
ejectCoin(): void;
|
|
253
|
+
dispense(): void;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
class VendingMachine {
|
|
257
|
+
private noCoinState: State;
|
|
258
|
+
private hasCoinState: State;
|
|
259
|
+
private soldState: State;
|
|
260
|
+
private currentState: State;
|
|
261
|
+
|
|
262
|
+
constructor() {
|
|
263
|
+
this.noCoinState = new NoCoinState(this);
|
|
264
|
+
this.hasCoinState = new HasCoinState(this);
|
|
265
|
+
this.soldState = new SoldState(this);
|
|
266
|
+
this.currentState = this.noCoinState;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
setState(state: State): void {
|
|
270
|
+
this.currentState = state;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
getNoCoinState(): State {
|
|
274
|
+
return this.noCoinState;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
getHasCoinState(): State {
|
|
278
|
+
return this.hasCoinState;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
getSoldState(): State {
|
|
282
|
+
return this.soldState;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
insertCoin(): void {
|
|
286
|
+
this.currentState.insertCoin();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
ejectCoin(): void {
|
|
290
|
+
this.currentState.ejectCoin();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
dispense(): void {
|
|
294
|
+
this.currentState.dispense();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
class NoCoinState implements State {
|
|
299
|
+
constructor(private machine: VendingMachine) {}
|
|
300
|
+
|
|
301
|
+
insertCoin(): void {
|
|
302
|
+
console.log('Coin inserted');
|
|
303
|
+
this.machine.setState(this.machine.getHasCoinState());
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
ejectCoin(): void {
|
|
307
|
+
console.log('No coin to eject');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
dispense(): void {
|
|
311
|
+
console.log('Insert coin first');
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
class HasCoinState implements State {
|
|
316
|
+
constructor(private machine: VendingMachine) {}
|
|
317
|
+
|
|
318
|
+
insertCoin(): void {
|
|
319
|
+
console.log('Coin already inserted');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
ejectCoin(): void {
|
|
323
|
+
console.log('Coin ejected');
|
|
324
|
+
this.machine.setState(this.machine.getNoCoinState());
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
dispense(): void {
|
|
328
|
+
console.log('Dispensing product');
|
|
329
|
+
this.machine.setState(this.machine.getSoldState());
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
class SoldState implements State {
|
|
334
|
+
constructor(private machine: VendingMachine) {}
|
|
335
|
+
|
|
336
|
+
insertCoin(): void {
|
|
337
|
+
console.log('Please wait, dispensing product');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
ejectCoin(): void {
|
|
341
|
+
console.log('Cannot eject, already dispensing');
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
dispense(): void {
|
|
345
|
+
console.log('Product dispensed');
|
|
346
|
+
this.machine.setState(this.machine.getNoCoinState());
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Usage
|
|
351
|
+
const machine = new VendingMachine();
|
|
352
|
+
machine.insertCoin(); // Coin inserted
|
|
353
|
+
machine.dispense(); // Dispensing product
|
|
354
|
+
machine.dispense(); // Product dispensed
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Use Cases
|
|
358
|
+
|
|
359
|
+
- Workflow engines
|
|
360
|
+
- Game character states
|
|
361
|
+
- Connection states (connected, disconnected, etc.)
|
|
362
|
+
- Order processing states
|
|
363
|
+
|
|
364
|
+
### Best Practices
|
|
365
|
+
|
|
366
|
+
1. **Observer**: Use for event-driven systems
|
|
367
|
+
2. **Strategy**: Use when you need to switch algorithms at runtime
|
|
368
|
+
3. **Command**: Use for undo/redo and transaction systems
|
|
369
|
+
4. **State**: Use when object behavior changes based on state
|
|
370
|
+
|