@engjts/nexus 0.1.8 → 0.1.9

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.
Files changed (205) hide show
  1. package/package.json +1 -1
  2. package/BENCHMARK_REPORT.md +0 -343
  3. package/documentation/01-getting-started.md +0 -240
  4. package/documentation/02-context.md +0 -335
  5. package/documentation/03-routing.md +0 -397
  6. package/documentation/04-middleware.md +0 -483
  7. package/documentation/05-validation.md +0 -514
  8. package/documentation/06-error-handling.md +0 -465
  9. package/documentation/07-performance.md +0 -364
  10. package/documentation/08-adapters.md +0 -470
  11. package/documentation/09-api-reference.md +0 -548
  12. package/documentation/10-examples.md +0 -582
  13. package/documentation/11-deployment.md +0 -477
  14. package/documentation/12-sentry.md +0 -620
  15. package/documentation/13-sentry-data-storage.md +0 -996
  16. package/documentation/14-sentry-data-reference.md +0 -457
  17. package/documentation/15-sentry-summary.md +0 -409
  18. package/documentation/16-alerts-system.md +0 -745
  19. package/documentation/17-alert-adapters.md +0 -696
  20. package/documentation/18-alerts-implementation-summary.md +0 -385
  21. package/documentation/19-class-based-routing.md +0 -840
  22. package/documentation/20-websocket-realtime.md +0 -813
  23. package/documentation/21-cache-system.md +0 -510
  24. package/documentation/22-job-queue.md +0 -772
  25. package/documentation/23-sentry-plugin.md +0 -551
  26. package/documentation/24-testing-utilities.md +0 -1287
  27. package/documentation/25-api-versioning.md +0 -533
  28. package/documentation/26-context-store.md +0 -607
  29. package/documentation/27-dependency-injection.md +0 -329
  30. package/documentation/28-lifecycle-hooks.md +0 -521
  31. package/documentation/29-package-structure.md +0 -196
  32. package/documentation/30-plugin-system.md +0 -414
  33. package/documentation/31-jwt-authentication.md +0 -597
  34. package/documentation/32-cli.md +0 -268
  35. package/documentation/ALERTS-COMPLETE-SUMMARY.md +0 -429
  36. package/documentation/ALERTS-INDEX.md +0 -330
  37. package/documentation/ALERTS-QUICK-REFERENCE.md +0 -286
  38. package/documentation/README.md +0 -178
  39. package/documentation/index.html +0 -34
  40. package/modern_framework_paper.md +0 -1870
  41. package/public/css/style.css +0 -87
  42. package/public/index.html +0 -34
  43. package/public/js/app.js +0 -27
  44. package/src/advanced/cache/InMemoryCacheStore.ts +0 -68
  45. package/src/advanced/cache/MultiTierCache.ts +0 -194
  46. package/src/advanced/cache/RedisCacheStore.ts +0 -341
  47. package/src/advanced/cache/index.ts +0 -5
  48. package/src/advanced/cache/types.ts +0 -40
  49. package/src/advanced/graphql/SimpleDataLoader.ts +0 -42
  50. package/src/advanced/graphql/index.ts +0 -22
  51. package/src/advanced/graphql/server.ts +0 -252
  52. package/src/advanced/graphql/types.ts +0 -42
  53. package/src/advanced/jobs/InMemoryQueueStore.ts +0 -68
  54. package/src/advanced/jobs/JobQueue.ts +0 -556
  55. package/src/advanced/jobs/RedisQueueStore.ts +0 -367
  56. package/src/advanced/jobs/index.ts +0 -5
  57. package/src/advanced/jobs/types.ts +0 -70
  58. package/src/advanced/observability/APMManager.ts +0 -163
  59. package/src/advanced/observability/AlertManager.ts +0 -109
  60. package/src/advanced/observability/MetricRegistry.ts +0 -151
  61. package/src/advanced/observability/ObservabilityCenter.ts +0 -304
  62. package/src/advanced/observability/StructuredLogger.ts +0 -154
  63. package/src/advanced/observability/TracingManager.ts +0 -117
  64. package/src/advanced/observability/adapters.ts +0 -304
  65. package/src/advanced/observability/createObservabilityMiddleware.ts +0 -63
  66. package/src/advanced/observability/index.ts +0 -11
  67. package/src/advanced/observability/types.ts +0 -174
  68. package/src/advanced/playground/extractPathParams.ts +0 -6
  69. package/src/advanced/playground/generateFieldExample.ts +0 -31
  70. package/src/advanced/playground/generatePlaygroundHTML.ts +0 -1956
  71. package/src/advanced/playground/generateSummary.ts +0 -19
  72. package/src/advanced/playground/getTagFromPath.ts +0 -9
  73. package/src/advanced/playground/index.ts +0 -8
  74. package/src/advanced/playground/playground.ts +0 -250
  75. package/src/advanced/playground/types.ts +0 -49
  76. package/src/advanced/playground/zodToExample.ts +0 -16
  77. package/src/advanced/playground/zodToParams.ts +0 -15
  78. package/src/advanced/postman/buildAuth.ts +0 -31
  79. package/src/advanced/postman/buildBody.ts +0 -15
  80. package/src/advanced/postman/buildQueryParams.ts +0 -27
  81. package/src/advanced/postman/buildRequestItem.ts +0 -36
  82. package/src/advanced/postman/buildResponses.ts +0 -11
  83. package/src/advanced/postman/buildUrl.ts +0 -33
  84. package/src/advanced/postman/capitalize.ts +0 -4
  85. package/src/advanced/postman/generateCollection.ts +0 -59
  86. package/src/advanced/postman/generateEnvironment.ts +0 -34
  87. package/src/advanced/postman/generateExampleFromZod.ts +0 -21
  88. package/src/advanced/postman/generateFieldExample.ts +0 -45
  89. package/src/advanced/postman/generateName.ts +0 -20
  90. package/src/advanced/postman/generateUUID.ts +0 -11
  91. package/src/advanced/postman/getTagFromPath.ts +0 -10
  92. package/src/advanced/postman/index.ts +0 -28
  93. package/src/advanced/postman/postman.ts +0 -156
  94. package/src/advanced/postman/slugify.ts +0 -7
  95. package/src/advanced/postman/types.ts +0 -140
  96. package/src/advanced/realtime/index.ts +0 -18
  97. package/src/advanced/realtime/websocket.ts +0 -231
  98. package/src/advanced/sentry/index.ts +0 -1236
  99. package/src/advanced/sentry/types.ts +0 -355
  100. package/src/advanced/static/generateDirectoryListing.ts +0 -47
  101. package/src/advanced/static/generateETag.ts +0 -7
  102. package/src/advanced/static/getMimeType.ts +0 -9
  103. package/src/advanced/static/index.ts +0 -32
  104. package/src/advanced/static/isSafePath.ts +0 -13
  105. package/src/advanced/static/publicDir.ts +0 -21
  106. package/src/advanced/static/serveStatic.ts +0 -225
  107. package/src/advanced/static/spa.ts +0 -24
  108. package/src/advanced/static/types.ts +0 -159
  109. package/src/advanced/swagger/SwaggerGenerator.ts +0 -66
  110. package/src/advanced/swagger/buildOperation.ts +0 -61
  111. package/src/advanced/swagger/buildParameters.ts +0 -61
  112. package/src/advanced/swagger/buildRequestBody.ts +0 -21
  113. package/src/advanced/swagger/buildResponses.ts +0 -54
  114. package/src/advanced/swagger/capitalize.ts +0 -5
  115. package/src/advanced/swagger/convertPath.ts +0 -9
  116. package/src/advanced/swagger/createSwagger.ts +0 -12
  117. package/src/advanced/swagger/generateOperationId.ts +0 -21
  118. package/src/advanced/swagger/generateSpec.ts +0 -105
  119. package/src/advanced/swagger/generateSummary.ts +0 -24
  120. package/src/advanced/swagger/generateSwaggerUI.ts +0 -70
  121. package/src/advanced/swagger/generateThemeCss.ts +0 -53
  122. package/src/advanced/swagger/index.ts +0 -25
  123. package/src/advanced/swagger/swagger.ts +0 -237
  124. package/src/advanced/swagger/types.ts +0 -206
  125. package/src/advanced/swagger/zodFieldToOpenAPI.ts +0 -94
  126. package/src/advanced/swagger/zodSchemaToOpenAPI.ts +0 -50
  127. package/src/advanced/swagger/zodToOpenAPI.ts +0 -22
  128. package/src/advanced/testing/factory.ts +0 -509
  129. package/src/advanced/testing/harness.ts +0 -612
  130. package/src/advanced/testing/index.ts +0 -430
  131. package/src/advanced/testing/load-test.ts +0 -618
  132. package/src/advanced/testing/mock-server.ts +0 -498
  133. package/src/advanced/testing/mock.ts +0 -670
  134. package/src/cli/bin.ts +0 -9
  135. package/src/cli/cli.ts +0 -158
  136. package/src/cli/commands/add.ts +0 -178
  137. package/src/cli/commands/build.ts +0 -73
  138. package/src/cli/commands/create.ts +0 -166
  139. package/src/cli/commands/dev.ts +0 -85
  140. package/src/cli/commands/generate.ts +0 -99
  141. package/src/cli/commands/help.ts +0 -95
  142. package/src/cli/commands/init.ts +0 -91
  143. package/src/cli/commands/version.ts +0 -38
  144. package/src/cli/index.ts +0 -6
  145. package/src/cli/templates/generators.ts +0 -359
  146. package/src/cli/templates/index.ts +0 -680
  147. package/src/cli/utils/exec.ts +0 -52
  148. package/src/cli/utils/file-system.ts +0 -78
  149. package/src/cli/utils/logger.ts +0 -111
  150. package/src/core/adapter.ts +0 -88
  151. package/src/core/application.ts +0 -1453
  152. package/src/core/context-pool.ts +0 -79
  153. package/src/core/context.ts +0 -856
  154. package/src/core/index.ts +0 -94
  155. package/src/core/middleware.ts +0 -272
  156. package/src/core/performance/buffer-pool.ts +0 -108
  157. package/src/core/performance/middleware-optimizer.ts +0 -162
  158. package/src/core/plugin/PluginManager.ts +0 -435
  159. package/src/core/plugin/builder.ts +0 -358
  160. package/src/core/plugin/index.ts +0 -50
  161. package/src/core/plugin/types.ts +0 -214
  162. package/src/core/router/file-router.ts +0 -623
  163. package/src/core/router/index.ts +0 -260
  164. package/src/core/router/radix-tree.ts +0 -242
  165. package/src/core/serializer.ts +0 -397
  166. package/src/core/store/index.ts +0 -30
  167. package/src/core/store/registry.ts +0 -178
  168. package/src/core/store/request-store.ts +0 -240
  169. package/src/core/store/types.ts +0 -233
  170. package/src/core/types.ts +0 -616
  171. package/src/database/adapter.ts +0 -35
  172. package/src/database/adapters/index.ts +0 -1
  173. package/src/database/adapters/mysql.ts +0 -669
  174. package/src/database/database.ts +0 -70
  175. package/src/database/dialect.ts +0 -388
  176. package/src/database/index.ts +0 -12
  177. package/src/database/migrations.ts +0 -86
  178. package/src/database/optimizer.ts +0 -125
  179. package/src/database/query-builder.ts +0 -404
  180. package/src/database/realtime.ts +0 -53
  181. package/src/database/schema.ts +0 -71
  182. package/src/database/transactions.ts +0 -56
  183. package/src/database/types.ts +0 -87
  184. package/src/deployment/cluster.ts +0 -471
  185. package/src/deployment/config.ts +0 -454
  186. package/src/deployment/docker.ts +0 -599
  187. package/src/deployment/graceful-shutdown.ts +0 -373
  188. package/src/deployment/index.ts +0 -56
  189. package/src/index.ts +0 -281
  190. package/src/security/adapter.ts +0 -318
  191. package/src/security/auth/JWTPlugin.ts +0 -234
  192. package/src/security/auth/JWTProvider.ts +0 -316
  193. package/src/security/auth/adapter.ts +0 -12
  194. package/src/security/auth/jwt.ts +0 -234
  195. package/src/security/auth/middleware.ts +0 -188
  196. package/src/security/csrf.ts +0 -220
  197. package/src/security/headers.ts +0 -108
  198. package/src/security/index.ts +0 -60
  199. package/src/security/rate-limit/adapter.ts +0 -7
  200. package/src/security/rate-limit/memory.ts +0 -108
  201. package/src/security/rate-limit/middleware.ts +0 -181
  202. package/src/security/sanitization.ts +0 -75
  203. package/src/security/types.ts +0 -240
  204. package/src/security/utils.ts +0 -52
  205. package/tsconfig.json +0 -39
@@ -1,385 +0,0 @@
1
- # Alert System Implementation Summary
2
-
3
- ## Status: ✅ COMPLETED
4
-
5
- Sistem Alert telah di-refactor menggunakan **Adapter Pattern** untuk mendukung multiple notification channels dengan mudah.
6
-
7
- ---
8
-
9
- ## What's Implemented
10
-
11
- ### 1. Alert Channel Adapter Pattern ✅
12
-
13
- File: `src/advanced/observability/adapters.ts`
14
-
15
- **Features:**
16
- - `AlertChannelAdapter` interface untuk standardisasi
17
- - `AlertChannelAdapterRegistry` untuk manage adapters
18
- - Extensible architecture untuk custom adapters
19
-
20
- ---
21
-
22
- ### 2. Built-in Adapters ✅
23
-
24
- Semua adapter sudah implemented dan siap pakai:
25
-
26
- | Adapter | Status | Config | Notes |
27
- |---------|--------|--------|-------|
28
- | **Slack** | ✅ | webhookUrl | Production-ready |
29
- | **Discord** | ✅ | webhookUrl | Production-ready |
30
- | **Telegram** | ✅ | botToken, chatId | Production-ready |
31
- | **Webhook** | ✅ | url | Generic HTTP endpoint |
32
- | **Email** | ✅ | recipients | Console logging (extend untuk real email) |
33
- | **PagerDuty** | ✅ | routingKey | Console logging (extend untuk API) |
34
- | **Console** | ✅ | - | Testing & debugging |
35
-
36
- ---
37
-
38
- ### 3. Refactored AlertManager ✅
39
-
40
- File: `src/advanced/observability/index.ts`
41
-
42
- **Perubahan:**
43
- - ❌ Removed hardcoded switch-case untuk channels
44
- - ✅ Uses adapter registry pattern
45
- - ✅ Supports dynamic adapter registration
46
- - ✅ Better error handling & validation
47
- - ✅ Config validation per adapter
48
-
49
- ---
50
-
51
- ### 4. Enhanced AlertingOptions ✅
52
-
53
- Updated interface untuk support semua channels:
54
-
55
- ```typescript
56
- channels?: {
57
- slack?: { webhookUrl: string };
58
- telegram?: { botToken: string; chatId: string }; // NEW
59
- discord?: { webhookUrl: string }; // NEW
60
- email?: { recipients: string[] };
61
- pagerduty?: { routingKey: string };
62
- webhook?: { url: string };
63
- console?: {};
64
- };
65
- ```
66
-
67
- ---
68
-
69
- ### 5. Comprehensive Documentation ✅
70
-
71
- **File 1: `16-alerts-system.md`**
72
- - Alert System overview
73
- - Quick start guide
74
- - Configuration & API reference
75
- - Common patterns & best practices
76
- - Troubleshooting
77
-
78
- **File 2: `17-alert-adapters.md`**
79
- - Adapter pattern explanation
80
- - All built-in adapters documented
81
- - Custom adapter creation guide
82
- - Real-world examples
83
- - Multi-severity alert patterns
84
-
85
- ---
86
-
87
- ## How to Use
88
-
89
- ### Quick Start - Telegram Alerts
90
-
91
- ```typescript
92
- import { createApp } from './nexus';
93
- import { ObservabilityCenter } from './nexus/advanced/observability';
94
-
95
- const observability = new ObservabilityCenter({
96
- alerting: {
97
- enabled: true,
98
- channels: {
99
- telegram: {
100
- botToken: process.env.TELEGRAM_BOT_TOKEN!,
101
- chatId: process.env.TELEGRAM_CHAT_ID!
102
- }
103
- },
104
- alerts: [
105
- {
106
- name: 'High Error Rate',
107
- condition: 'error_rate > 0.05',
108
- window: '5m',
109
- threshold: 0.05,
110
- channels: ['telegram']
111
- },
112
- {
113
- name: 'Server Down',
114
- condition: 'uptime < 1',
115
- window: '1m',
116
- threshold: 1,
117
- channels: ['telegram']
118
- }
119
- ]
120
- }
121
- });
122
-
123
- const app = createApp();
124
- app.use(createObservabilityMiddleware(observability, observability.options));
125
-
126
- export default app;
127
- ```
128
-
129
- ### Multi-Channel Setup
130
-
131
- ```typescript
132
- new ObservabilityCenter({
133
- alerting: {
134
- enabled: true,
135
- channels: {
136
- slack: { webhookUrl: process.env.SLACK_WEBHOOK_URL! },
137
- telegram: {
138
- botToken: process.env.TELEGRAM_BOT_TOKEN!,
139
- chatId: process.env.TELEGRAM_CHAT_ID!
140
- },
141
- discord: { webhookUrl: process.env.DISCORD_WEBHOOK_URL! }
142
- },
143
- alerts: [
144
- {
145
- name: 'Critical Alert',
146
- condition: 'error_rate > 0.5',
147
- window: '1m',
148
- threshold: 0.5,
149
- channels: ['slack', 'telegram', 'discord'] // All 3!
150
- }
151
- ]
152
- }
153
- });
154
- ```
155
-
156
- ### Custom Adapter
157
-
158
- ```typescript
159
- import { AlertChannelAdapter, AlertDefinition } from './nexus/advanced/observability';
160
-
161
- class CustomAdapter implements AlertChannelAdapter {
162
- async send(alert: AlertDefinition, value: any, config: any): Promise<void> {
163
- // Your implementation
164
- console.log(`Custom alert: ${alert.name}`);
165
- }
166
-
167
- validate(config: any): boolean {
168
- return !!config.url;
169
- }
170
- }
171
-
172
- // Register & use
173
- observability.alertManager?.getAdapterRegistry()?.register('custom', new CustomAdapter());
174
- ```
175
-
176
- ---
177
-
178
- ## Environment Variables
179
-
180
- Setup untuk production:
181
-
182
- ```bash
183
- # Slack
184
- SLACK_WEBHOOK_URL=https://hooks.slack.com/services/YOUR/WEBHOOK/URL
185
-
186
- # Telegram
187
- TELEGRAM_BOT_TOKEN=123456789:ABCdefGHIjklmnoPQRstUVwxyz
188
- TELEGRAM_CHAT_ID=987654321
189
-
190
- # Discord
191
- DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/YOUR/WEBHOOK/URL
192
-
193
- # Webhook
194
- WEBHOOK_URL=https://your-service.com/alerts
195
- ```
196
-
197
- ---
198
-
199
- ## Architecture
200
-
201
- ```
202
- AlertManager
203
- ├── AlertChannelAdapterRegistry
204
- │ ├── SlackAlertAdapter
205
- │ ├── TelegramAlertAdapter (NEW ✨)
206
- │ ├── DiscordAlertAdapter (NEW ✨)
207
- │ ├── WebhookAlertAdapter
208
- │ ├── EmailAlertAdapter
209
- │ ├── PagerDutyAlertAdapter
210
- │ └── ConsoleAlertAdapter
211
-
212
- └── Channel Configurations
213
- ├── slack: { webhookUrl }
214
- ├── telegram: { botToken, chatId }
215
- ├── discord: { webhookUrl }
216
- ├── webhook: { url }
217
- ├── email: { recipients }
218
- ├── pagerduty: { routingKey }
219
- └── console: {}
220
- ```
221
-
222
- ---
223
-
224
- ## Testing
225
-
226
- ### Test di Development
227
-
228
- ```typescript
229
- // Use console adapter for testing
230
- const alerting = {
231
- enabled: true,
232
- channels: {
233
- console: {} // Will log to console
234
- },
235
- alerts: [
236
- {
237
- name: 'Test Alert',
238
- condition: 'value > 1',
239
- window: '1m',
240
- threshold: 1,
241
- channels: ['console']
242
- }
243
- ]
244
- };
245
- ```
246
-
247
- ### Test Manual Trigger
248
-
249
- ```typescript
250
- // Trigger alert manually
251
- await observability.alertManager?.checkAndTrigger('Test Alert', 100);
252
-
253
- // Check history
254
- const history = observability.getAlertHistory();
255
- console.log(history);
256
- ```
257
-
258
- ---
259
-
260
- ## Files Modified/Created
261
-
262
- ### New Files
263
- - ✅ `src/advanced/observability/adapters.ts` - Adapter implementations
264
- - ✅ `documentation/17-alert-adapters.md` - Adapter documentation
265
-
266
- ### Modified Files
267
- - ✅ `src/advanced/observability/index.ts`
268
- - Import adapters
269
- - Update AlertingOptions interface
270
- - Refactor AlertManager to use adapters
271
- - Export adapter types
272
-
273
- - ✅ `documentation/16-alerts-system.md` (existing)
274
- - Already covers basic alert system
275
- - Now with adapter section reference
276
-
277
- ---
278
-
279
- ## What's Next (Optional Enhancements)
280
-
281
- 1. **System Metrics Monitoring** 🔔
282
- - Auto-detect memory usage
283
- - Auto-detect CPU usage
284
- - Auto-detect server crashes
285
- - Auto-trigger alerts
286
-
287
- 2. **Email Implementation** 📧
288
- - Extend EmailAlertAdapter dengan SendGrid/Mailgun
289
- - Template emails untuk alerts
290
-
291
- 3. **PagerDuty Implementation** 📱
292
- - Extend PagerDutyAlertAdapter dengan Events API v2
293
- - Incident creation & escalation
294
-
295
- 4. **Alert Dashboard** 📊
296
- - View active alerts
297
- - Alert history
298
- - Configure alerts via UI
299
-
300
- 5. **Conditional Retry Logic** 🔄
301
- - Retry failed sends
302
- - Exponential backoff
303
- - Dead letter queue
304
-
305
- ---
306
-
307
- ## Key Design Decisions
308
-
309
- ### 1. Why Adapter Pattern?
310
-
311
- - **Extensibility** - Easy add new channels
312
- - **Maintainability** - Each adapter is isolated
313
- - **Testability** - Mock adapters easily
314
- - **Flexibility** - Mix & match channels
315
- - **Loose Coupling** - AlertManager doesn't know about specific channels
316
-
317
- ### 2. Why Interface-based?
318
-
319
- - Type-safe implementation
320
- - IDE auto-completion
321
- - Compile-time validation
322
- - Clear contract untuk adapters
323
-
324
- ### 3. Registry Pattern?
325
-
326
- - Dynamic adapter registration
327
- - Runtime channel discovery
328
- - Support custom adapters
329
- - No hardcoding needed
330
-
331
- ---
332
-
333
- ## Comparison: Before vs After
334
-
335
- ### BEFORE (Hardcoded)
336
- ```typescript
337
- // ❌ Hard to extend
338
- switch (channel) {
339
- case 'slack':
340
- await this.sendSlackAlert(...);
341
- break;
342
- case 'webhook':
343
- await this.sendWebhookAlert(...);
344
- break;
345
- // Add new channel = modify AlertManager
346
- }
347
- ```
348
-
349
- ### AFTER (Adapter Pattern)
350
- ```typescript
351
- // ✅ Easy to extend
352
- const adapter = this.adapterRegistry.get(channelName);
353
- await adapter.send(alert, value, config);
354
-
355
- // Add new channel = create new adapter class
356
- class CustomAdapter implements AlertChannelAdapter {
357
- async send(alert, value, config) { /* ... */ }
358
- validate(config) { /* ... */ }
359
- }
360
- registry.register('custom', new CustomAdapter());
361
- ```
362
-
363
- ---
364
-
365
- ## Production Checklist
366
-
367
- - ✅ All adapters implemented
368
- - ✅ Error handling in place
369
- - ✅ Config validation per adapter
370
- - ✅ Cooldown to prevent alert spam (60 seconds)
371
- - ✅ Alert history tracking
372
- - ✅ Multiple channels support
373
- - ✅ Documentation complete
374
- - ⚠️ Email/PagerDuty need real service integration
375
- - ⚠️ Rate limiting per channel (optional enhancement)
376
- - ⚠️ Alert retry logic (optional enhancement)
377
-
378
- ---
379
-
380
- **Status: Ready for production use!** 🚀
381
-
382
- Dokumentasi lengkap ada di:
383
- - `16-alerts-system.md` - Alert system overview
384
- - `17-alert-adapters.md` - Adapter detailed guide
385
-