@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,154 +0,0 @@
1
- import { LogEntry, LoggingOptions } from './types';
2
-
3
- /**
4
- * Structured logger with masking support
5
- */
6
- export class StructuredLogger {
7
- private logs: LogEntry[] = [];
8
- private options: LoggingOptions;
9
- private maxLogs: number = 10000;
10
- private levelPriority = { debug: 0, info: 1, warn: 2, error: 3 };
11
-
12
- constructor(options: LoggingOptions = {}) {
13
- this.options = options;
14
- }
15
-
16
- private shouldLog(level: LogEntry['level']): boolean {
17
- const configuredLevel = this.options.level ?? 'info';
18
- return this.levelPriority[level] >= this.levelPriority[configuredLevel];
19
- }
20
-
21
- private maskSensitiveData(obj: any): any {
22
- if (!this.options.mask || typeof obj !== 'object' || obj === null) {
23
- return obj;
24
- }
25
-
26
- const result = Array.isArray(obj) ? [...obj] : { ...obj };
27
- const replacement = this.options.mask.replacement ?? '***REDACTED***';
28
-
29
- for (const key of Object.keys(result)) {
30
- // Check field names
31
- if (this.options.mask.fields?.some(f => key.toLowerCase().includes(f.toLowerCase()))) {
32
- result[key] = replacement;
33
- continue;
34
- }
35
-
36
- // Check patterns on string values
37
- if (typeof result[key] === 'string' && this.options.mask.patterns) {
38
- for (const pattern of this.options.mask.patterns) {
39
- if (pattern.test(result[key])) {
40
- result[key] = replacement;
41
- break;
42
- }
43
- }
44
- }
45
-
46
- // Recurse for nested objects
47
- if (typeof result[key] === 'object' && result[key] !== null) {
48
- result[key] = this.maskSensitiveData(result[key]);
49
- }
50
- }
51
-
52
- return result;
53
- }
54
-
55
- private formatEntry(entry: LogEntry): string {
56
- const masked = this.maskSensitiveData(entry);
57
- if (this.options.format === 'json') {
58
- return JSON.stringify(masked);
59
- }
60
-
61
- // Pretty format
62
- const timestamp = new Date(masked.timestamp).toISOString();
63
- const level = masked.level.toUpperCase().padEnd(5);
64
- const correlationId = masked.correlationId ? `[${masked.correlationId}]` : '';
65
- let msg = `${timestamp} ${level} ${correlationId} ${masked.message}`;
66
- if (masked.context) {
67
- msg += ` ${JSON.stringify(masked.context)}`;
68
- }
69
- if (masked.error) {
70
- msg += `\n Error: ${masked.error.message}`;
71
- if (masked.error.stack) {
72
- msg += `\n ${masked.error.stack}`;
73
- }
74
- }
75
- return msg;
76
- }
77
-
78
- log(level: LogEntry['level'], message: string, context?: Record<string, any>, correlationId?: string) {
79
- if (!this.shouldLog(level)) return;
80
-
81
- const entry: LogEntry = {
82
- level,
83
- message,
84
- timestamp: Date.now(),
85
- correlationId,
86
- context
87
- };
88
-
89
- if (this.logs.length >= this.maxLogs) {
90
- this.logs.shift();
91
- }
92
- this.logs.push(entry);
93
-
94
- // Output to console
95
- const formatted = this.formatEntry(entry);
96
- switch (level) {
97
- case 'error':
98
- console.error(formatted);
99
- break;
100
- case 'warn':
101
- console.warn(formatted);
102
- break;
103
- case 'debug':
104
- console.debug(formatted);
105
- break;
106
- default:
107
- console.log(formatted);
108
- }
109
- }
110
-
111
- debug(message: string, context?: Record<string, any>, correlationId?: string) {
112
- this.log('debug', message, context, correlationId);
113
- }
114
-
115
- info(message: string, context?: Record<string, any>, correlationId?: string) {
116
- this.log('info', message, context, correlationId);
117
- }
118
-
119
- warn(message: string, context?: Record<string, any>, correlationId?: string) {
120
- this.log('warn', message, context, correlationId);
121
- }
122
-
123
- error(message: string, error?: Error, context?: Record<string, any>, correlationId?: string) {
124
- const entry: LogEntry = {
125
- level: 'error',
126
- message,
127
- timestamp: Date.now(),
128
- correlationId,
129
- context,
130
- error: error ? { message: error.message, stack: error.stack } : undefined
131
- };
132
-
133
- if (this.logs.length >= this.maxLogs) {
134
- this.logs.shift();
135
- }
136
- this.logs.push(entry);
137
-
138
- console.error(this.formatEntry(entry));
139
- }
140
-
141
- getLogs(filter?: { level?: LogEntry['level']; since?: number; limit?: number; }): LogEntry[] {
142
- let result = this.logs;
143
- if (filter?.level) {
144
- result = result.filter(l => l.level === filter.level);
145
- }
146
- if (filter?.since) {
147
- result = result.filter(l => l.timestamp >= filter.since!);
148
- }
149
- if (filter?.limit) {
150
- result = result.slice(-filter.limit);
151
- }
152
- return result;
153
- }
154
- }
@@ -1,117 +0,0 @@
1
- import { randomUUID } from 'crypto';
2
- import { performance } from 'perf_hooks';
3
- import { Span, TracingOptions } from './types';
4
-
5
- /**
6
- * Distributed tracing manager
7
- */
8
- export class TracingManager {
9
- private spans: Span[] = [];
10
- private activeSpans: Map<string, Span> = new Map();
11
- private options: TracingOptions;
12
- private maxSpans: number = 10000;
13
-
14
- constructor(options: TracingOptions) {
15
- this.options = options;
16
- }
17
-
18
- shouldSample(path: string): boolean {
19
- if (this.options.alwaysTrace?.some(p => path.startsWith(p) || new RegExp(p).test(path))) {
20
- return true;
21
- }
22
- return Math.random() < (this.options.sampleRate ?? 1);
23
- }
24
-
25
- startSpan(name: string, parentSpanId?: string, traceId?: string): Span {
26
- const span: Span = {
27
- traceId: traceId || randomUUID(),
28
- spanId: randomUUID(),
29
- parentSpanId,
30
- name,
31
- startTime: performance.now(),
32
- status: 'unset',
33
- attributes: {},
34
- events: []
35
- };
36
- this.activeSpans.set(span.spanId, span);
37
- return span;
38
- }
39
-
40
- endSpan(spanId: string, status: 'ok' | 'error' = 'ok', error?: Error) {
41
- const span = this.activeSpans.get(spanId);
42
- if (!span) return;
43
-
44
- span.endTime = performance.now();
45
- span.duration = span.endTime - span.startTime;
46
- span.status = status;
47
-
48
- if (error) {
49
- span.attributes['error.message'] = error.message;
50
- span.attributes['error.stack'] = error.stack;
51
- }
52
-
53
- this.activeSpans.delete(spanId);
54
-
55
- // Limit stored spans
56
- if (this.spans.length >= this.maxSpans) {
57
- this.spans.shift();
58
- }
59
- this.spans.push(span);
60
-
61
- // Export if configured
62
- this.exportSpan(span);
63
- }
64
-
65
- addEvent(spanId: string, name: string, attributes?: Record<string, any>) {
66
- const span = this.activeSpans.get(spanId);
67
- if (span) {
68
- span.events.push({ name, timestamp: performance.now(), attributes });
69
- }
70
- }
71
-
72
- setAttributes(spanId: string, attributes: Record<string, any>) {
73
- const span = this.activeSpans.get(spanId);
74
- if (span) {
75
- Object.assign(span.attributes, attributes);
76
- }
77
- }
78
-
79
- getSpans(): Span[] {
80
- return [...this.spans];
81
- }
82
-
83
- getActiveSpans(): Span[] {
84
- return Array.from(this.activeSpans.values());
85
- }
86
-
87
- private exportSpan(span: Span) {
88
- if (this.options.exporter === 'console') {
89
- console.log('[TRACE]', JSON.stringify(span, null, 2));
90
- }
91
- // Other exporters would send to external services
92
- }
93
-
94
- /**
95
- * Extract trace context from incoming headers (W3C Trace Context)
96
- */
97
- extractContext(headers: Record<string, string | string[] | undefined>): { traceId?: string; parentSpanId?: string; } {
98
- const traceparent = headers['traceparent'];
99
- if (!traceparent) return {};
100
-
101
- const value = Array.isArray(traceparent) ? traceparent[0] : traceparent;
102
- const parts = value.split('-');
103
- if (parts.length >= 3) {
104
- return { traceId: parts[1], parentSpanId: parts[2] };
105
- }
106
- return {};
107
- }
108
-
109
- /**
110
- * Inject trace context into outgoing headers
111
- */
112
- injectContext(span: Span): Record<string, string> {
113
- return {
114
- traceparent: `00-${span.traceId}-${span.spanId}-01`
115
- };
116
- }
117
- }
@@ -1,304 +0,0 @@
1
- /**
2
- * Alert Channel Adapters
3
- *
4
- * Extensible adapter system for alert notification channels
5
- */
6
-
7
- export interface AlertDefinition {
8
- name: string;
9
- condition: string;
10
- window: string;
11
- channels: string[];
12
- threshold?: number;
13
- }
14
-
15
- /**
16
- * Alert channel adapter interface
17
- * Implement this to add custom notification channels
18
- */
19
- export interface AlertChannelAdapter {
20
- /**
21
- * Send alert notification through the channel
22
- * @param alert - Alert definition
23
- * @param value - Current metric value
24
- * @param config - Channel-specific configuration
25
- */
26
- send(alert: AlertDefinition, value: any, config: any): Promise<void>;
27
-
28
- /**
29
- * Validate channel configuration
30
- * @returns true if config is valid
31
- */
32
- validate(config: any): boolean;
33
- }
34
-
35
- /**
36
- * Slack alert adapter
37
- */
38
- export class SlackAlertAdapter implements AlertChannelAdapter {
39
- async send(alert: AlertDefinition, value: any, config: { webhookUrl: string }): Promise<void> {
40
- if (!config.webhookUrl) {
41
- throw new Error('Slack webhook URL is required');
42
- }
43
-
44
- const response = await fetch(config.webhookUrl, {
45
- method: 'POST',
46
- headers: { 'Content-Type': 'application/json' },
47
- body: JSON.stringify({
48
- text: `🚨 Alert: ${alert.name}`,
49
- blocks: [
50
- {
51
- type: 'section',
52
- text: {
53
- type: 'mrkdwn',
54
- text: `*Alert:* ${alert.name}\n*Condition:* ${alert.condition}\n*Current Value:* ${value}`
55
- }
56
- }
57
- ]
58
- })
59
- });
60
-
61
- if (!response.ok) {
62
- throw new Error(`Slack alert failed: ${response.statusText}`);
63
- }
64
- }
65
-
66
- validate(config: { webhookUrl?: string }): boolean {
67
- return !!(config.webhookUrl && typeof config.webhookUrl === 'string' && config.webhookUrl.startsWith('https://'));
68
- }
69
- }
70
-
71
- /**
72
- * Webhook alert adapter (generic HTTP endpoint)
73
- */
74
- export class WebhookAlertAdapter implements AlertChannelAdapter {
75
- async send(alert: AlertDefinition, value: any, config: { url: string }): Promise<void> {
76
- if (!config.url) {
77
- throw new Error('Webhook URL is required');
78
- }
79
-
80
- const response = await fetch(config.url, {
81
- method: 'POST',
82
- headers: { 'Content-Type': 'application/json' },
83
- body: JSON.stringify({
84
- alert: alert.name,
85
- condition: alert.condition,
86
- value,
87
- timestamp: Date.now()
88
- })
89
- });
90
-
91
- if (!response.ok) {
92
- throw new Error(`Webhook alert failed: ${response.statusText}`);
93
- }
94
- }
95
-
96
- validate(config: { url?: string }): boolean {
97
- try {
98
- if (!config.url) return false;
99
- new URL(config.url);
100
- return true;
101
- } catch {
102
- return false;
103
- }
104
- }
105
- }
106
-
107
- /**
108
- * Email alert adapter (console fallback - implement with real email service)
109
- */
110
- export class EmailAlertAdapter implements AlertChannelAdapter {
111
- async send(alert: AlertDefinition, value: any, config: { recipients: string[] }): Promise<void> {
112
- if (!config.recipients || config.recipients.length === 0) {
113
- throw new Error('Email recipients are required');
114
- }
115
-
116
- // TODO: Integrate with real email service (SendGrid, Mailgun, etc.)
117
- console.log(`[EMAIL ALERT] To: ${config.recipients.join(', ')}`);
118
- console.log(`[EMAIL ALERT] Subject: Alert: ${alert.name}`);
119
- console.log(`[EMAIL ALERT] Body: ${alert.name} - Current Value: ${value}`);
120
- }
121
-
122
- validate(config: { recipients?: string[] }): boolean {
123
- return !!(
124
- config.recipients &&
125
- Array.isArray(config.recipients) &&
126
- config.recipients.length > 0 &&
127
- config.recipients.every(r => typeof r === 'string' && r.includes('@'))
128
- );
129
- }
130
- }
131
-
132
- /**
133
- * PagerDuty alert adapter
134
- */
135
- export class PagerDutyAlertAdapter implements AlertChannelAdapter {
136
- async send(alert: AlertDefinition, value: any, config: { routingKey: string }): Promise<void> {
137
- if (!config.routingKey) {
138
- throw new Error('PagerDuty routing key is required');
139
- }
140
-
141
- // TODO: Implement real PagerDuty Events API v2 integration
142
- console.log(`[PAGERDUTY ALERT] Routing Key: ${config.routingKey}`);
143
- console.log(`[PAGERDUTY ALERT] Event: ${alert.name} - Value: ${value}`);
144
- }
145
-
146
- validate(config: { routingKey?: string }): boolean {
147
- return !!(config.routingKey && typeof config.routingKey === 'string' && config.routingKey.length > 0);
148
- }
149
- }
150
-
151
- /**
152
- * Telegram alert adapter
153
- */
154
- export class TelegramAlertAdapter implements AlertChannelAdapter {
155
- async send(alert: AlertDefinition, value: any, config: { botToken: string; chatId: string }): Promise<void> {
156
- if (!config.botToken || !config.chatId) {
157
- throw new Error('Telegram bot token and chat ID are required');
158
- }
159
-
160
- const message = `🚨 *Alert: ${alert.name}*\n\nCondition: ${alert.condition}\nCurrent Value: ${value}\nTime: ${new Date().toISOString()}`;
161
-
162
- const response = await fetch(`https://api.telegram.org/bot${config.botToken}/sendMessage`, {
163
- method: 'POST',
164
- headers: { 'Content-Type': 'application/json' },
165
- body: JSON.stringify({
166
- chat_id: config.chatId,
167
- text: message,
168
- parse_mode: 'Markdown'
169
- })
170
- });
171
-
172
- if (!response.ok) {
173
- try {
174
- const error = await response.json() as Record<string, any>;
175
- throw new Error(`Telegram alert failed: ${error.description || response.statusText}`);
176
- } catch {
177
- throw new Error(`Telegram alert failed: ${response.statusText}`);
178
- }
179
- }
180
- }
181
-
182
- validate(config: { botToken?: string; chatId?: string }): boolean {
183
- return !!(
184
- config.botToken &&
185
- typeof config.botToken === 'string' &&
186
- config.botToken.length > 0 &&
187
- config.chatId &&
188
- typeof config.chatId === 'string' &&
189
- config.chatId.length > 0
190
- );
191
- }
192
- }
193
-
194
- /**
195
- * Discord alert adapter
196
- */
197
- export class DiscordAlertAdapter implements AlertChannelAdapter {
198
- async send(alert: AlertDefinition, value: any, config: { webhookUrl: string }): Promise<void> {
199
- if (!config.webhookUrl) {
200
- throw new Error('Discord webhook URL is required');
201
- }
202
-
203
- const response = await fetch(config.webhookUrl, {
204
- method: 'POST',
205
- headers: { 'Content-Type': 'application/json' },
206
- body: JSON.stringify({
207
- content: `🚨 **Alert: ${alert.name}**`,
208
- embeds: [
209
- {
210
- title: alert.name,
211
- description: `**Condition:** ${alert.condition}\n**Current Value:** ${value}`,
212
- color: 15158332, // Red
213
- timestamp: new Date().toISOString()
214
- }
215
- ]
216
- })
217
- });
218
-
219
- if (!response.ok) {
220
- throw new Error(`Discord alert failed: ${response.statusText}`);
221
- }
222
- }
223
-
224
- validate(config: { webhookUrl?: string }): boolean {
225
- try {
226
- if (!config.webhookUrl) return false;
227
- const url = new URL(config.webhookUrl);
228
- return url.hostname.includes('discord.com');
229
- } catch {
230
- return false;
231
- }
232
- }
233
- }
234
-
235
- /**
236
- * Console alert adapter (for testing/logging)
237
- */
238
- export class ConsoleAlertAdapter implements AlertChannelAdapter {
239
- async send(alert: AlertDefinition, value: any, _config: any): Promise<void> {
240
- const timestamp = new Date().toISOString();
241
- console.log(`\n[ALERT] ${timestamp}`);
242
- console.log(` Name: ${alert.name}`);
243
- console.log(` Condition: ${alert.condition}`);
244
- console.log(` Current Value: ${value}\n`);
245
- }
246
-
247
- validate(_config: any): boolean {
248
- return true; // Console adapter doesn't need config
249
- }
250
- }
251
-
252
- /**
253
- * Alert channel adapter registry
254
- * Manages all available alert channel adapters
255
- */
256
- export class AlertChannelAdapterRegistry {
257
- private adapters: Map<string, AlertChannelAdapter> = new Map();
258
-
259
- constructor() {
260
- // Register built-in adapters
261
- this.register('slack', new SlackAlertAdapter());
262
- this.register('webhook', new WebhookAlertAdapter());
263
- this.register('email', new EmailAlertAdapter());
264
- this.register('pagerduty', new PagerDutyAlertAdapter());
265
- this.register('telegram', new TelegramAlertAdapter());
266
- this.register('discord', new DiscordAlertAdapter());
267
- this.register('console', new ConsoleAlertAdapter());
268
- }
269
-
270
- /**
271
- * Register a new alert channel adapter
272
- */
273
- register(name: string, adapter: AlertChannelAdapter): void {
274
- this.adapters.set(name.toLowerCase(), adapter);
275
- }
276
-
277
- /**
278
- * Get an adapter by name
279
- */
280
- get(name: string): AlertChannelAdapter | undefined {
281
- return this.adapters.get(name.toLowerCase());
282
- }
283
-
284
- /**
285
- * Get all registered adapter names
286
- */
287
- getNames(): string[] {
288
- return Array.from(this.adapters.keys());
289
- }
290
-
291
- /**
292
- * Check if an adapter is registered
293
- */
294
- has(name: string): boolean {
295
- return this.adapters.has(name.toLowerCase());
296
- }
297
-
298
- /**
299
- * Remove an adapter
300
- */
301
- unregister(name: string): boolean {
302
- return this.adapters.delete(name.toLowerCase());
303
- }
304
- }
@@ -1,63 +0,0 @@
1
- import { randomUUID } from 'crypto';
2
- import { performance } from 'perf_hooks';
3
- import { Middleware } from '../../core/types';
4
- import { ObservabilityCenter } from './ObservabilityCenter';
5
- import { ObservabilityOptions } from './types';
6
-
7
- /**
8
- * Create observability middleware
9
- */
10
-
11
-
12
- export function createObservabilityMiddleware(
13
- center: ObservabilityCenter,
14
- options: ObservabilityOptions
15
- ): Middleware {
16
- const correlationHeader = options.logging?.correlationId?.header ?? 'x-request-id';
17
- const correlationGenerator = options.logging?.correlationId?.generator ?? (() => randomUUID());
18
-
19
- return async (ctx, next, _deps) => {
20
- // Set correlation ID
21
- const correlationId = (ctx.headers[correlationHeader] as string) || correlationGenerator();
22
- ctx.correlationId = correlationId;
23
-
24
- // Start span if tracing is enabled
25
- const span = center.startSpan(`${ctx.method} ${ctx.path}`, ctx);
26
- if (span) {
27
- ctx.spanId = span.spanId;
28
- ctx.traceId = span.traceId;
29
- }
30
-
31
- const start = performance.now();
32
- try {
33
- const response = await next(ctx);
34
- const duration = performance.now() - start;
35
-
36
- center.recordRequest(ctx, response, duration);
37
-
38
- if (span) {
39
- center.setSpanAttributes(span.spanId, {
40
- 'http.status_code': response.statusCode,
41
- 'http.response_size': response.body?.length ?? 0
42
- });
43
- center.endSpan(span.spanId, response.statusCode < 400 ? 'ok' : 'error');
44
- }
45
-
46
- return response;
47
- } catch (error) {
48
- const duration = performance.now() - start;
49
- center.recordRequest(
50
- ctx,
51
- { statusCode: 500, headers: {}, body: '' },
52
- duration,
53
- error as Error
54
- );
55
-
56
- if (span) {
57
- center.endSpan(span.spanId, 'error', error as Error);
58
- }
59
-
60
- throw error;
61
- }
62
- };
63
- }
@@ -1,11 +0,0 @@
1
- export {
2
- AlertChannelAdapter,
3
- AlertChannelAdapterRegistry,
4
- SlackAlertAdapter,
5
- WebhookAlertAdapter,
6
- EmailAlertAdapter,
7
- PagerDutyAlertAdapter,
8
- TelegramAlertAdapter,
9
- DiscordAlertAdapter,
10
- ConsoleAlertAdapter
11
- } from './adapters';