@noony-serverless/core 0.1.1 → 0.2.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/core.d.ts +16 -48
- package/build/core/core.js +2 -61
- package/build/core/handler.d.ts +37 -16
- package/build/core/handler.js +131 -42
- package/build/core/index.d.ts +0 -1
- package/build/core/index.js +0 -1
- package/build/middlewares/ConsolidatedValidationMiddleware.d.ts +126 -0
- package/build/middlewares/ConsolidatedValidationMiddleware.js +330 -0
- package/build/middlewares/ProcessingMiddleware.d.ts +138 -0
- package/build/middlewares/ProcessingMiddleware.js +425 -0
- package/build/middlewares/SecurityMiddleware.d.ts +157 -0
- package/build/middlewares/SecurityMiddleware.js +307 -0
- package/build/middlewares/authenticationMiddleware.d.ts +379 -0
- package/build/middlewares/authenticationMiddleware.js +216 -0
- package/build/middlewares/bodyParserMiddleware.d.ts +99 -0
- package/build/middlewares/bodyParserMiddleware.js +99 -0
- package/build/middlewares/bodyValidationMiddleware.d.ts +69 -3
- package/build/middlewares/bodyValidationMiddleware.js +68 -2
- package/build/middlewares/dependencyInjectionMiddleware.d.ts +238 -0
- package/build/middlewares/dependencyInjectionMiddleware.js +238 -0
- package/build/middlewares/errorHandlerMiddleware.d.ts +94 -0
- package/build/middlewares/errorHandlerMiddleware.js +105 -0
- package/build/middlewares/guards/RouteGuards.d.ts +476 -21
- package/build/middlewares/guards/RouteGuards.js +418 -21
- package/build/middlewares/guards/adapters/CustomTokenVerificationPortAdapter.d.ts +271 -0
- package/build/middlewares/guards/adapters/CustomTokenVerificationPortAdapter.js +301 -0
- package/build/middlewares/guards/cache/CacheAdapter.d.ts +369 -28
- package/build/middlewares/guards/cache/CacheAdapter.js +124 -5
- package/build/middlewares/guards/cache/MemoryCacheAdapter.d.ts +113 -4
- package/build/middlewares/guards/cache/MemoryCacheAdapter.js +113 -4
- package/build/middlewares/guards/config/GuardConfiguration.d.ts +568 -18
- package/build/middlewares/guards/config/GuardConfiguration.js +266 -10
- package/build/middlewares/guards/guards/FastAuthGuard.d.ts +5 -5
- package/build/middlewares/guards/guards/PermissionGuardFactory.d.ts +5 -13
- package/build/middlewares/guards/guards/PermissionGuardFactory.js +4 -4
- package/build/middlewares/guards/index.d.ts +43 -1
- package/build/middlewares/guards/index.js +46 -1
- 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 +20 -33
- package/build/middlewares/guards/services/FastUserContextService.js +19 -5
- package/build/middlewares/headerVariablesMiddleware.d.ts +118 -0
- package/build/middlewares/headerVariablesMiddleware.js +118 -0
- package/build/middlewares/httpAttributesMiddleware.d.ts +235 -0
- package/build/middlewares/httpAttributesMiddleware.js +236 -1
- package/build/middlewares/index.d.ts +3 -1
- package/build/middlewares/index.js +6 -1
- package/build/middlewares/queryParametersMiddleware.d.ts +105 -0
- package/build/middlewares/queryParametersMiddleware.js +105 -0
- package/build/middlewares/rateLimitingMiddleware.d.ts +601 -9
- package/build/middlewares/rateLimitingMiddleware.js +623 -11
- package/build/middlewares/responseWrapperMiddleware.d.ts +170 -1
- package/build/middlewares/responseWrapperMiddleware.js +170 -1
- package/build/middlewares/securityAuditMiddleware.js +5 -5
- package/package.json +11 -9
- package/build/core/containerPool.d.ts +0 -44
- package/build/core/containerPool.js +0 -103
- package/build/middlewares/validationMiddleware.d.ts +0 -9
- package/build/middlewares/validationMiddleware.js +0 -40
|
@@ -169,6 +169,93 @@ async function verifyToken(tokenVerificationPort, context, options = {}) {
|
|
|
169
169
|
throw new errors_1.AuthenticationError('Invalid authentication');
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
|
+
/**
|
|
173
|
+
* Class-based authentication middleware with comprehensive security features.
|
|
174
|
+
* Provides JWT validation, rate limiting, token blacklisting, and security logging.
|
|
175
|
+
*
|
|
176
|
+
* @template T - The type of user data returned by the token verification port
|
|
177
|
+
*
|
|
178
|
+
* @example
|
|
179
|
+
* Basic JWT authentication:
|
|
180
|
+
* ```typescript
|
|
181
|
+
* import { Handler, AuthenticationMiddleware } from '@noony-serverless/core';
|
|
182
|
+
* import jwt from 'jsonwebtoken';
|
|
183
|
+
*
|
|
184
|
+
* interface User {
|
|
185
|
+
* id: string;
|
|
186
|
+
* email: string;
|
|
187
|
+
* roles: string[];
|
|
188
|
+
* }
|
|
189
|
+
*
|
|
190
|
+
* class JWTVerifier implements CustomTokenVerificationPort<User> {
|
|
191
|
+
* async verifyToken(token: string): Promise<User> {
|
|
192
|
+
* const payload = jwt.verify(token, process.env.JWT_SECRET!) as any;
|
|
193
|
+
* return {
|
|
194
|
+
* id: payload.sub,
|
|
195
|
+
* email: payload.email,
|
|
196
|
+
* roles: payload.roles || []
|
|
197
|
+
* };
|
|
198
|
+
* }
|
|
199
|
+
* }
|
|
200
|
+
*
|
|
201
|
+
* const protectedHandler = new Handler()
|
|
202
|
+
* .use(new AuthenticationMiddleware(new JWTVerifier()))
|
|
203
|
+
* .handle(async (request, context) => {
|
|
204
|
+
* const user = context.user as User;
|
|
205
|
+
* return {
|
|
206
|
+
* success: true,
|
|
207
|
+
* data: { message: `Hello ${user.email}`, userId: user.id }
|
|
208
|
+
* };
|
|
209
|
+
* });
|
|
210
|
+
* ```
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* Advanced authentication with security options:
|
|
214
|
+
* ```typescript
|
|
215
|
+
* const secureAuthMiddleware = new AuthenticationMiddleware(
|
|
216
|
+
* new JWTVerifier(),
|
|
217
|
+
* {
|
|
218
|
+
* maxTokenAge: 1800, // 30 minutes
|
|
219
|
+
* rateLimiting: {
|
|
220
|
+
* maxAttempts: 5,
|
|
221
|
+
* windowMs: 15 * 60 * 1000 // 15 minutes
|
|
222
|
+
* },
|
|
223
|
+
* isTokenBlacklisted: async (tokenId) => {
|
|
224
|
+
* return await redis.sismember('revoked_tokens', tokenId);
|
|
225
|
+
* },
|
|
226
|
+
* requiredClaims: {
|
|
227
|
+
* issuer: 'my-auth-server',
|
|
228
|
+
* audience: 'my-api'
|
|
229
|
+
* }
|
|
230
|
+
* }
|
|
231
|
+
* );
|
|
232
|
+
*
|
|
233
|
+
* const secureHandler = new Handler()
|
|
234
|
+
* .use(secureAuthMiddleware)
|
|
235
|
+
* .handle(async (request, context) => {
|
|
236
|
+
* // Only authenticated users reach here
|
|
237
|
+
* return { success: true, data: 'Secure data' };
|
|
238
|
+
* });
|
|
239
|
+
* ```
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* Google Cloud Functions integration:
|
|
243
|
+
* ```typescript
|
|
244
|
+
* import { http } from '@google-cloud/functions-framework';
|
|
245
|
+
*
|
|
246
|
+
* const userProfileHandler = new Handler()
|
|
247
|
+
* .use(new AuthenticationMiddleware(new JWTVerifier()))
|
|
248
|
+
* .handle(async (request, context) => {
|
|
249
|
+
* const user = context.user as User;
|
|
250
|
+
* const profile = await getUserProfile(user.id);
|
|
251
|
+
* return { success: true, data: profile };
|
|
252
|
+
* });
|
|
253
|
+
*
|
|
254
|
+
* export const getUserProfile = http('getUserProfile', (req, res) => {
|
|
255
|
+
* return userProfileHandler.execute(req, res);
|
|
256
|
+
* });
|
|
257
|
+
* ```
|
|
258
|
+
*/
|
|
172
259
|
class AuthenticationMiddleware {
|
|
173
260
|
tokenVerificationPort;
|
|
174
261
|
options;
|
|
@@ -181,6 +268,135 @@ class AuthenticationMiddleware {
|
|
|
181
268
|
}
|
|
182
269
|
}
|
|
183
270
|
exports.AuthenticationMiddleware = AuthenticationMiddleware;
|
|
271
|
+
/**
|
|
272
|
+
* Factory function that creates an authentication middleware with token verification.
|
|
273
|
+
* Provides a functional approach for authentication setup.
|
|
274
|
+
*
|
|
275
|
+
* @template T - The type of user data returned by the token verification port
|
|
276
|
+
* @param tokenVerificationPort - The token verification implementation
|
|
277
|
+
* @param options - Authentication configuration options
|
|
278
|
+
* @returns A BaseMiddleware object with authentication logic
|
|
279
|
+
*
|
|
280
|
+
* @example
|
|
281
|
+
* Simple JWT authentication:
|
|
282
|
+
* ```typescript
|
|
283
|
+
* import { Handler, verifyAuthTokenMiddleware } from '@noony-serverless/core';
|
|
284
|
+
*
|
|
285
|
+
* class SimpleJWTVerifier implements CustomTokenVerificationPort<{ userId: string }> {
|
|
286
|
+
* async verifyToken(token: string): Promise<{ userId: string }> {
|
|
287
|
+
* // Simple token verification logic
|
|
288
|
+
* if (token === 'valid-token') {
|
|
289
|
+
* return { userId: 'user-123' };
|
|
290
|
+
* }
|
|
291
|
+
* throw new Error('Invalid token');
|
|
292
|
+
* }
|
|
293
|
+
* }
|
|
294
|
+
*
|
|
295
|
+
* const handler = new Handler()
|
|
296
|
+
* .use(verifyAuthTokenMiddleware(new SimpleJWTVerifier()))
|
|
297
|
+
* .handle(async (request, context) => {
|
|
298
|
+
* const user = context.user as { userId: string };
|
|
299
|
+
* return { success: true, userId: user.userId };
|
|
300
|
+
* });
|
|
301
|
+
* ```
|
|
302
|
+
*
|
|
303
|
+
* @example
|
|
304
|
+
* API key authentication with rate limiting:
|
|
305
|
+
* ```typescript
|
|
306
|
+
* interface APIKeyUser {
|
|
307
|
+
* keyId: string;
|
|
308
|
+
* permissions: string[];
|
|
309
|
+
* organization: string;
|
|
310
|
+
* }
|
|
311
|
+
*
|
|
312
|
+
* class APIKeyVerifier implements CustomTokenVerificationPort<APIKeyUser> {
|
|
313
|
+
* async verifyToken(token: string): Promise<APIKeyUser> {
|
|
314
|
+
* const keyData = await this.validateAPIKey(token);
|
|
315
|
+
* if (!keyData) {
|
|
316
|
+
* throw new Error('Invalid API key');
|
|
317
|
+
* }
|
|
318
|
+
* return keyData;
|
|
319
|
+
* }
|
|
320
|
+
*
|
|
321
|
+
* private async validateAPIKey(key: string): Promise<APIKeyUser | null> {
|
|
322
|
+
* // Database lookup or external validation
|
|
323
|
+
* return {
|
|
324
|
+
* keyId: 'key-123',
|
|
325
|
+
* permissions: ['read', 'write'],
|
|
326
|
+
* organization: 'org-456'
|
|
327
|
+
* };
|
|
328
|
+
* }
|
|
329
|
+
* }
|
|
330
|
+
*
|
|
331
|
+
* const apiHandler = new Handler()
|
|
332
|
+
* .use(verifyAuthTokenMiddleware(
|
|
333
|
+
* new APIKeyVerifier(),
|
|
334
|
+
* {
|
|
335
|
+
* rateLimiting: {
|
|
336
|
+
* maxAttempts: 100,
|
|
337
|
+
* windowMs: 60 * 1000 // 1 minute
|
|
338
|
+
* }
|
|
339
|
+
* }
|
|
340
|
+
* ))
|
|
341
|
+
* .handle(async (request, context) => {
|
|
342
|
+
* const apiUser = context.user as APIKeyUser;
|
|
343
|
+
* return {
|
|
344
|
+
* success: true,
|
|
345
|
+
* data: { organization: apiUser.organization }
|
|
346
|
+
* };
|
|
347
|
+
* });
|
|
348
|
+
* ```
|
|
349
|
+
*
|
|
350
|
+
* @example
|
|
351
|
+
* Express-style middleware chain:
|
|
352
|
+
* ```typescript
|
|
353
|
+
* import { Handler, verifyAuthTokenMiddleware, errorHandler } from '@noony-serverless/core';
|
|
354
|
+
*
|
|
355
|
+
* const authMiddleware = verifyAuthTokenMiddleware(
|
|
356
|
+
* new JWTVerifier(),
|
|
357
|
+
* {
|
|
358
|
+
* maxTokenAge: 3600,
|
|
359
|
+
* requiredClaims: {
|
|
360
|
+
* issuer: 'my-app',
|
|
361
|
+
* audience: 'api-users'
|
|
362
|
+
* }
|
|
363
|
+
* }
|
|
364
|
+
* );
|
|
365
|
+
*
|
|
366
|
+
* const protectedEndpoint = new Handler()
|
|
367
|
+
* .use(authMiddleware)
|
|
368
|
+
* .use(errorHandler())
|
|
369
|
+
* .handle(async (request, context) => {
|
|
370
|
+
* // Authenticated user available in context.user
|
|
371
|
+
* return { success: true, data: 'Protected resource' };
|
|
372
|
+
* });
|
|
373
|
+
* ```
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* Multiple authentication strategies:
|
|
377
|
+
* ```typescript
|
|
378
|
+
* // Different handlers for different auth types
|
|
379
|
+
* const jwtHandler = new Handler()
|
|
380
|
+
* .use(verifyAuthTokenMiddleware(new JWTVerifier()))
|
|
381
|
+
* .handle(jwtLogic);
|
|
382
|
+
*
|
|
383
|
+
* const apiKeyHandler = new Handler()
|
|
384
|
+
* .use(verifyAuthTokenMiddleware(new APIKeyVerifier()))
|
|
385
|
+
* .handle(apiKeyLogic);
|
|
386
|
+
*
|
|
387
|
+
* // Route based on authentication type
|
|
388
|
+
* export const handleRequest = (req: any, res: any) => {
|
|
389
|
+
* const authHeader = req.headers.authorization;
|
|
390
|
+
* if (authHeader?.startsWith('Bearer jwt.')) {
|
|
391
|
+
* return jwtHandler.execute(req, res);
|
|
392
|
+
* } else if (authHeader?.startsWith('Bearer ak_')) {
|
|
393
|
+
* return apiKeyHandler.execute(req, res);
|
|
394
|
+
* } else {
|
|
395
|
+
* res.status(401).json({ error: 'Authentication required' });
|
|
396
|
+
* }
|
|
397
|
+
* };
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
184
400
|
const verifyAuthTokenMiddleware = (tokenVerificationPort, options = {}) => ({
|
|
185
401
|
async before(context) {
|
|
186
402
|
await verifyToken(tokenVerificationPort, context, options);
|
|
@@ -10,6 +10,58 @@ import { BaseMiddleware, Context } from '../core';
|
|
|
10
10
|
*
|
|
11
11
|
* @template T - The expected type of the parsed body. Defaults to unknown if not specified.
|
|
12
12
|
* @implements {BaseMiddleware}
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* Basic usage with typed body parsing:
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import { Handler, BodyParserMiddleware } from '@noony-serverless/core';
|
|
18
|
+
*
|
|
19
|
+
* interface UserRequest {
|
|
20
|
+
* name: string;
|
|
21
|
+
* email: string;
|
|
22
|
+
* age: number;
|
|
23
|
+
* }
|
|
24
|
+
*
|
|
25
|
+
* const createUserHandler = new Handler()
|
|
26
|
+
* .use(new BodyParserMiddleware<UserRequest>())
|
|
27
|
+
* .handle(async (context) => {
|
|
28
|
+
* const userData = context.req.parsedBody as UserRequest;
|
|
29
|
+
* console.log('User data:', userData.name, userData.email);
|
|
30
|
+
* return { success: true, data: userData };
|
|
31
|
+
* });
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* Custom size limit configuration:
|
|
36
|
+
* ```typescript
|
|
37
|
+
* const largeBodyParser = new BodyParserMiddleware<any>(2 * 1024 * 1024); // 2MB limit
|
|
38
|
+
*
|
|
39
|
+
* const uploadHandler = new Handler()
|
|
40
|
+
* .use(largeBodyParser)
|
|
41
|
+
* .handle(async (context) => {
|
|
42
|
+
* const uploadData = context.req.parsedBody;
|
|
43
|
+
* return { success: true, size: JSON.stringify(uploadData).length };
|
|
44
|
+
* });
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* Google Cloud Pub/Sub message handling:
|
|
49
|
+
* ```typescript
|
|
50
|
+
* interface PubSubData {
|
|
51
|
+
* eventType: string;
|
|
52
|
+
* timestamp: string;
|
|
53
|
+
* payload: any;
|
|
54
|
+
* }
|
|
55
|
+
*
|
|
56
|
+
* const pubSubHandler = new Handler()
|
|
57
|
+
* .use(new BodyParserMiddleware<PubSubData>())
|
|
58
|
+
* .handle(async (context) => {
|
|
59
|
+
* // Automatically decodes base64 Pub/Sub message data
|
|
60
|
+
* const messageData = context.req.parsedBody as PubSubData;
|
|
61
|
+
* console.log('Event type:', messageData.eventType);
|
|
62
|
+
* return { success: true, processed: true };
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
13
65
|
*/
|
|
14
66
|
export declare class BodyParserMiddleware<T = unknown> implements BaseMiddleware {
|
|
15
67
|
private maxSize;
|
|
@@ -25,7 +77,54 @@ export declare class BodyParserMiddleware<T = unknown> implements BaseMiddleware
|
|
|
25
77
|
* - Size validation
|
|
26
78
|
*
|
|
27
79
|
* @template T - The expected type of the parsed request body.
|
|
80
|
+
* @param maxSize - Maximum allowed body size in bytes (default: 1MB)
|
|
28
81
|
* @returns {BaseMiddleware} A middleware object containing a `before` hook.
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* Basic body parsing with default settings:
|
|
85
|
+
* ```typescript
|
|
86
|
+
* import { Handler, bodyParser } from '@noony-serverless/core';
|
|
87
|
+
*
|
|
88
|
+
* const apiHandler = new Handler()
|
|
89
|
+
* .use(bodyParser<{ name: string; email: string }>())
|
|
90
|
+
* .handle(async (context) => {
|
|
91
|
+
* const body = context.req.parsedBody;
|
|
92
|
+
* return { success: true, received: body };
|
|
93
|
+
* });
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* Custom size limit for large uploads:
|
|
98
|
+
* ```typescript
|
|
99
|
+
* const uploadHandler = new Handler()
|
|
100
|
+
* .use(bodyParser<any>(5 * 1024 * 1024)) // 5MB limit
|
|
101
|
+
* .handle(async (context) => {
|
|
102
|
+
* const uploadData = context.req.parsedBody;
|
|
103
|
+
* console.log('Upload size:', JSON.stringify(uploadData).length);
|
|
104
|
+
* return { success: true, uploadId: generateId() };
|
|
105
|
+
* });
|
|
106
|
+
* ```
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* Combining with validation middleware:
|
|
110
|
+
* ```typescript
|
|
111
|
+
* import { z } from 'zod';
|
|
112
|
+
* import { bodyParser, validationMiddleware } from '@noony-serverless/core';
|
|
113
|
+
*
|
|
114
|
+
* const userSchema = z.object({
|
|
115
|
+
* name: z.string().min(1),
|
|
116
|
+
* email: z.string().email(),
|
|
117
|
+
* age: z.number().int().min(18)
|
|
118
|
+
* });
|
|
119
|
+
*
|
|
120
|
+
* const createUserHandler = new Handler()
|
|
121
|
+
* .use(bodyParser<z.infer<typeof userSchema>>())
|
|
122
|
+
* .use(validationMiddleware(userSchema))
|
|
123
|
+
* .handle(async (context) => {
|
|
124
|
+
* const validatedUser = context.req.validatedBody;
|
|
125
|
+
* return { success: true, user: validatedUser };
|
|
126
|
+
* });
|
|
127
|
+
* ```
|
|
29
128
|
*/
|
|
30
129
|
export declare const bodyParser: <T = unknown>(maxSize?: number) => BaseMiddleware;
|
|
31
130
|
//# sourceMappingURL=bodyParserMiddleware.d.ts.map
|
|
@@ -159,6 +159,58 @@ const parseBody = async (body) => {
|
|
|
159
159
|
*
|
|
160
160
|
* @template T - The expected type of the parsed body. Defaults to unknown if not specified.
|
|
161
161
|
* @implements {BaseMiddleware}
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* Basic usage with typed body parsing:
|
|
165
|
+
* ```typescript
|
|
166
|
+
* import { Handler, BodyParserMiddleware } from '@noony-serverless/core';
|
|
167
|
+
*
|
|
168
|
+
* interface UserRequest {
|
|
169
|
+
* name: string;
|
|
170
|
+
* email: string;
|
|
171
|
+
* age: number;
|
|
172
|
+
* }
|
|
173
|
+
*
|
|
174
|
+
* const createUserHandler = new Handler()
|
|
175
|
+
* .use(new BodyParserMiddleware<UserRequest>())
|
|
176
|
+
* .handle(async (context) => {
|
|
177
|
+
* const userData = context.req.parsedBody as UserRequest;
|
|
178
|
+
* console.log('User data:', userData.name, userData.email);
|
|
179
|
+
* return { success: true, data: userData };
|
|
180
|
+
* });
|
|
181
|
+
* ```
|
|
182
|
+
*
|
|
183
|
+
* @example
|
|
184
|
+
* Custom size limit configuration:
|
|
185
|
+
* ```typescript
|
|
186
|
+
* const largeBodyParser = new BodyParserMiddleware<any>(2 * 1024 * 1024); // 2MB limit
|
|
187
|
+
*
|
|
188
|
+
* const uploadHandler = new Handler()
|
|
189
|
+
* .use(largeBodyParser)
|
|
190
|
+
* .handle(async (context) => {
|
|
191
|
+
* const uploadData = context.req.parsedBody;
|
|
192
|
+
* return { success: true, size: JSON.stringify(uploadData).length };
|
|
193
|
+
* });
|
|
194
|
+
* ```
|
|
195
|
+
*
|
|
196
|
+
* @example
|
|
197
|
+
* Google Cloud Pub/Sub message handling:
|
|
198
|
+
* ```typescript
|
|
199
|
+
* interface PubSubData {
|
|
200
|
+
* eventType: string;
|
|
201
|
+
* timestamp: string;
|
|
202
|
+
* payload: any;
|
|
203
|
+
* }
|
|
204
|
+
*
|
|
205
|
+
* const pubSubHandler = new Handler()
|
|
206
|
+
* .use(new BodyParserMiddleware<PubSubData>())
|
|
207
|
+
* .handle(async (context) => {
|
|
208
|
+
* // Automatically decodes base64 Pub/Sub message data
|
|
209
|
+
* const messageData = context.req.parsedBody as PubSubData;
|
|
210
|
+
* console.log('Event type:', messageData.eventType);
|
|
211
|
+
* return { success: true, processed: true };
|
|
212
|
+
* });
|
|
213
|
+
* ```
|
|
162
214
|
*/
|
|
163
215
|
class BodyParserMiddleware {
|
|
164
216
|
maxSize;
|
|
@@ -190,7 +242,54 @@ exports.BodyParserMiddleware = BodyParserMiddleware;
|
|
|
190
242
|
* - Size validation
|
|
191
243
|
*
|
|
192
244
|
* @template T - The expected type of the parsed request body.
|
|
245
|
+
* @param maxSize - Maximum allowed body size in bytes (default: 1MB)
|
|
193
246
|
* @returns {BaseMiddleware} A middleware object containing a `before` hook.
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* Basic body parsing with default settings:
|
|
250
|
+
* ```typescript
|
|
251
|
+
* import { Handler, bodyParser } from '@noony-serverless/core';
|
|
252
|
+
*
|
|
253
|
+
* const apiHandler = new Handler()
|
|
254
|
+
* .use(bodyParser<{ name: string; email: string }>())
|
|
255
|
+
* .handle(async (context) => {
|
|
256
|
+
* const body = context.req.parsedBody;
|
|
257
|
+
* return { success: true, received: body };
|
|
258
|
+
* });
|
|
259
|
+
* ```
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* Custom size limit for large uploads:
|
|
263
|
+
* ```typescript
|
|
264
|
+
* const uploadHandler = new Handler()
|
|
265
|
+
* .use(bodyParser<any>(5 * 1024 * 1024)) // 5MB limit
|
|
266
|
+
* .handle(async (context) => {
|
|
267
|
+
* const uploadData = context.req.parsedBody;
|
|
268
|
+
* console.log('Upload size:', JSON.stringify(uploadData).length);
|
|
269
|
+
* return { success: true, uploadId: generateId() };
|
|
270
|
+
* });
|
|
271
|
+
* ```
|
|
272
|
+
*
|
|
273
|
+
* @example
|
|
274
|
+
* Combining with validation middleware:
|
|
275
|
+
* ```typescript
|
|
276
|
+
* import { z } from 'zod';
|
|
277
|
+
* import { bodyParser, validationMiddleware } from '@noony-serverless/core';
|
|
278
|
+
*
|
|
279
|
+
* const userSchema = z.object({
|
|
280
|
+
* name: z.string().min(1),
|
|
281
|
+
* email: z.string().email(),
|
|
282
|
+
* age: z.number().int().min(18)
|
|
283
|
+
* });
|
|
284
|
+
*
|
|
285
|
+
* const createUserHandler = new Handler()
|
|
286
|
+
* .use(bodyParser<z.infer<typeof userSchema>>())
|
|
287
|
+
* .use(validationMiddleware(userSchema))
|
|
288
|
+
* .handle(async (context) => {
|
|
289
|
+
* const validatedUser = context.req.validatedBody;
|
|
290
|
+
* return { success: true, user: validatedUser };
|
|
291
|
+
* });
|
|
292
|
+
* ```
|
|
194
293
|
*/
|
|
195
294
|
const bodyParser = (maxSize = MAX_JSON_SIZE) => ({
|
|
196
295
|
before: async (context) => {
|
|
@@ -1,12 +1,78 @@
|
|
|
1
1
|
import { BaseMiddleware } from '../core/handler';
|
|
2
2
|
import { Context } from '../core/core';
|
|
3
3
|
import { z } from 'zod';
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Body validation middleware using Zod schemas for runtime type checking.
|
|
6
|
+
* Validates the parsed request body against a provided Zod schema and sets
|
|
7
|
+
* the validated result in context.req.validatedBody.
|
|
8
|
+
*
|
|
9
|
+
* @template T - The expected type of the validated body data
|
|
10
|
+
* @implements {BaseMiddleware}
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* Simple user creation with type safety:
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { z } from 'zod';
|
|
16
|
+
* import { Handler, BodyValidationMiddleware } from '@noony-serverless/core';
|
|
17
|
+
*
|
|
18
|
+
* const userSchema = z.object({
|
|
19
|
+
* name: z.string().min(1),
|
|
20
|
+
* email: z.string().email(),
|
|
21
|
+
* age: z.number().min(18)
|
|
22
|
+
* });
|
|
23
|
+
*
|
|
24
|
+
* type UserRequest = z.infer<typeof userSchema>;
|
|
25
|
+
*
|
|
26
|
+
* async function handleCreateUser(context: Context<UserRequest>) {
|
|
27
|
+
* const user = context.req.validatedBody!; // Fully typed
|
|
28
|
+
* const authenticatedUser = context.user; // User type inferred from auth middleware
|
|
29
|
+
* return { success: true, user: { id: '123', ...user } };
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* const createUserHandler = new Handler<UserRequest>()
|
|
33
|
+
* .use(new BodyValidationMiddleware<UserRequest>(userSchema))
|
|
34
|
+
* .handle(handleCreateUser);
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare class BodyValidationMiddleware<T = unknown> implements BaseMiddleware<T> {
|
|
5
38
|
private readonly schema;
|
|
6
39
|
constructor(schema: z.ZodSchema<T>);
|
|
7
|
-
before(context: Context): Promise<void>;
|
|
40
|
+
before(context: Context<T>): Promise<void>;
|
|
8
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Factory function that creates a body validation middleware with Zod schema validation.
|
|
44
|
+
* This function validates and parses the request body, setting the result in context.req.parsedBody.
|
|
45
|
+
*
|
|
46
|
+
* @template T - The expected type of the validated body data
|
|
47
|
+
* @param schema - Zod schema to validate against
|
|
48
|
+
* @returns A BaseMiddleware object with validation logic
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* Simple login validation:
|
|
52
|
+
* ```typescript
|
|
53
|
+
* import { z } from 'zod';
|
|
54
|
+
* import { Handler, bodyValidatorMiddleware } from '@noony-serverless/core';
|
|
55
|
+
*
|
|
56
|
+
* const loginSchema = z.object({
|
|
57
|
+
* username: z.string().min(3),
|
|
58
|
+
* password: z.string().min(8)
|
|
59
|
+
* });
|
|
60
|
+
*
|
|
61
|
+
* type LoginRequest = z.infer<typeof loginSchema>;
|
|
62
|
+
*
|
|
63
|
+
* async function handleLogin(context: Context<LoginRequest>) {
|
|
64
|
+
* const credentials = context.req.parsedBody as LoginRequest;
|
|
65
|
+
* const token = await authenticate(credentials.username, credentials.password);
|
|
66
|
+
* const authenticatedUser = context.user; // User type from auth middleware
|
|
67
|
+
* return { success: true, token };
|
|
68
|
+
* }
|
|
69
|
+
*
|
|
70
|
+
* const loginHandler = new Handler<LoginRequest>()
|
|
71
|
+
* .use(bodyValidatorMiddleware<LoginRequest>(loginSchema))
|
|
72
|
+
* .handle(handleLogin);
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
9
75
|
export declare const bodyValidatorMiddleware: <T>(schema: z.ZodType<T>) => {
|
|
10
|
-
before: (context: Context) => Promise<void>;
|
|
76
|
+
before: (context: Context<T>) => Promise<void>;
|
|
11
77
|
};
|
|
12
78
|
//# sourceMappingURL=bodyValidationMiddleware.d.ts.map
|
|
@@ -9,11 +9,44 @@ const validateBody = async (schema, data) => {
|
|
|
9
9
|
}
|
|
10
10
|
catch (error) {
|
|
11
11
|
if (error instanceof zod_1.z.ZodError) {
|
|
12
|
-
throw new errors_1.ValidationError('Validation error', error.
|
|
12
|
+
throw new errors_1.ValidationError('Validation error', error.issues);
|
|
13
13
|
}
|
|
14
14
|
throw error;
|
|
15
15
|
}
|
|
16
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* Body validation middleware using Zod schemas for runtime type checking.
|
|
19
|
+
* Validates the parsed request body against a provided Zod schema and sets
|
|
20
|
+
* the validated result in context.req.validatedBody.
|
|
21
|
+
*
|
|
22
|
+
* @template T - The expected type of the validated body data
|
|
23
|
+
* @implements {BaseMiddleware}
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* Simple user creation with type safety:
|
|
27
|
+
* ```typescript
|
|
28
|
+
* import { z } from 'zod';
|
|
29
|
+
* import { Handler, BodyValidationMiddleware } from '@noony-serverless/core';
|
|
30
|
+
*
|
|
31
|
+
* const userSchema = z.object({
|
|
32
|
+
* name: z.string().min(1),
|
|
33
|
+
* email: z.string().email(),
|
|
34
|
+
* age: z.number().min(18)
|
|
35
|
+
* });
|
|
36
|
+
*
|
|
37
|
+
* type UserRequest = z.infer<typeof userSchema>;
|
|
38
|
+
*
|
|
39
|
+
* async function handleCreateUser(context: Context<UserRequest>) {
|
|
40
|
+
* const user = context.req.validatedBody!; // Fully typed
|
|
41
|
+
* const authenticatedUser = context.user; // User type inferred from auth middleware
|
|
42
|
+
* return { success: true, user: { id: '123', ...user } };
|
|
43
|
+
* }
|
|
44
|
+
*
|
|
45
|
+
* const createUserHandler = new Handler<UserRequest>()
|
|
46
|
+
* .use(new BodyValidationMiddleware<UserRequest>(userSchema))
|
|
47
|
+
* .handle(handleCreateUser);
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
17
50
|
class BodyValidationMiddleware {
|
|
18
51
|
schema;
|
|
19
52
|
constructor(schema) {
|
|
@@ -24,7 +57,40 @@ class BodyValidationMiddleware {
|
|
|
24
57
|
}
|
|
25
58
|
}
|
|
26
59
|
exports.BodyValidationMiddleware = BodyValidationMiddleware;
|
|
27
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Factory function that creates a body validation middleware with Zod schema validation.
|
|
62
|
+
* This function validates and parses the request body, setting the result in context.req.parsedBody.
|
|
63
|
+
*
|
|
64
|
+
* @template T - The expected type of the validated body data
|
|
65
|
+
* @param schema - Zod schema to validate against
|
|
66
|
+
* @returns A BaseMiddleware object with validation logic
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* Simple login validation:
|
|
70
|
+
* ```typescript
|
|
71
|
+
* import { z } from 'zod';
|
|
72
|
+
* import { Handler, bodyValidatorMiddleware } from '@noony-serverless/core';
|
|
73
|
+
*
|
|
74
|
+
* const loginSchema = z.object({
|
|
75
|
+
* username: z.string().min(3),
|
|
76
|
+
* password: z.string().min(8)
|
|
77
|
+
* });
|
|
78
|
+
*
|
|
79
|
+
* type LoginRequest = z.infer<typeof loginSchema>;
|
|
80
|
+
*
|
|
81
|
+
* async function handleLogin(context: Context<LoginRequest>) {
|
|
82
|
+
* const credentials = context.req.parsedBody as LoginRequest;
|
|
83
|
+
* const token = await authenticate(credentials.username, credentials.password);
|
|
84
|
+
* const authenticatedUser = context.user; // User type from auth middleware
|
|
85
|
+
* return { success: true, token };
|
|
86
|
+
* }
|
|
87
|
+
*
|
|
88
|
+
* const loginHandler = new Handler<LoginRequest>()
|
|
89
|
+
* .use(bodyValidatorMiddleware<LoginRequest>(loginSchema))
|
|
90
|
+
* .handle(handleLogin);
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
// Simplified factory function for body validation middleware
|
|
28
94
|
const bodyValidatorMiddleware = (schema) => ({
|
|
29
95
|
before: async (context) => {
|
|
30
96
|
context.req.parsedBody = await validateBody(schema, context.req.body);
|