@noony-serverless/core 0.3.4 → 0.4.1

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 (53) hide show
  1. package/README.md +199 -0
  2. package/build/core/containerPool.d.ts +129 -26
  3. package/build/core/containerPool.js +213 -68
  4. package/build/core/handler.d.ts +2 -2
  5. package/build/core/handler.js +6 -12
  6. package/build/core/index.d.ts +1 -0
  7. package/build/core/index.js +1 -0
  8. package/build/core/logger.d.ts +89 -1
  9. package/build/core/logger.js +136 -5
  10. package/build/core/telemetry/config.d.ts +331 -0
  11. package/build/core/telemetry/config.js +153 -0
  12. package/build/core/telemetry/index.d.ts +22 -0
  13. package/build/core/telemetry/index.js +45 -0
  14. package/build/core/telemetry/provider.d.ts +203 -0
  15. package/build/core/telemetry/provider.js +3 -0
  16. package/build/core/telemetry/providers/console-provider.d.ts +54 -0
  17. package/build/core/telemetry/providers/console-provider.js +124 -0
  18. package/build/core/telemetry/providers/index.d.ts +10 -0
  19. package/build/core/telemetry/providers/index.js +19 -0
  20. package/build/core/telemetry/providers/noop-provider.d.ts +51 -0
  21. package/build/core/telemetry/providers/noop-provider.js +67 -0
  22. package/build/core/telemetry/providers/opentelemetry-provider.d.ts +102 -0
  23. package/build/core/telemetry/providers/opentelemetry-provider.js +342 -0
  24. package/build/middlewares/bodyValidationMiddleware.js +1 -1
  25. package/build/middlewares/dependencyInjectionMiddleware.d.ts +16 -8
  26. package/build/middlewares/dependencyInjectionMiddleware.js +31 -11
  27. package/build/middlewares/guards/adapters/CustomTokenVerificationPortAdapter.d.ts +1 -1
  28. package/build/middlewares/guards/guards/FastAuthGuard.d.ts +5 -5
  29. package/build/middlewares/guards/guards/FastAuthGuard.js +3 -2
  30. package/build/middlewares/guards/guards/PermissionGuardFactory.d.ts +7 -9
  31. package/build/middlewares/guards/resolvers/ExpressionPermissionResolver.d.ts +1 -1
  32. package/build/middlewares/guards/resolvers/ExpressionPermissionResolver.js +1 -1
  33. package/build/middlewares/guards/resolvers/PermissionResolver.d.ts +1 -1
  34. package/build/middlewares/guards/resolvers/PlainPermissionResolver.d.ts +1 -1
  35. package/build/middlewares/guards/resolvers/WildcardPermissionResolver.d.ts +1 -1
  36. package/build/middlewares/guards/services/FastUserContextService.d.ts +11 -32
  37. package/build/middlewares/index.d.ts +1 -0
  38. package/build/middlewares/index.js +1 -0
  39. package/build/middlewares/openTelemetryMiddleware.d.ts +162 -0
  40. package/build/middlewares/openTelemetryMiddleware.js +359 -0
  41. package/build/middlewares/rateLimitingMiddleware.js +16 -5
  42. package/build/utils/container.utils.js +4 -1
  43. package/build/utils/fastify-wrapper.d.ts +74 -0
  44. package/build/utils/fastify-wrapper.js +175 -0
  45. package/build/utils/index.d.ts +4 -0
  46. package/build/utils/index.js +23 -1
  47. package/build/utils/otel.helper.d.ts +122 -0
  48. package/build/utils/otel.helper.js +258 -0
  49. package/build/utils/pubsub-trace.utils.d.ts +102 -0
  50. package/build/utils/pubsub-trace.utils.js +155 -0
  51. package/build/utils/wrapper-utils.d.ts +177 -0
  52. package/build/utils/wrapper-utils.js +236 -0
  53. package/package.json +61 -2
@@ -23,7 +23,10 @@
23
23
  * @version 1.0.0
24
24
  */
25
25
  import { CacheAdapter } from '../cache/CacheAdapter';
26
- import { GuardConfiguration, PermissionResolutionStrategy } from '../config/GuardConfiguration';
26
+ import { GuardConfiguration } from '../config/GuardConfiguration';
27
+ import { PlainPermissionResolver } from '../resolvers/PlainPermissionResolver';
28
+ import { WildcardPermissionResolver } from '../resolvers/WildcardPermissionResolver';
29
+ import { ExpressionPermissionResolver } from '../resolvers/ExpressionPermissionResolver';
27
30
  import { PermissionRegistry } from '../registry/PermissionRegistry';
28
31
  import { PermissionResolverType, PermissionCheckResult, PermissionExpression } from '../resolvers/PermissionResolver';
29
32
  /**
@@ -38,7 +41,7 @@ export interface UserContext {
38
41
  userId: string;
39
42
  permissions: Set<string>;
40
43
  roles: string[];
41
- metadata: Record<string, any>;
44
+ metadata: Record<string, unknown>;
42
45
  expandedPermissions?: Set<string>;
43
46
  lastUpdated: string;
44
47
  expiresAt?: string;
@@ -53,7 +56,7 @@ export interface UserPermissionSource {
53
56
  getUserPermissions(userId: string): Promise<{
54
57
  permissions: string[];
55
58
  roles: string[];
56
- metadata?: Record<string, any>;
59
+ metadata?: Record<string, unknown>;
57
60
  } | null>;
58
61
  /**
59
62
  * Get role-based permissions for expansion
@@ -118,7 +121,7 @@ export declare class FastUserContextService {
118
121
  * @param options - Check options
119
122
  * @returns Detailed permission check result
120
123
  */
121
- checkPermission(userId: string, requirement: any, options?: PermissionCheckOptions): Promise<PermissionCheckResult>;
124
+ checkPermission(userId: string, requirement: unknown, options?: PermissionCheckOptions): Promise<PermissionCheckResult>;
122
125
  /**
123
126
  * Batch check multiple permissions for a user
124
127
  *
@@ -131,7 +134,7 @@ export declare class FastUserContextService {
131
134
  * @returns Array of permission check results
132
135
  */
133
136
  checkPermissions(userId: string, requirements: Array<{
134
- requirement: any;
137
+ requirement: unknown;
135
138
  resolverType?: PermissionResolverType;
136
139
  }>, options?: PermissionCheckOptions): Promise<PermissionCheckResult[]>;
137
140
  /**
@@ -166,33 +169,9 @@ export declare class FastUserContextService {
166
169
  averageResolutionTimeUs: number;
167
170
  totalResolutionTimeUs: number;
168
171
  resolverStats: {
169
- plain: {
170
- checkCount: number;
171
- averageResolutionTimeUs: number;
172
- totalResolutionTimeUs: number;
173
- };
174
- wildcard: {
175
- strategy: PermissionResolutionStrategy;
176
- checkCount: number;
177
- averageResolutionTimeUs: number;
178
- totalResolutionTimeUs: number;
179
- cacheHitRate: number;
180
- cacheHits: number;
181
- cacheMisses: number;
182
- };
183
- expression: {
184
- checkCount: number;
185
- averageResolutionTimeUs: number;
186
- totalResolutionTimeUs: number;
187
- cacheHitRate: number;
188
- cacheHits: number;
189
- cacheMisses: number;
190
- complexityDistribution: {
191
- simple: number;
192
- moderate: number;
193
- complex: number;
194
- };
195
- };
172
+ plain: ReturnType<PlainPermissionResolver['getStats']>;
173
+ wildcard: ReturnType<WildcardPermissionResolver['getStats']>;
174
+ expression: ReturnType<ExpressionPermissionResolver['getStats']>;
196
175
  };
197
176
  };
198
177
  /**
@@ -5,6 +5,7 @@ export * from './dependencyInjectionMiddleware';
5
5
  export * from './errorHandlerMiddleware';
6
6
  export * from './headerVariablesMiddleware';
7
7
  export * from './httpAttributesMiddleware';
8
+ export * from './openTelemetryMiddleware';
8
9
  export * from './queryParametersMiddleware';
9
10
  export * from './rateLimitingMiddleware';
10
11
  export * from './responseWrapperMiddleware';
@@ -21,6 +21,7 @@ __exportStar(require("./dependencyInjectionMiddleware"), exports);
21
21
  __exportStar(require("./errorHandlerMiddleware"), exports);
22
22
  __exportStar(require("./headerVariablesMiddleware"), exports);
23
23
  __exportStar(require("./httpAttributesMiddleware"), exports);
24
+ __exportStar(require("./openTelemetryMiddleware"), exports);
24
25
  __exportStar(require("./queryParametersMiddleware"), exports);
25
26
  __exportStar(require("./rateLimitingMiddleware"), exports);
26
27
  __exportStar(require("./responseWrapperMiddleware"), exports);
@@ -0,0 +1,162 @@
1
+ import { BaseMiddleware, Context } from '../core';
2
+ import { TelemetryProvider, TelemetryConfig } from '../core/telemetry/provider';
3
+ /**
4
+ * OpenTelemetry Middleware Options
5
+ */
6
+ export interface OpenTelemetryOptions {
7
+ /**
8
+ * Telemetry provider (auto-detects if not provided)
9
+ * @default Auto-detected based on environment variables
10
+ */
11
+ provider?: TelemetryProvider;
12
+ /**
13
+ * Enable telemetry
14
+ * @default true for production, false for NODE_ENV=test
15
+ */
16
+ enabled?: boolean;
17
+ /**
18
+ * Extract custom attributes from context
19
+ * @default Extracts http.method, http.url, request.id, http.user_agent
20
+ */
21
+ extractAttributes?: (context: Context<unknown, unknown>) => Record<string, unknown>;
22
+ /**
23
+ * Filter which requests to trace
24
+ * @default Traces all requests
25
+ *
26
+ * @example
27
+ * shouldTrace: (context) => context.req.path !== '/health'
28
+ */
29
+ shouldTrace?: (context: Context<unknown, unknown>) => boolean;
30
+ /**
31
+ * Custom error handler for telemetry errors
32
+ * @default Logs error to console
33
+ */
34
+ onError?: (error: Error, context: Context<unknown, unknown>) => void;
35
+ /**
36
+ * Fail silently on telemetry errors (never throw)
37
+ * @default true (telemetry errors never break application)
38
+ */
39
+ failSilently?: boolean;
40
+ /**
41
+ * Enable trace context propagation for Google Cloud Pub/Sub messages
42
+ *
43
+ * When enabled:
44
+ * - Extracts W3C Trace Context from incoming Pub/Sub message attributes
45
+ * - Links new spans to parent trace from publisher
46
+ * - Allows distributed tracing across Pub/Sub producers and consumers
47
+ *
48
+ * Trace context is stored in message attributes as:
49
+ * - `traceparent`: W3C Trace Context version-traceid-spanid-flags
50
+ * - `tracestate`: Vendor-specific trace state (optional)
51
+ *
52
+ * @default true
53
+ */
54
+ propagatePubSubTraces?: boolean;
55
+ }
56
+ /**
57
+ * OpenTelemetry Middleware
58
+ *
59
+ * Provides distributed tracing and metrics collection with:
60
+ * - Auto-detection of telemetry provider from environment
61
+ * - Graceful degradation when configuration is missing
62
+ * - Zero-configuration local development support
63
+ * - Type-safe generics to preserve middleware chain
64
+ *
65
+ * Provider Auto-Detection Priority:
66
+ * 1. Explicit provider via options.provider
67
+ * 2. New Relic (if NEW_RELIC_LICENSE_KEY set)
68
+ * 3. Datadog (if DD_API_KEY or DD_SERVICE set)
69
+ * 4. Standard OTEL (if OTEL_EXPORTER_OTLP_ENDPOINT set)
70
+ * 5. Console (if NODE_ENV=development and no OTEL endpoint)
71
+ * 6. Noop (if NODE_ENV=test or no configuration)
72
+ *
73
+ * @template TBody - Request body type
74
+ * @template TUser - Authenticated user type
75
+ *
76
+ * @example
77
+ * // Zero configuration (auto-detects provider)
78
+ * const handler = new Handler()
79
+ * .use(new OpenTelemetryMiddleware())
80
+ * .handle(async (context) => {
81
+ * // Your business logic
82
+ * });
83
+ *
84
+ * @example
85
+ * // With custom filtering
86
+ * const handler = new Handler()
87
+ * .use(new OpenTelemetryMiddleware({
88
+ * shouldTrace: (context) => context.req.path !== '/health'
89
+ * }))
90
+ * .handle(async (context) => {
91
+ * // Your business logic
92
+ * });
93
+ */
94
+ export declare class OpenTelemetryMiddleware<TBody = unknown, TUser = unknown> implements BaseMiddleware<TBody, TUser> {
95
+ private provider;
96
+ private enabled;
97
+ private failSilently;
98
+ private propagatePubSubTraces;
99
+ private extractAttributes;
100
+ private shouldTrace;
101
+ private customErrorHandler;
102
+ private initialized;
103
+ constructor(options?: OpenTelemetryOptions);
104
+ /**
105
+ * Auto-detect telemetry provider based on environment
106
+ */
107
+ private autoDetectProvider;
108
+ /**
109
+ * Initialize provider with configuration
110
+ *
111
+ * This should be called once at application startup.
112
+ * If not called explicitly, it will be initialized on first request.
113
+ *
114
+ * @param config Telemetry configuration
115
+ */
116
+ initialize(config: TelemetryConfig): Promise<void>;
117
+ /**
118
+ * Before hook - Create span and store in context
119
+ *
120
+ * If propagatePubSubTraces is enabled and the request is a Pub/Sub message:
121
+ * 1. Extracts W3C Trace Context from message attributes
122
+ * 2. Creates a child span linked to the publisher's trace
123
+ * 3. Enables end-to-end distributed tracing across Pub/Sub
124
+ */
125
+ before(context: Context<TBody, TUser>): Promise<void>;
126
+ /**
127
+ * After hook - End span with success status and add X-Trace-Id header
128
+ */
129
+ after(context: Context<TBody, TUser>): Promise<void>;
130
+ /**
131
+ * Error hook - Record exception and end span
132
+ */
133
+ onError(error: Error, context: Context<TBody, TUser>): Promise<void>;
134
+ /**
135
+ * Default attribute extractor
136
+ */
137
+ private defaultExtractAttributes;
138
+ /**
139
+ * Default error handler
140
+ */
141
+ private defaultOnError;
142
+ /**
143
+ * Get current provider (useful for testing)
144
+ */
145
+ getProvider(): TelemetryProvider;
146
+ /**
147
+ * Shutdown telemetry provider
148
+ *
149
+ * Should be called during application shutdown to flush pending data.
150
+ */
151
+ shutdown(): Promise<void>;
152
+ }
153
+ /**
154
+ * Factory function for OpenTelemetry middleware
155
+ *
156
+ * @example
157
+ * const handler = new Handler()
158
+ * .use(openTelemetry({ shouldTrace: ctx => ctx.req.path !== '/health' }))
159
+ * .handle(async (context) => { });
160
+ */
161
+ export declare const openTelemetry: <TBody = unknown, TUser = unknown>(options?: OpenTelemetryOptions) => OpenTelemetryMiddleware<TBody, TUser>;
162
+ //# sourceMappingURL=openTelemetryMiddleware.d.ts.map
@@ -0,0 +1,359 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.openTelemetry = exports.OpenTelemetryMiddleware = void 0;
4
+ const providers_1 = require("../core/telemetry/providers");
5
+ const pubsub_trace_utils_1 = require("../utils/pubsub-trace.utils");
6
+ /**
7
+ * OpenTelemetry Middleware
8
+ *
9
+ * Provides distributed tracing and metrics collection with:
10
+ * - Auto-detection of telemetry provider from environment
11
+ * - Graceful degradation when configuration is missing
12
+ * - Zero-configuration local development support
13
+ * - Type-safe generics to preserve middleware chain
14
+ *
15
+ * Provider Auto-Detection Priority:
16
+ * 1. Explicit provider via options.provider
17
+ * 2. New Relic (if NEW_RELIC_LICENSE_KEY set)
18
+ * 3. Datadog (if DD_API_KEY or DD_SERVICE set)
19
+ * 4. Standard OTEL (if OTEL_EXPORTER_OTLP_ENDPOINT set)
20
+ * 5. Console (if NODE_ENV=development and no OTEL endpoint)
21
+ * 6. Noop (if NODE_ENV=test or no configuration)
22
+ *
23
+ * @template TBody - Request body type
24
+ * @template TUser - Authenticated user type
25
+ *
26
+ * @example
27
+ * // Zero configuration (auto-detects provider)
28
+ * const handler = new Handler()
29
+ * .use(new OpenTelemetryMiddleware())
30
+ * .handle(async (context) => {
31
+ * // Your business logic
32
+ * });
33
+ *
34
+ * @example
35
+ * // With custom filtering
36
+ * const handler = new Handler()
37
+ * .use(new OpenTelemetryMiddleware({
38
+ * shouldTrace: (context) => context.req.path !== '/health'
39
+ * }))
40
+ * .handle(async (context) => {
41
+ * // Your business logic
42
+ * });
43
+ */
44
+ class OpenTelemetryMiddleware {
45
+ provider;
46
+ enabled;
47
+ failSilently;
48
+ propagatePubSubTraces;
49
+ extractAttributes;
50
+ shouldTrace;
51
+ customErrorHandler;
52
+ initialized = false;
53
+ constructor(options = {}) {
54
+ this.enabled = options.enabled ?? process.env.NODE_ENV !== 'test';
55
+ this.failSilently = options.failSilently ?? true;
56
+ this.propagatePubSubTraces = options.propagatePubSubTraces ?? true;
57
+ this.extractAttributes =
58
+ options.extractAttributes || this.defaultExtractAttributes;
59
+ this.shouldTrace = options.shouldTrace || (() => true);
60
+ this.customErrorHandler = options.onError || this.defaultOnError;
61
+ // Use NoopProvider if disabled
62
+ if (!this.enabled) {
63
+ this.provider = new providers_1.NoopProvider();
64
+ return;
65
+ }
66
+ // Use provided provider or auto-detect
67
+ this.provider = options.provider || this.autoDetectProvider();
68
+ }
69
+ /**
70
+ * Auto-detect telemetry provider based on environment
71
+ */
72
+ autoDetectProvider() {
73
+ // Priority 1: New Relic (check for license key and package)
74
+ if (process.env.NEW_RELIC_LICENSE_KEY) {
75
+ try {
76
+ require.resolve('newrelic');
77
+ console.log('[Telemetry] Detected New Relic configuration');
78
+ // Note: NewRelicProvider would be imported here when implemented
79
+ // const { NewRelicProvider } = require('../core/telemetry/providers/newrelic-provider');
80
+ // return new NewRelicProvider();
81
+ }
82
+ catch {
83
+ console.warn('[Telemetry] NEW_RELIC_LICENSE_KEY set but newrelic package not installed');
84
+ }
85
+ }
86
+ // Priority 2: Datadog (check for API key or service name and package)
87
+ if (process.env.DD_API_KEY || process.env.DD_SERVICE) {
88
+ try {
89
+ require.resolve('dd-trace');
90
+ console.log('[Telemetry] Detected Datadog configuration');
91
+ // Note: DatadogProvider would be imported here when implemented
92
+ // const { DatadogProvider } = require('../core/telemetry/providers/datadog-provider');
93
+ // return new DatadogProvider();
94
+ }
95
+ catch {
96
+ console.warn('[Telemetry] Datadog config detected but dd-trace package not installed');
97
+ }
98
+ }
99
+ // Priority 3: Standard OTEL (check for OTLP endpoint)
100
+ if (process.env.OTEL_EXPORTER_OTLP_ENDPOINT) {
101
+ console.log('[Telemetry] Using standard OpenTelemetry provider');
102
+ return new providers_1.OpenTelemetryProvider();
103
+ }
104
+ // Priority 4: Console (development mode without OTEL endpoint)
105
+ if (process.env.NODE_ENV === 'development' &&
106
+ !process.env.OTEL_EXPORTER_OTLP_ENDPOINT) {
107
+ console.log('[Telemetry] Using console provider for local development');
108
+ return new providers_1.ConsoleProvider();
109
+ }
110
+ // Priority 5: Noop (no configuration found)
111
+ console.log('[Telemetry] No telemetry configuration detected, using Noop provider');
112
+ return new providers_1.NoopProvider();
113
+ }
114
+ /**
115
+ * Initialize provider with configuration
116
+ *
117
+ * This should be called once at application startup.
118
+ * If not called explicitly, it will be initialized on first request.
119
+ *
120
+ * @param config Telemetry configuration
121
+ */
122
+ async initialize(config) {
123
+ if (this.initialized)
124
+ return;
125
+ if (!this.enabled) {
126
+ this.initialized = true;
127
+ return;
128
+ }
129
+ try {
130
+ // Validate provider before initialization
131
+ const validation = await this.provider.validate();
132
+ if (!validation.valid) {
133
+ console.warn(`[Telemetry] Provider '${this.provider.name}' validation failed: ${validation.reason}`);
134
+ console.warn('[Telemetry] Falling back to Noop provider');
135
+ this.provider = new providers_1.NoopProvider();
136
+ this.initialized = true;
137
+ return;
138
+ }
139
+ // Initialize provider
140
+ await this.provider.initialize(config);
141
+ // Check if provider is ready
142
+ if (!this.provider.isReady()) {
143
+ console.warn(`[Telemetry] Provider '${this.provider.name}' initialization failed, falling back to Noop`);
144
+ this.provider = new providers_1.NoopProvider();
145
+ }
146
+ else {
147
+ console.log(`[Telemetry] Provider '${this.provider.name}' initialized successfully`);
148
+ }
149
+ this.initialized = true;
150
+ }
151
+ catch (error) {
152
+ console.error('[Telemetry] Failed to initialize provider:', error);
153
+ this.provider = new providers_1.NoopProvider();
154
+ this.initialized = true;
155
+ }
156
+ }
157
+ /**
158
+ * Before hook - Create span and store in context
159
+ *
160
+ * If propagatePubSubTraces is enabled and the request is a Pub/Sub message:
161
+ * 1. Extracts W3C Trace Context from message attributes
162
+ * 2. Creates a child span linked to the publisher's trace
163
+ * 3. Enables end-to-end distributed tracing across Pub/Sub
164
+ */
165
+ async before(context) {
166
+ if (!this.enabled)
167
+ return;
168
+ // Auto-initialize with minimal config if not initialized
169
+ if (!this.initialized) {
170
+ await this.initialize({
171
+ serviceName: process.env.SERVICE_NAME || 'noony-service',
172
+ serviceVersion: process.env.SERVICE_VERSION || '1.0.0',
173
+ environment: process.env.NODE_ENV || 'production',
174
+ });
175
+ }
176
+ // Check if should trace
177
+ if (!this.shouldTrace(context)) {
178
+ return;
179
+ }
180
+ try {
181
+ // Extract trace context from Pub/Sub message if enabled
182
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
183
+ let parentContext = undefined;
184
+ if (this.propagatePubSubTraces && (0, pubsub_trace_utils_1.isPubSubMessage)(context.req.body)) {
185
+ const traceContext = (0, pubsub_trace_utils_1.extractTraceContext)(context.req.body);
186
+ if (traceContext.traceparent) {
187
+ // Store trace context for span creation
188
+ const carrier = (0, pubsub_trace_utils_1.createParentContext)(traceContext);
189
+ // Try to extract parent context using OpenTelemetry API
190
+ try {
191
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
192
+ const otelApi = require('@opentelemetry/api');
193
+ const { propagation, context: otelContext } = otelApi;
194
+ // Extract parent context from carrier
195
+ parentContext = propagation.extract(otelContext.active(), carrier);
196
+ console.log('[Telemetry] Extracted Pub/Sub trace context:', {
197
+ traceparent: traceContext.traceparent,
198
+ tracestate: traceContext.tracestate,
199
+ });
200
+ }
201
+ catch (err) {
202
+ // OpenTelemetry API not available, continue without parent context
203
+ console.warn('[Telemetry] Failed to extract Pub/Sub trace context:', err);
204
+ }
205
+ }
206
+ }
207
+ // Create span (with parent context if available)
208
+ const span = this.provider.createSpan(context);
209
+ if (!span)
210
+ return;
211
+ // If we have a parent context from Pub/Sub, link the span
212
+ if (parentContext) {
213
+ span.setAttributes({
214
+ 'messaging.system': 'pubsub',
215
+ 'messaging.operation': 'process',
216
+ 'pubsub.message_id':
217
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
218
+ context.req.body?.message?.messageId || 'unknown',
219
+ });
220
+ }
221
+ // Add custom attributes
222
+ const customAttributes = this.extractAttributes(context);
223
+ span.setAttributes(customAttributes);
224
+ // Store span and provider name in businessData
225
+ context.businessData.set('otel_span', span);
226
+ context.businessData.set('otel_provider', this.provider.name);
227
+ }
228
+ catch (error) {
229
+ if (!this.failSilently)
230
+ throw error;
231
+ console.error('[Telemetry] Error in before hook:', error);
232
+ }
233
+ }
234
+ /**
235
+ * After hook - End span with success status and add X-Trace-Id header
236
+ */
237
+ async after(context) {
238
+ if (!this.enabled)
239
+ return;
240
+ try {
241
+ const span = context.businessData.get('otel_span');
242
+ if (span) {
243
+ // Add response attributes
244
+ span.setAttributes({
245
+ 'http.status_code': context.res.statusCode || 200,
246
+ 'request.duration_ms': Date.now() - context.startTime,
247
+ });
248
+ // Add X-Trace-Id header with clean trace ID
249
+ try {
250
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
251
+ const otelApi = require('@opentelemetry/api');
252
+ const { context: otelContext, trace } = otelApi;
253
+ // Get span from active context
254
+ const activeContext = otelContext.active();
255
+ const activeSpan = trace.getSpan(activeContext);
256
+ if (activeSpan) {
257
+ const spanContext = activeSpan.spanContext();
258
+ if (spanContext.traceId) {
259
+ // Add custom header with clean trace ID (32 hex chars)
260
+ context.res.header('X-Trace-Id', spanContext.traceId);
261
+ }
262
+ }
263
+ }
264
+ catch (headerError) {
265
+ // Non-critical error - don't fail the request
266
+ console.warn('[Telemetry] Failed to add X-Trace-Id header:', headerError);
267
+ }
268
+ // Set success status (code 0 = OK in OTEL)
269
+ span.setStatus({ code: 0 });
270
+ // End span
271
+ span.end();
272
+ }
273
+ }
274
+ catch (error) {
275
+ if (!this.failSilently)
276
+ throw error;
277
+ console.error('[Telemetry] Error in after hook:', error);
278
+ }
279
+ }
280
+ /**
281
+ * Error hook - Record exception and end span
282
+ */
283
+ async onError(error, context) {
284
+ if (!this.enabled)
285
+ return;
286
+ try {
287
+ const span = context.businessData.get('otel_span');
288
+ if (span) {
289
+ // Record exception
290
+ span.recordException(error);
291
+ // Set error status (code 1 = ERROR in OTEL, code 2 = ERROR in some systems)
292
+ span.setStatus({
293
+ code: 1,
294
+ message: error.message,
295
+ });
296
+ // End span
297
+ span.end();
298
+ }
299
+ // Call custom error handler
300
+ this.customErrorHandler(error, context);
301
+ }
302
+ catch (err) {
303
+ if (!this.failSilently)
304
+ throw err;
305
+ console.error('[Telemetry] Error in onError hook:', err);
306
+ }
307
+ }
308
+ /**
309
+ * Default attribute extractor
310
+ */
311
+ defaultExtractAttributes(context) {
312
+ return {
313
+ 'http.method': context.req.method,
314
+ 'http.url': context.req.url || context.req.path,
315
+ 'http.target': context.req.path || '/',
316
+ 'request.id': context.requestId,
317
+ 'http.user_agent': context.req.headers?.['user-agent'] || '',
318
+ };
319
+ }
320
+ /**
321
+ * Default error handler
322
+ */
323
+ defaultOnError(error, _context) {
324
+ console.error('[Telemetry] Request error:', {
325
+ name: error.name,
326
+ message: error.message,
327
+ });
328
+ }
329
+ /**
330
+ * Get current provider (useful for testing)
331
+ */
332
+ getProvider() {
333
+ return this.provider;
334
+ }
335
+ /**
336
+ * Shutdown telemetry provider
337
+ *
338
+ * Should be called during application shutdown to flush pending data.
339
+ */
340
+ async shutdown() {
341
+ if (this.provider) {
342
+ await this.provider.shutdown();
343
+ }
344
+ }
345
+ }
346
+ exports.OpenTelemetryMiddleware = OpenTelemetryMiddleware;
347
+ /**
348
+ * Factory function for OpenTelemetry middleware
349
+ *
350
+ * @example
351
+ * const handler = new Handler()
352
+ * .use(openTelemetry({ shouldTrace: ctx => ctx.req.path !== '/health' }))
353
+ * .handle(async (context) => { });
354
+ */
355
+ const openTelemetry = (options = {}) => {
356
+ return new OpenTelemetryMiddleware(options);
357
+ };
358
+ exports.openTelemetry = openTelemetry;
359
+ //# sourceMappingURL=openTelemetryMiddleware.js.map
@@ -618,6 +618,7 @@ exports.RateLimitPresets = {
618
618
  keyGenerator: (context) => {
619
619
  // Rate limit per IP + email combination for better security
620
620
  const ip = context.req.ip || 'unknown';
621
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
621
622
  const email = context.req.parsedBody?.email;
622
623
  return email ? `auth:${email}:${ip}` : `auth:${ip}`;
623
624
  },
@@ -670,28 +671,38 @@ exports.RateLimitPresets = {
670
671
  free: {
671
672
  maxRequests: 100,
672
673
  windowMs: 60000,
673
- matcher: (context) => !context.user || context.user?.plan === 'free',
674
+ matcher: (context) =>
675
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
676
+ !context.user || context.user?.plan === 'free',
674
677
  },
675
678
  premium: {
676
679
  maxRequests: 1000,
677
680
  windowMs: 60000,
678
- matcher: (context) => context.user?.plan === 'premium',
681
+ matcher: (context) =>
682
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
683
+ context.user?.plan === 'premium',
679
684
  },
680
685
  enterprise: {
681
686
  maxRequests: 5000,
682
687
  windowMs: 60000,
683
- matcher: (context) => context.user?.plan === 'enterprise',
688
+ matcher: (context) =>
689
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
690
+ context.user?.plan === 'enterprise',
684
691
  },
685
692
  admin: {
686
693
  maxRequests: 10000,
687
694
  windowMs: 60000,
688
- matcher: (context) => context.user?.role === 'admin',
695
+ matcher: (context) =>
696
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
697
+ context.user?.role === 'admin',
689
698
  },
690
699
  },
691
700
  keyGenerator: (context) => {
692
701
  // Use user ID for authenticated, IP for anonymous
702
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
693
703
  return context.user?.id
694
- ? `user:${context.user.id}`
704
+ ? // eslint-disable-next-line @typescript-eslint/no-explicit-any
705
+ `user:${context.user.id}`
695
706
  : `ip:${context.req.ip}`;
696
707
  },
697
708
  },
@@ -39,10 +39,13 @@ exports.getService = getService;
39
39
  * }
40
40
  * ```
41
41
  */
42
- function getService(context, serviceIdentifier) {
42
+ function getService(context,
43
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
+ serviceIdentifier) {
43
45
  if (!context.container) {
44
46
  throw new Error('Container not initialized. Did you forget to add DependencyInjectionMiddleware?');
45
47
  }
48
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
49
  return context.container.get(serviceIdentifier);
47
50
  }
48
51
  //# sourceMappingURL=container.utils.js.map