@noony-serverless/core 0.3.3 → 0.4.0
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/build/core/containerPool.d.ts +129 -26
- package/build/core/containerPool.js +213 -68
- package/build/core/handler.d.ts +2 -2
- package/build/core/handler.js +6 -12
- package/build/core/index.d.ts +1 -0
- package/build/core/index.js +1 -0
- package/build/core/logger.d.ts +89 -1
- package/build/core/logger.js +136 -5
- package/build/core/telemetry/config.d.ts +331 -0
- package/build/core/telemetry/config.js +153 -0
- package/build/core/telemetry/index.d.ts +22 -0
- package/build/core/telemetry/index.js +45 -0
- package/build/core/telemetry/provider.d.ts +203 -0
- package/build/core/telemetry/provider.js +3 -0
- package/build/core/telemetry/providers/console-provider.d.ts +54 -0
- package/build/core/telemetry/providers/console-provider.js +124 -0
- package/build/core/telemetry/providers/index.d.ts +10 -0
- package/build/core/telemetry/providers/index.js +19 -0
- package/build/core/telemetry/providers/noop-provider.d.ts +51 -0
- package/build/core/telemetry/providers/noop-provider.js +67 -0
- package/build/core/telemetry/providers/opentelemetry-provider.d.ts +102 -0
- package/build/core/telemetry/providers/opentelemetry-provider.js +342 -0
- package/build/middlewares/ProcessingMiddleware.d.ts +6 -3
- package/build/middlewares/ProcessingMiddleware.js +3 -0
- package/build/middlewares/dependencyInjectionMiddleware.d.ts +23 -11
- package/build/middlewares/dependencyInjectionMiddleware.js +36 -12
- package/build/middlewares/guards/adapters/CustomTokenVerificationPortAdapter.d.ts +1 -1
- package/build/middlewares/guards/guards/FastAuthGuard.d.ts +5 -5
- package/build/middlewares/guards/guards/FastAuthGuard.js +3 -2
- package/build/middlewares/guards/guards/PermissionGuardFactory.d.ts +7 -9
- package/build/middlewares/guards/resolvers/ExpressionPermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/resolvers/ExpressionPermissionResolver.js +1 -1
- package/build/middlewares/guards/resolvers/PermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/resolvers/PlainPermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/resolvers/WildcardPermissionResolver.d.ts +1 -1
- package/build/middlewares/guards/services/FastUserContextService.d.ts +11 -32
- package/build/middlewares/headerVariablesMiddleware.d.ts +8 -4
- package/build/middlewares/headerVariablesMiddleware.js +5 -1
- package/build/middlewares/httpAttributesMiddleware.d.ts +5 -3
- package/build/middlewares/httpAttributesMiddleware.js +3 -1
- package/build/middlewares/index.d.ts +1 -0
- package/build/middlewares/index.js +1 -0
- package/build/middlewares/openTelemetryMiddleware.d.ts +162 -0
- package/build/middlewares/openTelemetryMiddleware.js +359 -0
- package/build/middlewares/rateLimitingMiddleware.d.ts +8 -6
- package/build/middlewares/rateLimitingMiddleware.js +19 -6
- package/build/middlewares/securityAuditMiddleware.d.ts +6 -3
- package/build/middlewares/securityAuditMiddleware.js +3 -0
- package/build/middlewares/securityHeadersMiddleware.d.ts +5 -2
- package/build/middlewares/securityHeadersMiddleware.js +3 -0
- package/build/middlewares/validationMiddleware.d.ts +8 -4
- package/build/middlewares/validationMiddleware.js +5 -1
- package/build/utils/container.utils.js +4 -1
- package/build/utils/fastify-wrapper.d.ts +74 -0
- package/build/utils/fastify-wrapper.js +175 -0
- package/build/utils/index.d.ts +4 -0
- package/build/utils/index.js +23 -1
- package/build/utils/otel.helper.d.ts +122 -0
- package/build/utils/otel.helper.js +258 -0
- package/build/utils/pubsub-trace.utils.d.ts +102 -0
- package/build/utils/pubsub-trace.utils.js +155 -0
- package/build/utils/wrapper-utils.d.ts +177 -0
- package/build/utils/wrapper-utils.js +236 -0
- package/package.json +61 -2
|
@@ -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
|
|
@@ -14,7 +14,7 @@ export interface RateLimitOptions {
|
|
|
14
14
|
* Function to generate rate limiting key
|
|
15
15
|
* @default Uses IP address
|
|
16
16
|
*/
|
|
17
|
-
keyGenerator?: (context: Context) => string;
|
|
17
|
+
keyGenerator?: <TBody, TUser>(context: Context<TBody, TUser>) => string;
|
|
18
18
|
/**
|
|
19
19
|
* Custom error message
|
|
20
20
|
* @default 'Too many requests, please try again later'
|
|
@@ -28,7 +28,7 @@ export interface RateLimitOptions {
|
|
|
28
28
|
/**
|
|
29
29
|
* Skip rate limiting for certain requests
|
|
30
30
|
*/
|
|
31
|
-
skip?: (context: Context) => boolean;
|
|
31
|
+
skip?: <TBody, TUser>(context: Context<TBody, TUser>) => boolean;
|
|
32
32
|
/**
|
|
33
33
|
* Headers to include in response
|
|
34
34
|
*/
|
|
@@ -40,7 +40,7 @@ export interface RateLimitOptions {
|
|
|
40
40
|
[key: string]: {
|
|
41
41
|
maxRequests: number;
|
|
42
42
|
windowMs: number;
|
|
43
|
-
matcher: (context: Context) => boolean;
|
|
43
|
+
matcher: <TBody, TUser>(context: Context<TBody, TUser>) => boolean;
|
|
44
44
|
};
|
|
45
45
|
};
|
|
46
46
|
/**
|
|
@@ -120,7 +120,9 @@ declare class MemoryStore implements RateLimitStore {
|
|
|
120
120
|
* - Implement multiple protection layers within middleware
|
|
121
121
|
* - Critical for security in simple deployments
|
|
122
122
|
*
|
|
123
|
-
* @
|
|
123
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
124
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
125
|
+
* @implements {BaseMiddleware<TBody, TUser>}
|
|
124
126
|
*
|
|
125
127
|
* @example
|
|
126
128
|
* Basic API rate limiting:
|
|
@@ -274,11 +276,11 @@ declare class MemoryStore implements RateLimitStore {
|
|
|
274
276
|
* });
|
|
275
277
|
* ```
|
|
276
278
|
*/
|
|
277
|
-
export declare class RateLimitingMiddleware implements BaseMiddleware {
|
|
279
|
+
export declare class RateLimitingMiddleware<TBody = unknown, TUser = unknown> implements BaseMiddleware<TBody, TUser> {
|
|
278
280
|
private store;
|
|
279
281
|
private options;
|
|
280
282
|
constructor(options?: RateLimitOptions);
|
|
281
|
-
before(context: Context): Promise<void>;
|
|
283
|
+
before(context: Context<TBody, TUser>): Promise<void>;
|
|
282
284
|
}
|
|
283
285
|
/**
|
|
284
286
|
* Factory function that creates a rate limiting middleware.
|
|
@@ -138,7 +138,9 @@ const getRateLimit = (context, options) => {
|
|
|
138
138
|
* - Implement multiple protection layers within middleware
|
|
139
139
|
* - Critical for security in simple deployments
|
|
140
140
|
*
|
|
141
|
-
* @
|
|
141
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
142
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
143
|
+
* @implements {BaseMiddleware<TBody, TUser>}
|
|
142
144
|
*
|
|
143
145
|
* @example
|
|
144
146
|
* Basic API rate limiting:
|
|
@@ -616,6 +618,7 @@ exports.RateLimitPresets = {
|
|
|
616
618
|
keyGenerator: (context) => {
|
|
617
619
|
// Rate limit per IP + email combination for better security
|
|
618
620
|
const ip = context.req.ip || 'unknown';
|
|
621
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
619
622
|
const email = context.req.parsedBody?.email;
|
|
620
623
|
return email ? `auth:${email}:${ip}` : `auth:${ip}`;
|
|
621
624
|
},
|
|
@@ -668,28 +671,38 @@ exports.RateLimitPresets = {
|
|
|
668
671
|
free: {
|
|
669
672
|
maxRequests: 100,
|
|
670
673
|
windowMs: 60000,
|
|
671
|
-
matcher: (context) =>
|
|
674
|
+
matcher: (context) =>
|
|
675
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
676
|
+
!context.user || context.user?.plan === 'free',
|
|
672
677
|
},
|
|
673
678
|
premium: {
|
|
674
679
|
maxRequests: 1000,
|
|
675
680
|
windowMs: 60000,
|
|
676
|
-
matcher: (context) =>
|
|
681
|
+
matcher: (context) =>
|
|
682
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
683
|
+
context.user?.plan === 'premium',
|
|
677
684
|
},
|
|
678
685
|
enterprise: {
|
|
679
686
|
maxRequests: 5000,
|
|
680
687
|
windowMs: 60000,
|
|
681
|
-
matcher: (context) =>
|
|
688
|
+
matcher: (context) =>
|
|
689
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
690
|
+
context.user?.plan === 'enterprise',
|
|
682
691
|
},
|
|
683
692
|
admin: {
|
|
684
693
|
maxRequests: 10000,
|
|
685
694
|
windowMs: 60000,
|
|
686
|
-
matcher: (context) =>
|
|
695
|
+
matcher: (context) =>
|
|
696
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
697
|
+
context.user?.role === 'admin',
|
|
687
698
|
},
|
|
688
699
|
},
|
|
689
700
|
keyGenerator: (context) => {
|
|
690
701
|
// Use user ID for authenticated, IP for anonymous
|
|
702
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
691
703
|
return context.user?.id
|
|
692
|
-
?
|
|
704
|
+
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
705
|
+
`user:${context.user.id}`
|
|
693
706
|
: `ip:${context.req.ip}`;
|
|
694
707
|
},
|
|
695
708
|
},
|
|
@@ -72,12 +72,15 @@ declare const securityEventTracker: SecurityEventTracker;
|
|
|
72
72
|
/**
|
|
73
73
|
* Security Audit Middleware
|
|
74
74
|
* Provides comprehensive security event logging and monitoring
|
|
75
|
+
*
|
|
76
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
77
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
75
78
|
*/
|
|
76
|
-
export declare class SecurityAuditMiddleware implements BaseMiddleware {
|
|
79
|
+
export declare class SecurityAuditMiddleware<TBody = unknown, TUser = unknown> implements BaseMiddleware<TBody, TUser> {
|
|
77
80
|
private options;
|
|
78
81
|
constructor(options?: SecurityAuditOptions);
|
|
79
|
-
before(context: Context): Promise<void>;
|
|
80
|
-
after(context: Context): Promise<void>;
|
|
82
|
+
before(context: Context<TBody, TUser>): Promise<void>;
|
|
83
|
+
after(context: Context<TBody, TUser>): Promise<void>;
|
|
81
84
|
onError(error: Error, context: Context): Promise<void>;
|
|
82
85
|
private logSecurityEvent;
|
|
83
86
|
private sanitizeHeaders;
|
|
@@ -156,6 +156,9 @@ const extractClientInfo = (context) => ({
|
|
|
156
156
|
/**
|
|
157
157
|
* Security Audit Middleware
|
|
158
158
|
* Provides comprehensive security event logging and monitoring
|
|
159
|
+
*
|
|
160
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
161
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
159
162
|
*/
|
|
160
163
|
class SecurityAuditMiddleware {
|
|
161
164
|
options;
|
|
@@ -75,11 +75,14 @@ export interface SecurityHeadersOptions {
|
|
|
75
75
|
/**
|
|
76
76
|
* Security Headers Middleware
|
|
77
77
|
* Implements comprehensive security headers following OWASP recommendations
|
|
78
|
+
*
|
|
79
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
80
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
78
81
|
*/
|
|
79
|
-
export declare class SecurityHeadersMiddleware implements BaseMiddleware {
|
|
82
|
+
export declare class SecurityHeadersMiddleware<TBody = unknown, TUser = unknown> implements BaseMiddleware<TBody, TUser> {
|
|
80
83
|
private options;
|
|
81
84
|
constructor(options?: SecurityHeadersOptions);
|
|
82
|
-
before(context: Context): Promise<void>;
|
|
85
|
+
before(context: Context<TBody, TUser>): Promise<void>;
|
|
83
86
|
}
|
|
84
87
|
/**
|
|
85
88
|
* Security Headers Middleware Factory
|
|
@@ -40,6 +40,9 @@ const isOriginAllowed = (origin, allowedOrigins) => {
|
|
|
40
40
|
/**
|
|
41
41
|
* Security Headers Middleware
|
|
42
42
|
* Implements comprehensive security headers following OWASP recommendations
|
|
43
|
+
*
|
|
44
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
45
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
43
46
|
*/
|
|
44
47
|
class SecurityHeadersMiddleware {
|
|
45
48
|
options;
|
|
@@ -4,7 +4,9 @@ import { z } from 'zod';
|
|
|
4
4
|
* Middleware class that validates request data (body or query parameters) using Zod schemas.
|
|
5
5
|
* Automatically detects GET requests and validates query parameters, or validates body for other methods.
|
|
6
6
|
*
|
|
7
|
-
* @
|
|
7
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
8
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
9
|
+
* @implements {BaseMiddleware<TBody, TUser>}
|
|
8
10
|
*
|
|
9
11
|
* @example
|
|
10
12
|
* User registration validation:
|
|
@@ -76,15 +78,17 @@ import { z } from 'zod';
|
|
|
76
78
|
* });
|
|
77
79
|
* ```
|
|
78
80
|
*/
|
|
79
|
-
export declare class ValidationMiddleware implements BaseMiddleware {
|
|
81
|
+
export declare class ValidationMiddleware<TBody = unknown, TUser = unknown> implements BaseMiddleware<TBody, TUser> {
|
|
80
82
|
private readonly schema;
|
|
81
83
|
constructor(schema: z.ZodSchema);
|
|
82
|
-
before(context: Context): Promise<void>;
|
|
84
|
+
before(context: Context<TBody, TUser>): Promise<void>;
|
|
83
85
|
}
|
|
84
86
|
/**
|
|
85
87
|
* Factory function that creates a validation middleware using Zod schema.
|
|
86
88
|
* Automatically validates request body for non-GET requests or query parameters for GET requests.
|
|
87
89
|
*
|
|
90
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
91
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
88
92
|
* @param schema - Zod schema to validate against
|
|
89
93
|
* @returns BaseMiddleware object with validation logic
|
|
90
94
|
*
|
|
@@ -150,5 +154,5 @@ export declare class ValidationMiddleware implements BaseMiddleware {
|
|
|
150
154
|
* });
|
|
151
155
|
* ```
|
|
152
156
|
*/
|
|
153
|
-
export declare const validationMiddleware: (schema: z.ZodSchema) => BaseMiddleware
|
|
157
|
+
export declare const validationMiddleware: <TBody = unknown, TUser = unknown>(schema: z.ZodSchema) => BaseMiddleware<TBody, TUser>;
|
|
154
158
|
//# sourceMappingURL=validationMiddleware.d.ts.map
|
|
@@ -25,7 +25,9 @@ const validate = async (schema, context) => {
|
|
|
25
25
|
* Middleware class that validates request data (body or query parameters) using Zod schemas.
|
|
26
26
|
* Automatically detects GET requests and validates query parameters, or validates body for other methods.
|
|
27
27
|
*
|
|
28
|
-
* @
|
|
28
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
29
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
30
|
+
* @implements {BaseMiddleware<TBody, TUser>}
|
|
29
31
|
*
|
|
30
32
|
* @example
|
|
31
33
|
* User registration validation:
|
|
@@ -111,6 +113,8 @@ exports.ValidationMiddleware = ValidationMiddleware;
|
|
|
111
113
|
* Factory function that creates a validation middleware using Zod schema.
|
|
112
114
|
* Automatically validates request body for non-GET requests or query parameters for GET requests.
|
|
113
115
|
*
|
|
116
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
117
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
114
118
|
* @param schema - Zod schema to validate against
|
|
115
119
|
* @returns BaseMiddleware object with validation logic
|
|
116
120
|
*
|
|
@@ -39,10 +39,13 @@ exports.getService = getService;
|
|
|
39
39
|
* }
|
|
40
40
|
* ```
|
|
41
41
|
*/
|
|
42
|
-
function getService(context,
|
|
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
|