@engjts/nexus 0.1.7 → 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.
- package/dist/advanced/playground/generatePlaygroundHTML.d.ts.map +1 -1
- package/dist/advanced/playground/generatePlaygroundHTML.js +107 -0
- package/dist/advanced/playground/generatePlaygroundHTML.js.map +1 -1
- package/dist/advanced/playground/playground.d.ts +19 -0
- package/dist/advanced/playground/playground.d.ts.map +1 -1
- package/dist/advanced/playground/playground.js +70 -0
- package/dist/advanced/playground/playground.js.map +1 -1
- package/dist/advanced/playground/types.d.ts +20 -0
- package/dist/advanced/playground/types.d.ts.map +1 -1
- package/dist/core/application.d.ts +14 -0
- package/dist/core/application.d.ts.map +1 -1
- package/dist/core/application.js +173 -71
- package/dist/core/application.js.map +1 -1
- package/dist/core/context-pool.d.ts +2 -13
- package/dist/core/context-pool.d.ts.map +1 -1
- package/dist/core/context-pool.js +7 -45
- package/dist/core/context-pool.js.map +1 -1
- package/dist/core/context.d.ts +108 -5
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +449 -53
- package/dist/core/context.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +9 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/middleware.d.ts +6 -0
- package/dist/core/middleware.d.ts.map +1 -1
- package/dist/core/middleware.js +83 -84
- package/dist/core/middleware.js.map +1 -1
- package/dist/core/performance/fast-json.d.ts +149 -0
- package/dist/core/performance/fast-json.d.ts.map +1 -0
- package/dist/core/performance/fast-json.js +473 -0
- package/dist/core/performance/fast-json.js.map +1 -0
- package/dist/core/router/file-router.d.ts +20 -7
- package/dist/core/router/file-router.d.ts.map +1 -1
- package/dist/core/router/file-router.js +41 -13
- package/dist/core/router/file-router.js.map +1 -1
- package/dist/core/router/index.d.ts +6 -0
- package/dist/core/router/index.d.ts.map +1 -1
- package/dist/core/router/index.js +33 -6
- package/dist/core/router/index.js.map +1 -1
- package/dist/core/router/radix-tree.d.ts +4 -1
- package/dist/core/router/radix-tree.d.ts.map +1 -1
- package/dist/core/router/radix-tree.js +7 -3
- package/dist/core/router/radix-tree.js.map +1 -1
- package/dist/core/serializer.d.ts +251 -0
- package/dist/core/serializer.d.ts.map +1 -0
- package/dist/core/serializer.js +290 -0
- package/dist/core/serializer.js.map +1 -0
- package/dist/core/types.d.ts +39 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/documentation/01-getting-started.md +0 -240
- package/documentation/02-context.md +0 -335
- package/documentation/03-routing.md +0 -397
- package/documentation/04-middleware.md +0 -483
- package/documentation/05-validation.md +0 -514
- package/documentation/06-error-handling.md +0 -465
- package/documentation/07-performance.md +0 -364
- package/documentation/08-adapters.md +0 -470
- package/documentation/09-api-reference.md +0 -548
- package/documentation/10-examples.md +0 -582
- package/documentation/11-deployment.md +0 -477
- package/documentation/12-sentry.md +0 -620
- package/documentation/13-sentry-data-storage.md +0 -996
- package/documentation/14-sentry-data-reference.md +0 -457
- package/documentation/15-sentry-summary.md +0 -409
- package/documentation/16-alerts-system.md +0 -745
- package/documentation/17-alert-adapters.md +0 -696
- package/documentation/18-alerts-implementation-summary.md +0 -385
- package/documentation/19-class-based-routing.md +0 -840
- package/documentation/20-websocket-realtime.md +0 -813
- package/documentation/21-cache-system.md +0 -510
- package/documentation/22-job-queue.md +0 -772
- package/documentation/23-sentry-plugin.md +0 -551
- package/documentation/24-testing-utilities.md +0 -1287
- package/documentation/25-api-versioning.md +0 -533
- package/documentation/26-context-store.md +0 -607
- package/documentation/27-dependency-injection.md +0 -329
- package/documentation/28-lifecycle-hooks.md +0 -521
- package/documentation/29-package-structure.md +0 -196
- package/documentation/30-plugin-system.md +0 -414
- package/documentation/31-jwt-authentication.md +0 -597
- package/documentation/32-cli.md +0 -268
- package/documentation/ALERTS-COMPLETE-SUMMARY.md +0 -429
- package/documentation/ALERTS-INDEX.md +0 -330
- package/documentation/ALERTS-QUICK-REFERENCE.md +0 -286
- package/documentation/README.md +0 -178
- package/documentation/index.html +0 -34
- package/modern_framework_paper.md +0 -1870
- package/public/css/style.css +0 -87
- package/public/index.html +0 -34
- package/public/js/app.js +0 -27
- package/src/advanced/cache/InMemoryCacheStore.ts +0 -68
- package/src/advanced/cache/MultiTierCache.ts +0 -194
- package/src/advanced/cache/RedisCacheStore.ts +0 -341
- package/src/advanced/cache/index.ts +0 -5
- package/src/advanced/cache/types.ts +0 -40
- package/src/advanced/graphql/SimpleDataLoader.ts +0 -42
- package/src/advanced/graphql/index.ts +0 -22
- package/src/advanced/graphql/server.ts +0 -252
- package/src/advanced/graphql/types.ts +0 -42
- package/src/advanced/jobs/InMemoryQueueStore.ts +0 -68
- package/src/advanced/jobs/JobQueue.ts +0 -556
- package/src/advanced/jobs/RedisQueueStore.ts +0 -367
- package/src/advanced/jobs/index.ts +0 -5
- package/src/advanced/jobs/types.ts +0 -70
- package/src/advanced/observability/APMManager.ts +0 -163
- package/src/advanced/observability/AlertManager.ts +0 -109
- package/src/advanced/observability/MetricRegistry.ts +0 -151
- package/src/advanced/observability/ObservabilityCenter.ts +0 -304
- package/src/advanced/observability/StructuredLogger.ts +0 -154
- package/src/advanced/observability/TracingManager.ts +0 -117
- package/src/advanced/observability/adapters.ts +0 -304
- package/src/advanced/observability/createObservabilityMiddleware.ts +0 -63
- package/src/advanced/observability/index.ts +0 -11
- package/src/advanced/observability/types.ts +0 -174
- package/src/advanced/playground/extractPathParams.ts +0 -6
- package/src/advanced/playground/generateFieldExample.ts +0 -31
- package/src/advanced/playground/generatePlaygroundHTML.ts +0 -1849
- package/src/advanced/playground/generateSummary.ts +0 -19
- package/src/advanced/playground/getTagFromPath.ts +0 -9
- package/src/advanced/playground/index.ts +0 -8
- package/src/advanced/playground/playground.ts +0 -170
- package/src/advanced/playground/types.ts +0 -20
- package/src/advanced/playground/zodToExample.ts +0 -16
- package/src/advanced/playground/zodToParams.ts +0 -15
- package/src/advanced/postman/buildAuth.ts +0 -31
- package/src/advanced/postman/buildBody.ts +0 -15
- package/src/advanced/postman/buildQueryParams.ts +0 -27
- package/src/advanced/postman/buildRequestItem.ts +0 -36
- package/src/advanced/postman/buildResponses.ts +0 -11
- package/src/advanced/postman/buildUrl.ts +0 -33
- package/src/advanced/postman/capitalize.ts +0 -4
- package/src/advanced/postman/generateCollection.ts +0 -59
- package/src/advanced/postman/generateEnvironment.ts +0 -34
- package/src/advanced/postman/generateExampleFromZod.ts +0 -21
- package/src/advanced/postman/generateFieldExample.ts +0 -45
- package/src/advanced/postman/generateName.ts +0 -20
- package/src/advanced/postman/generateUUID.ts +0 -11
- package/src/advanced/postman/getTagFromPath.ts +0 -10
- package/src/advanced/postman/index.ts +0 -28
- package/src/advanced/postman/postman.ts +0 -156
- package/src/advanced/postman/slugify.ts +0 -7
- package/src/advanced/postman/types.ts +0 -140
- package/src/advanced/realtime/index.ts +0 -18
- package/src/advanced/realtime/websocket.ts +0 -231
- package/src/advanced/sentry/index.ts +0 -1236
- package/src/advanced/sentry/types.ts +0 -355
- package/src/advanced/static/generateDirectoryListing.ts +0 -47
- package/src/advanced/static/generateETag.ts +0 -7
- package/src/advanced/static/getMimeType.ts +0 -9
- package/src/advanced/static/index.ts +0 -32
- package/src/advanced/static/isSafePath.ts +0 -13
- package/src/advanced/static/publicDir.ts +0 -21
- package/src/advanced/static/serveStatic.ts +0 -225
- package/src/advanced/static/spa.ts +0 -24
- package/src/advanced/static/types.ts +0 -159
- package/src/advanced/swagger/SwaggerGenerator.ts +0 -66
- package/src/advanced/swagger/buildOperation.ts +0 -61
- package/src/advanced/swagger/buildParameters.ts +0 -61
- package/src/advanced/swagger/buildRequestBody.ts +0 -21
- package/src/advanced/swagger/buildResponses.ts +0 -54
- package/src/advanced/swagger/capitalize.ts +0 -5
- package/src/advanced/swagger/convertPath.ts +0 -9
- package/src/advanced/swagger/createSwagger.ts +0 -12
- package/src/advanced/swagger/generateOperationId.ts +0 -21
- package/src/advanced/swagger/generateSpec.ts +0 -105
- package/src/advanced/swagger/generateSummary.ts +0 -24
- package/src/advanced/swagger/generateSwaggerUI.ts +0 -70
- package/src/advanced/swagger/generateThemeCss.ts +0 -53
- package/src/advanced/swagger/index.ts +0 -25
- package/src/advanced/swagger/swagger.ts +0 -237
- package/src/advanced/swagger/types.ts +0 -206
- package/src/advanced/swagger/zodFieldToOpenAPI.ts +0 -94
- package/src/advanced/swagger/zodSchemaToOpenAPI.ts +0 -50
- package/src/advanced/swagger/zodToOpenAPI.ts +0 -22
- package/src/advanced/testing/factory.ts +0 -509
- package/src/advanced/testing/harness.ts +0 -612
- package/src/advanced/testing/index.ts +0 -430
- package/src/advanced/testing/load-test.ts +0 -618
- package/src/advanced/testing/mock-server.ts +0 -498
- package/src/advanced/testing/mock.ts +0 -670
- package/src/cli/bin.ts +0 -9
- package/src/cli/cli.ts +0 -158
- package/src/cli/commands/add.ts +0 -178
- package/src/cli/commands/build.ts +0 -73
- package/src/cli/commands/create.ts +0 -166
- package/src/cli/commands/dev.ts +0 -85
- package/src/cli/commands/generate.ts +0 -99
- package/src/cli/commands/help.ts +0 -95
- package/src/cli/commands/init.ts +0 -91
- package/src/cli/commands/version.ts +0 -38
- package/src/cli/index.ts +0 -6
- package/src/cli/templates/generators.ts +0 -359
- package/src/cli/templates/index.ts +0 -680
- package/src/cli/utils/exec.ts +0 -52
- package/src/cli/utils/file-system.ts +0 -78
- package/src/cli/utils/logger.ts +0 -111
- package/src/core/adapter.ts +0 -88
- package/src/core/application.ts +0 -1335
- package/src/core/context-pool.ts +0 -127
- package/src/core/context.ts +0 -412
- package/src/core/index.ts +0 -80
- package/src/core/middleware.ts +0 -262
- package/src/core/performance/buffer-pool.ts +0 -108
- package/src/core/performance/middleware-optimizer.ts +0 -162
- package/src/core/plugin/PluginManager.ts +0 -435
- package/src/core/plugin/builder.ts +0 -358
- package/src/core/plugin/index.ts +0 -50
- package/src/core/plugin/types.ts +0 -214
- package/src/core/router/file-router.ts +0 -594
- package/src/core/router/index.ts +0 -227
- package/src/core/router/radix-tree.ts +0 -226
- package/src/core/store/index.ts +0 -30
- package/src/core/store/registry.ts +0 -178
- package/src/core/store/request-store.ts +0 -240
- package/src/core/store/types.ts +0 -233
- package/src/core/types.ts +0 -574
- package/src/database/adapter.ts +0 -35
- package/src/database/adapters/index.ts +0 -1
- package/src/database/adapters/mysql.ts +0 -669
- package/src/database/database.ts +0 -70
- package/src/database/dialect.ts +0 -388
- package/src/database/index.ts +0 -12
- package/src/database/migrations.ts +0 -86
- package/src/database/optimizer.ts +0 -125
- package/src/database/query-builder.ts +0 -404
- package/src/database/realtime.ts +0 -53
- package/src/database/schema.ts +0 -71
- package/src/database/transactions.ts +0 -56
- package/src/database/types.ts +0 -87
- package/src/deployment/cluster.ts +0 -471
- package/src/deployment/config.ts +0 -454
- package/src/deployment/docker.ts +0 -599
- package/src/deployment/graceful-shutdown.ts +0 -373
- package/src/deployment/index.ts +0 -56
- package/src/index.ts +0 -264
- package/src/security/adapter.ts +0 -318
- package/src/security/auth/JWTPlugin.ts +0 -234
- package/src/security/auth/JWTProvider.ts +0 -316
- package/src/security/auth/adapter.ts +0 -12
- package/src/security/auth/jwt.ts +0 -234
- package/src/security/auth/middleware.ts +0 -188
- package/src/security/csrf.ts +0 -220
- package/src/security/headers.ts +0 -108
- package/src/security/index.ts +0 -60
- package/src/security/rate-limit/adapter.ts +0 -7
- package/src/security/rate-limit/memory.ts +0 -108
- package/src/security/rate-limit/middleware.ts +0 -181
- package/src/security/sanitization.ts +0 -75
- package/src/security/types.ts +0 -240
- package/src/security/utils.ts +0 -52
- 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
|
-
}
|