@noony-serverless/core 0.6.0 → 0.8.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.
@@ -23,6 +23,30 @@ export interface BaseMiddleware<T = unknown, U = unknown> {
23
23
  * process a request/response flow either before the main handler (via `before`),
24
24
  * after the main handler (via `after`), or handle errors (via `onError`).
25
25
  *
26
+ * @example Type-safe handler with explicit types
27
+ * interface LoginRequest {
28
+ * email: string;
29
+ * password: string;
30
+ * }
31
+ *
32
+ * interface AuthUser extends BaseAuthenticatedUser {
33
+ * role: 'admin' | 'user';
34
+ * }
35
+ *
36
+ * const handler = new Handler<LoginRequest, AuthUser>()
37
+ * .use(new ErrorHandlerMiddleware<LoginRequest, AuthUser>())
38
+ * .use(new BodyValidationMiddleware<LoginRequest, AuthUser>(loginSchema))
39
+ * .handle(loginController); // ✅ No 'as any' needed
40
+ *
41
+ * @example Type inference with createTypedHandler
42
+ * async function loginController(context: Context<LoginRequest, AuthUser>) {
43
+ * // controller logic
44
+ * }
45
+ *
46
+ * const handler = createTypedHandler(loginController)
47
+ * .use(new ErrorHandlerMiddleware()) // Types inferred
48
+ * .handle(loginController); // ✅ No 'as any' needed
49
+ *
26
50
  * interface MessagePayload {
27
51
  * action: string;
28
52
  * data: Record<string, unknown>;
@@ -45,7 +69,7 @@ export declare class Handler<T = unknown, U = unknown> {
45
69
  private errorMiddlewares;
46
70
  private middlewaresPrecomputed;
47
71
  static use<T = unknown, U = unknown>(middleware: BaseMiddleware<T, U>): Handler<T, U>;
48
- use<NewT = T, NewU = U>(middleware: BaseMiddleware<NewT, NewU>): Handler<NewT, NewU>;
72
+ use(middleware: BaseMiddleware<T, U>): Handler<T, U>;
49
73
  handle(handler: (context: Context<T, U>) => Promise<void | unknown>): Handler<T, U>;
50
74
  /**
51
75
  * Performance optimization: Pre-compute middleware arrays to avoid runtime array operations
@@ -69,4 +93,30 @@ export declare class Handler<T = unknown, U = unknown> {
69
93
  */
70
94
  executeGeneric(req: GenericRequest<T>, res: GenericResponse): Promise<void>;
71
95
  }
96
+ /**
97
+ * Helper to infer types automatically from the controller function.
98
+ *
99
+ * This helper is a permanent feature that improves Developer Experience (DX).
100
+ * It eliminates the need to write explicit generic type parameters when they
101
+ * are already defined in the controller signature.
102
+ *
103
+ * @example
104
+ * // Controller with explicit types
105
+ * async function loginController(context: Context<LoginRequest, AuthUser>) {
106
+ * const { email, password } = context.req.validatedBody!;
107
+ * // ... authentication logic
108
+ * }
109
+ *
110
+ * // Helper infers LoginRequest and AuthUser automatically
111
+ * const handler = createTypedHandler(loginController)
112
+ * .use(new ErrorHandlerMiddleware()) // Types inferred
113
+ * .use(new BodyValidationMiddleware(loginSchema))
114
+ * .handle(loginController); // ✅ Types match perfectly, no 'as any' needed
115
+ *
116
+ * @template T - The request body type (inferred from controller's Context<T, U>)
117
+ * @template U - The user type (inferred from controller's Context<T, U>)
118
+ * @param controller - The controller function with explicit Context<T, U> types
119
+ * @returns A new Handler instance with types inferred from the controller
120
+ */
121
+ export declare function createTypedHandler<T, U>(_controller: (context: Context<T, U>) => Promise<void | unknown>): Handler<T, U>;
72
122
  //# sourceMappingURL=handler.d.ts.map
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Handler = void 0;
4
+ exports.createTypedHandler = createTypedHandler;
4
5
  // Container import removed - now using containerPool for performance
5
6
  const core_1 = require("./core");
6
7
  const containerPool_1 = require("./containerPool");
@@ -12,6 +13,30 @@ const containerPool_1 = require("./containerPool");
12
13
  * process a request/response flow either before the main handler (via `before`),
13
14
  * after the main handler (via `after`), or handle errors (via `onError`).
14
15
  *
16
+ * @example Type-safe handler with explicit types
17
+ * interface LoginRequest {
18
+ * email: string;
19
+ * password: string;
20
+ * }
21
+ *
22
+ * interface AuthUser extends BaseAuthenticatedUser {
23
+ * role: 'admin' | 'user';
24
+ * }
25
+ *
26
+ * const handler = new Handler<LoginRequest, AuthUser>()
27
+ * .use(new ErrorHandlerMiddleware<LoginRequest, AuthUser>())
28
+ * .use(new BodyValidationMiddleware<LoginRequest, AuthUser>(loginSchema))
29
+ * .handle(loginController); // ✅ No 'as any' needed
30
+ *
31
+ * @example Type inference with createTypedHandler
32
+ * async function loginController(context: Context<LoginRequest, AuthUser>) {
33
+ * // controller logic
34
+ * }
35
+ *
36
+ * const handler = createTypedHandler(loginController)
37
+ * .use(new ErrorHandlerMiddleware()) // Types inferred
38
+ * .handle(loginController); // ✅ No 'as any' needed
39
+ *
15
40
  * interface MessagePayload {
16
41
  * action: string;
17
42
  * data: Record<string, unknown>;
@@ -40,12 +65,8 @@ class Handler {
40
65
  return handler;
41
66
  }
42
67
  use(middleware) {
43
- const handler = new Handler();
44
- handler.baseMiddlewares = [
45
- ...this.baseMiddlewares,
46
- middleware,
47
- ];
48
- return handler;
68
+ this.baseMiddlewares.push(middleware);
69
+ return this;
49
70
  }
50
71
  handle(handler) {
51
72
  this.handler = handler;
@@ -152,4 +173,32 @@ class Handler {
152
173
  }
153
174
  }
154
175
  exports.Handler = Handler;
176
+ /**
177
+ * Helper to infer types automatically from the controller function.
178
+ *
179
+ * This helper is a permanent feature that improves Developer Experience (DX).
180
+ * It eliminates the need to write explicit generic type parameters when they
181
+ * are already defined in the controller signature.
182
+ *
183
+ * @example
184
+ * // Controller with explicit types
185
+ * async function loginController(context: Context<LoginRequest, AuthUser>) {
186
+ * const { email, password } = context.req.validatedBody!;
187
+ * // ... authentication logic
188
+ * }
189
+ *
190
+ * // Helper infers LoginRequest and AuthUser automatically
191
+ * const handler = createTypedHandler(loginController)
192
+ * .use(new ErrorHandlerMiddleware()) // Types inferred
193
+ * .use(new BodyValidationMiddleware(loginSchema))
194
+ * .handle(loginController); // ✅ Types match perfectly, no 'as any' needed
195
+ *
196
+ * @template T - The request body type (inferred from controller's Context<T, U>)
197
+ * @template U - The user type (inferred from controller's Context<T, U>)
198
+ * @param controller - The controller function with explicit Context<T, U> types
199
+ * @returns A new Handler instance with types inferred from the controller
200
+ */
201
+ function createTypedHandler(_controller) {
202
+ return new Handler();
203
+ }
155
204
  //# sourceMappingURL=handler.js.map
@@ -5,7 +5,6 @@ const otel_helper_1 = require("../utils/otel.helper");
5
5
  // Import trace for dynamic OTEL operations
6
6
  let trace = null;
7
7
  try {
8
- // eslint-disable-next-line @typescript-eslint/no-var-requires
9
8
  trace = require('@opentelemetry/api').trace;
10
9
  }
11
10
  catch {
@@ -76,19 +76,14 @@ class OpenTelemetryProvider {
76
76
  async initialize(config) {
77
77
  try {
78
78
  // Dynamic require to avoid compile-time dependency on OTEL packages
79
- // eslint-disable-next-line @typescript-eslint/no-var-requires
80
79
  const otelApi = require('@opentelemetry/api');
81
80
  const { trace, metrics } = otelApi;
82
- // eslint-disable-next-line @typescript-eslint/no-var-requires
83
81
  const sdkNode = require('@opentelemetry/sdk-node');
84
82
  const { NodeSDK } = sdkNode;
85
- // eslint-disable-next-line @typescript-eslint/no-var-requires
86
83
  const exporterHttp = require('@opentelemetry/exporter-trace-otlp-http');
87
84
  const { OTLPTraceExporter } = exporterHttp;
88
- // eslint-disable-next-line @typescript-eslint/no-var-requires
89
85
  const resources = require('@opentelemetry/resources');
90
86
  const { Resource } = resources;
91
- // eslint-disable-next-line @typescript-eslint/no-var-requires
92
87
  const semConv = require('@opentelemetry/semantic-conventions');
93
88
  const { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } = semConv;
94
89
  // Create resource with service metadata
@@ -201,7 +196,6 @@ class OpenTelemetryProvider {
201
196
  return;
202
197
  try {
203
198
  // Try to get active span for correlation
204
- // eslint-disable-next-line @typescript-eslint/no-var-requires
205
199
  const { trace } = require('@opentelemetry/api');
206
200
  const span = trace.getActiveSpan();
207
201
  const traceContext = span
@@ -274,7 +268,6 @@ class OpenTelemetryProvider {
274
268
  // Add Cloud Trace propagator (priority 1 - reads X-Cloud-Trace-Context from GCP)
275
269
  if (useCloudTrace) {
276
270
  try {
277
- // eslint-disable-next-line @typescript-eslint/no-var-requires
278
271
  const cloudPropagator = require('@google-cloud/opentelemetry-cloud-trace-propagator');
279
272
  const { CloudPropagator } = cloudPropagator;
280
273
  propagators.push(new CloudPropagator());
@@ -287,7 +280,6 @@ class OpenTelemetryProvider {
287
280
  }
288
281
  // Add W3C Trace Context propagator (priority 2 - standard traceparent)
289
282
  if (useW3C) {
290
- // eslint-disable-next-line @typescript-eslint/no-var-requires
291
283
  const otelCore = require('@opentelemetry/core');
292
284
  const { W3CTraceContextPropagator } = otelCore;
293
285
  propagators.push(new W3CTraceContextPropagator());
@@ -295,7 +287,6 @@ class OpenTelemetryProvider {
295
287
  }
296
288
  // If multiple propagators, use CompositePropagator
297
289
  if (propagators.length > 1) {
298
- // eslint-disable-next-line @typescript-eslint/no-var-requires
299
290
  const otelCore = require('@opentelemetry/core');
300
291
  const { CompositePropagator } = otelCore;
301
292
  return new CompositePropagator({ propagators });
@@ -305,7 +296,6 @@ class OpenTelemetryProvider {
305
296
  return propagators[0];
306
297
  }
307
298
  // Fallback to W3C if no propagators configured
308
- // eslint-disable-next-line @typescript-eslint/no-var-requires
309
299
  const otelCore = require('@opentelemetry/core');
310
300
  const { W3CTraceContextPropagator } = otelCore;
311
301
  return new W3CTraceContextPropagator();
@@ -314,7 +304,6 @@ class OpenTelemetryProvider {
314
304
  console.error('[Telemetry] Failed to create propagator:', error);
315
305
  // Return W3C propagator as safe fallback
316
306
  try {
317
- // eslint-disable-next-line @typescript-eslint/no-var-requires
318
307
  const otelCore = require('@opentelemetry/core');
319
308
  const { W3CTraceContextPropagator } = otelCore;
320
309
  return new W3CTraceContextPropagator();
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- /* eslint-disable @typescript-eslint/no-explicit-any */
3
2
  Object.defineProperty(exports, "__esModule", { value: true });
4
3
  exports.dependencyInjection = exports.DependencyInjectionMiddleware = void 0;
5
4
  const containerPool_1 = require("../core/containerPool");
@@ -188,7 +188,6 @@ class OpenTelemetryMiddleware {
188
188
  const carrier = (0, pubsub_trace_utils_1.createParentContext)(traceContext);
189
189
  // Try to extract parent context using OpenTelemetry API
190
190
  try {
191
- // eslint-disable-next-line @typescript-eslint/no-var-requires
192
191
  const otelApi = require('@opentelemetry/api');
193
192
  const { propagation, context: otelContext } = otelApi;
194
193
  // Extract parent context from carrier
@@ -247,7 +246,6 @@ class OpenTelemetryMiddleware {
247
246
  });
248
247
  // Add X-Trace-Id header with clean trace ID
249
248
  try {
250
- // eslint-disable-next-line @typescript-eslint/no-var-requires
251
249
  const otelApi = require('@opentelemetry/api');
252
250
  const { context: otelContext, trace } = otelApi;
253
251
  // Get span from active context
@@ -14,7 +14,6 @@ exports.isOTELInstalled = exports.isOTELActive = exports.createCloudLoggingEntry
14
14
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
15
15
  let trace;
16
16
  try {
17
- // eslint-disable-next-line @typescript-eslint/no-var-requires
18
17
  const otelApi = require('@opentelemetry/api');
19
18
  trace = otelApi.trace;
20
19
  }
@@ -84,7 +84,6 @@ function injectTraceContext(message, context) {
84
84
  const attributes = message.attributes || {};
85
85
  try {
86
86
  // Try to use OpenTelemetry API if available
87
- // eslint-disable-next-line @typescript-eslint/no-var-requires
88
87
  const otelApi = require('@opentelemetry/api');
89
88
  const { trace, propagation, context: otelContext } = otelApi;
90
89
  // Get current context (either from provided context or active context)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noony-serverless/core",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "A Middy base framework compatible with Firebase and GCP Cloud Functions with TypeScript",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -38,36 +38,40 @@
38
38
  "lint": "eslint . --ext .ts",
39
39
  "lint:fix": "eslint . --ext .ts --fix",
40
40
  "format": "prettier --write \"**/*.{ts,js,json}\"",
41
- "format:check": "prettier --check \"src/**/*.{ts,js,json}\""
41
+ "format:check": "prettier --check \"src/**/*.{ts,js,json}\"",
42
+ "audit": "npm audit --audit-level=moderate",
43
+ "audit:fix": "npm audit fix",
44
+ "audit:production": "npm audit --omit=dev --audit-level=moderate"
42
45
  },
43
46
  "dependencies": {
44
47
  "@google-cloud/firestore": "^8.2.0",
45
48
  "@google-cloud/functions-framework": "^5.0.0",
46
49
  "@google-cloud/pubsub": "^5.2.2",
47
50
  "@types/jsonwebtoken": "^9.0.10",
48
- "axios": "^1.11.0",
49
- "fastify": "^5.7.1",
51
+ "axios": "^1.13.4",
52
+ "fastify": "^5.7.4",
50
53
  "firebase-admin": "^13.6.0",
51
54
  "firebase-functions": "^6.6.0",
52
55
  "jsonwebtoken": "^9.0.3",
53
56
  "reflect-metadata": "^0.2.2",
54
57
  "typedi": "^0.10.0",
55
- "zod": "^4.3.5"
58
+ "zod": "^4.3.6"
56
59
  },
57
60
  "devDependencies": {
58
61
  "@types/jest": "^29.5.14",
59
62
  "@types/module-alias": "^2.0.4",
60
- "@types/node": "^20.19.30",
61
- "@typescript-eslint/eslint-plugin": "^6.21.0",
62
- "@typescript-eslint/parser": "^6.21.0",
63
+ "@types/node": "^20.19.31",
64
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
65
+ "@typescript-eslint/parser": "^8.54.0",
63
66
  "concurrently": "^9.2.1",
64
- "eslint": "^8.57.1",
65
- "eslint-config-prettier": "^9.1.2",
67
+ "eslint": "^9.39.2",
68
+ "eslint-config-prettier": "^10.1.8",
66
69
  "eslint-plugin-prettier": "^5.5.5",
67
70
  "firebase-functions-test": "^3.4.1",
71
+ "globals": "^17.3.0",
68
72
  "jest": "^29.7.0",
69
73
  "module-alias": "^2.2.3",
70
- "prettier": "^3.8.0",
74
+ "prettier": "^3.8.1",
71
75
  "ts-jest": "^29.4.6",
72
76
  "typescript": "^5.9.3"
73
77
  },
@@ -128,7 +132,9 @@
128
132
  "@google-cloud/opentelemetry-cloud-trace-propagator": "^0.21.0"
129
133
  },
130
134
  "overrides": {
131
- "body-parser": "^2.2.2"
135
+ "body-parser": "^2.2.2",
136
+ "fast-xml-parser": "^5.3.4",
137
+ "lodash": "^4.17.21"
132
138
  },
133
139
  "directories": {
134
140
  "doc": "docs",