@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.
- package/build/core/handler.d.ts +51 -1
- package/build/core/handler.js +55 -6
- package/build/core/logger.js +0 -1
- package/build/core/telemetry/providers/opentelemetry-provider.js +0 -11
- package/build/middlewares/dependencyInjectionMiddleware.js +0 -1
- package/build/middlewares/openTelemetryMiddleware.js +0 -2
- package/build/utils/otel.helper.js +0 -1
- package/build/utils/pubsub-trace.utils.js +0 -1
- package/package.json +18 -12
package/build/core/handler.d.ts
CHANGED
|
@@ -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
|
|
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
|
package/build/core/handler.js
CHANGED
|
@@ -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
|
-
|
|
44
|
-
|
|
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
|
package/build/core/logger.js
CHANGED
|
@@ -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.
|
|
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.
|
|
49
|
-
"fastify": "^5.7.
|
|
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.
|
|
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.
|
|
61
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
62
|
-
"@typescript-eslint/parser": "^
|
|
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": "^
|
|
65
|
-
"eslint-config-prettier": "^
|
|
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.
|
|
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",
|