@noony-serverless/core 0.2.2 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/build/core/containerPool.d.ts +44 -0
- package/build/core/containerPool.js +100 -0
- package/build/core/core.d.ts +68 -17
- package/build/core/core.js +63 -2
- package/build/core/errors.d.ts +43 -0
- package/build/core/errors.js +74 -1
- package/build/core/handler.d.ts +16 -37
- package/build/core/handler.js +42 -131
- package/build/core/index.d.ts +1 -0
- package/build/core/index.js +1 -0
- package/build/index.d.ts +1 -0
- package/build/index.js +4 -0
- package/build/middlewares/authenticationMiddleware.d.ts +8 -6
- package/build/middlewares/authenticationMiddleware.js +4 -2
- package/build/middlewares/bodyParserMiddleware.d.ts +7 -5
- package/build/middlewares/bodyParserMiddleware.js +4 -2
- package/build/middlewares/bodyValidationMiddleware.d.ts +10 -12
- package/build/middlewares/bodyValidationMiddleware.js +7 -9
- package/build/middlewares/errorHandlerMiddleware.d.ts +8 -3
- package/build/middlewares/errorHandlerMiddleware.js +7 -0
- package/build/middlewares/guards/RouteGuards.d.ts +2 -2
- package/build/middlewares/guards/RouteGuards.js +2 -2
- 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/PermissionGuardFactory.d.ts +10 -2
- package/build/middlewares/guards/guards/PermissionGuardFactory.js +1 -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 +34 -10
- package/build/middlewares/index.d.ts +1 -3
- package/build/middlewares/index.js +1 -6
- package/build/middlewares/queryParametersMiddleware.d.ts +7 -3
- package/build/middlewares/queryParametersMiddleware.js +4 -0
- package/build/middlewares/responseWrapperMiddleware.d.ts +10 -4
- package/build/middlewares/responseWrapperMiddleware.js +6 -0
- package/build/middlewares/validationMiddleware.d.ts +154 -0
- package/build/middlewares/validationMiddleware.js +185 -0
- package/package.json +1 -3
|
@@ -412,7 +412,7 @@ class ExpressionPermissionResolver extends PermissionResolver_1.PermissionResolv
|
|
|
412
412
|
* Check if this resolver can handle the given requirement type
|
|
413
413
|
*/
|
|
414
414
|
canHandle(requirement) {
|
|
415
|
-
return
|
|
415
|
+
return (requirement &&
|
|
416
416
|
typeof requirement === 'object' &&
|
|
417
417
|
PermissionResolver_1.PermissionUtils.isValidExpression(requirement));
|
|
418
418
|
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* different types of permission requirements. The generic type T represents
|
|
20
20
|
* the specific requirement format for each resolver.
|
|
21
21
|
*/
|
|
22
|
-
export declare abstract class PermissionResolver<T =
|
|
22
|
+
export declare abstract class PermissionResolver<T = any> {
|
|
23
23
|
/**
|
|
24
24
|
* Check if user permissions satisfy the requirement
|
|
25
25
|
*
|
|
@@ -96,6 +96,6 @@ export declare class PlainPermissionResolver extends PermissionResolver<string[]
|
|
|
96
96
|
* @param requirement - The requirement to check
|
|
97
97
|
* @returns true if this resolver can handle the requirement
|
|
98
98
|
*/
|
|
99
|
-
canHandle(requirement:
|
|
99
|
+
canHandle(requirement: any): requirement is string[];
|
|
100
100
|
}
|
|
101
101
|
//# sourceMappingURL=PlainPermissionResolver.d.ts.map
|
|
@@ -141,6 +141,6 @@ export declare class WildcardPermissionResolver extends PermissionResolver<strin
|
|
|
141
141
|
/**
|
|
142
142
|
* Check if this resolver can handle the given requirement type
|
|
143
143
|
*/
|
|
144
|
-
canHandle(requirement:
|
|
144
|
+
canHandle(requirement: any): requirement is string[];
|
|
145
145
|
}
|
|
146
146
|
//# sourceMappingURL=WildcardPermissionResolver.d.ts.map
|
|
@@ -23,13 +23,14 @@
|
|
|
23
23
|
* @version 1.0.0
|
|
24
24
|
*/
|
|
25
25
|
import { CacheAdapter } from '../cache/CacheAdapter';
|
|
26
|
-
import { GuardConfiguration } from '../config/GuardConfiguration';
|
|
26
|
+
import { GuardConfiguration, PermissionResolutionStrategy } from '../config/GuardConfiguration';
|
|
27
27
|
import { PermissionRegistry } from '../registry/PermissionRegistry';
|
|
28
28
|
import { PermissionResolverType, PermissionCheckResult, PermissionExpression } from '../resolvers/PermissionResolver';
|
|
29
29
|
/**
|
|
30
30
|
* Type alias for permission requirements that can be strings, string arrays, or complex expressions
|
|
31
|
+
* Note: Currently defined but reserved for future use
|
|
31
32
|
*/
|
|
32
|
-
type PermissionRequirement = string | string[] | PermissionExpression | Record<string, unknown>;
|
|
33
|
+
export type PermissionRequirement = string | string[] | PermissionExpression | Record<string, unknown>;
|
|
33
34
|
/**
|
|
34
35
|
* User context with cached permissions and metadata
|
|
35
36
|
*/
|
|
@@ -37,7 +38,7 @@ export interface UserContext {
|
|
|
37
38
|
userId: string;
|
|
38
39
|
permissions: Set<string>;
|
|
39
40
|
roles: string[];
|
|
40
|
-
metadata: Record<string,
|
|
41
|
+
metadata: Record<string, any>;
|
|
41
42
|
expandedPermissions?: Set<string>;
|
|
42
43
|
lastUpdated: string;
|
|
43
44
|
expiresAt?: string;
|
|
@@ -52,7 +53,7 @@ export interface UserPermissionSource {
|
|
|
52
53
|
getUserPermissions(userId: string): Promise<{
|
|
53
54
|
permissions: string[];
|
|
54
55
|
roles: string[];
|
|
55
|
-
metadata?: Record<string,
|
|
56
|
+
metadata?: Record<string, any>;
|
|
56
57
|
} | null>;
|
|
57
58
|
/**
|
|
58
59
|
* Get role-based permissions for expansion
|
|
@@ -117,7 +118,7 @@ export declare class FastUserContextService {
|
|
|
117
118
|
* @param options - Check options
|
|
118
119
|
* @returns Detailed permission check result
|
|
119
120
|
*/
|
|
120
|
-
checkPermission(userId: string, requirement:
|
|
121
|
+
checkPermission(userId: string, requirement: any, options?: PermissionCheckOptions): Promise<PermissionCheckResult>;
|
|
121
122
|
/**
|
|
122
123
|
* Batch check multiple permissions for a user
|
|
123
124
|
*
|
|
@@ -130,7 +131,7 @@ export declare class FastUserContextService {
|
|
|
130
131
|
* @returns Array of permission check results
|
|
131
132
|
*/
|
|
132
133
|
checkPermissions(userId: string, requirements: Array<{
|
|
133
|
-
requirement:
|
|
134
|
+
requirement: any;
|
|
134
135
|
resolverType?: PermissionResolverType;
|
|
135
136
|
}>, options?: PermissionCheckOptions): Promise<PermissionCheckResult[]>;
|
|
136
137
|
/**
|
|
@@ -165,9 +166,33 @@ export declare class FastUserContextService {
|
|
|
165
166
|
averageResolutionTimeUs: number;
|
|
166
167
|
totalResolutionTimeUs: number;
|
|
167
168
|
resolverStats: {
|
|
168
|
-
plain:
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
plain: {
|
|
170
|
+
checkCount: number;
|
|
171
|
+
averageResolutionTimeUs: number;
|
|
172
|
+
totalResolutionTimeUs: number;
|
|
173
|
+
};
|
|
174
|
+
wildcard: {
|
|
175
|
+
strategy: PermissionResolutionStrategy;
|
|
176
|
+
checkCount: number;
|
|
177
|
+
averageResolutionTimeUs: number;
|
|
178
|
+
totalResolutionTimeUs: number;
|
|
179
|
+
cacheHitRate: number;
|
|
180
|
+
cacheHits: number;
|
|
181
|
+
cacheMisses: number;
|
|
182
|
+
};
|
|
183
|
+
expression: {
|
|
184
|
+
checkCount: number;
|
|
185
|
+
averageResolutionTimeUs: number;
|
|
186
|
+
totalResolutionTimeUs: number;
|
|
187
|
+
cacheHitRate: number;
|
|
188
|
+
cacheHits: number;
|
|
189
|
+
cacheMisses: number;
|
|
190
|
+
complexityDistribution: {
|
|
191
|
+
simple: number;
|
|
192
|
+
moderate: number;
|
|
193
|
+
complex: number;
|
|
194
|
+
};
|
|
195
|
+
};
|
|
171
196
|
};
|
|
172
197
|
};
|
|
173
198
|
/**
|
|
@@ -199,5 +224,4 @@ export declare class FastUserContextService {
|
|
|
199
224
|
*/
|
|
200
225
|
private recordAuditTrail;
|
|
201
226
|
}
|
|
202
|
-
export {};
|
|
203
227
|
//# sourceMappingURL=FastUserContextService.d.ts.map
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
export * from './SecurityMiddleware';
|
|
2
|
-
export * from './ConsolidatedValidationMiddleware';
|
|
3
|
-
export * from './ProcessingMiddleware';
|
|
4
1
|
export * from './authenticationMiddleware';
|
|
5
2
|
export * from './bodyParserMiddleware';
|
|
6
3
|
export * from './bodyValidationMiddleware';
|
|
@@ -13,5 +10,6 @@ export * from './rateLimitingMiddleware';
|
|
|
13
10
|
export * from './responseWrapperMiddleware';
|
|
14
11
|
export * from './securityAuditMiddleware';
|
|
15
12
|
export * from './securityHeadersMiddleware';
|
|
13
|
+
export * from './validationMiddleware';
|
|
16
14
|
export * from './guards';
|
|
17
15
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -14,11 +14,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
// === NEW CONSOLIDATED MIDDLEWARES ===
|
|
18
|
-
__exportStar(require("./SecurityMiddleware"), exports);
|
|
19
|
-
__exportStar(require("./ConsolidatedValidationMiddleware"), exports);
|
|
20
|
-
__exportStar(require("./ProcessingMiddleware"), exports);
|
|
21
|
-
// === EXISTING INDIVIDUAL MIDDLEWARES (for backward compatibility) ===
|
|
22
17
|
__exportStar(require("./authenticationMiddleware"), exports);
|
|
23
18
|
__exportStar(require("./bodyParserMiddleware"), exports);
|
|
24
19
|
__exportStar(require("./bodyValidationMiddleware"), exports);
|
|
@@ -31,6 +26,6 @@ __exportStar(require("./rateLimitingMiddleware"), exports);
|
|
|
31
26
|
__exportStar(require("./responseWrapperMiddleware"), exports);
|
|
32
27
|
__exportStar(require("./securityAuditMiddleware"), exports);
|
|
33
28
|
__exportStar(require("./securityHeadersMiddleware"), exports);
|
|
34
|
-
|
|
29
|
+
__exportStar(require("./validationMiddleware"), exports);
|
|
35
30
|
__exportStar(require("./guards"), exports);
|
|
36
31
|
//# sourceMappingURL=index.js.map
|
|
@@ -3,6 +3,8 @@ import { BaseMiddleware, Context } from '../core';
|
|
|
3
3
|
* Middleware class that validates and processes query parameters from the request URL.
|
|
4
4
|
* Extracts query parameters and validates that required parameters are present.
|
|
5
5
|
*
|
|
6
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
7
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
6
8
|
* @implements {BaseMiddleware}
|
|
7
9
|
*
|
|
8
10
|
* @example
|
|
@@ -43,15 +45,17 @@ import { BaseMiddleware, Context } from '../core';
|
|
|
43
45
|
* });
|
|
44
46
|
* ```
|
|
45
47
|
*/
|
|
46
|
-
export declare class QueryParametersMiddleware implements BaseMiddleware {
|
|
48
|
+
export declare class QueryParametersMiddleware<TBody = unknown, TUser = unknown> implements BaseMiddleware<TBody, TUser> {
|
|
47
49
|
private readonly requiredParams;
|
|
48
50
|
constructor(requiredParams?: string[]);
|
|
49
|
-
before(context: Context): Promise<void>;
|
|
51
|
+
before(context: Context<TBody, TUser>): Promise<void>;
|
|
50
52
|
}
|
|
51
53
|
/**
|
|
52
54
|
* Factory function that creates a query parameter processing middleware.
|
|
53
55
|
* Extracts and validates query parameters from the request URL.
|
|
54
56
|
*
|
|
57
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
58
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
55
59
|
* @param requiredParams - Array of parameter names that must be present (default: empty array)
|
|
56
60
|
* @returns BaseMiddleware object with query parameter processing logic
|
|
57
61
|
*
|
|
@@ -109,5 +113,5 @@ export declare class QueryParametersMiddleware implements BaseMiddleware {
|
|
|
109
113
|
* });
|
|
110
114
|
* ```
|
|
111
115
|
*/
|
|
112
|
-
export declare const queryParametersMiddleware: (requiredParams?: string[]) => BaseMiddleware
|
|
116
|
+
export declare const queryParametersMiddleware: <TBody = unknown, TUser = unknown>(requiredParams?: string[]) => BaseMiddleware<TBody, TUser>;
|
|
113
117
|
//# sourceMappingURL=queryParametersMiddleware.d.ts.map
|
|
@@ -24,6 +24,8 @@ const convertQueryToRecord = (query) => {
|
|
|
24
24
|
* Middleware class that validates and processes query parameters from the request URL.
|
|
25
25
|
* Extracts query parameters and validates that required parameters are present.
|
|
26
26
|
*
|
|
27
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
28
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
27
29
|
* @implements {BaseMiddleware}
|
|
28
30
|
*
|
|
29
31
|
* @example
|
|
@@ -84,6 +86,8 @@ exports.QueryParametersMiddleware = QueryParametersMiddleware;
|
|
|
84
86
|
* Factory function that creates a query parameter processing middleware.
|
|
85
87
|
* Extracts and validates query parameters from the request URL.
|
|
86
88
|
*
|
|
89
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
90
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
87
91
|
* @param requiredParams - Array of parameter names that must be present (default: empty array)
|
|
88
92
|
* @returns BaseMiddleware object with query parameter processing logic
|
|
89
93
|
*
|
|
@@ -5,6 +5,8 @@ import { Context } from '../core/core';
|
|
|
5
5
|
* Automatically wraps the response with success flag, payload, and timestamp.
|
|
6
6
|
*
|
|
7
7
|
* @template T - The type of response data being wrapped
|
|
8
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
9
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
8
10
|
* @implements {BaseMiddleware}
|
|
9
11
|
*
|
|
10
12
|
* @example
|
|
@@ -60,14 +62,16 @@ import { Context } from '../core/core';
|
|
|
60
62
|
* });
|
|
61
63
|
* ```
|
|
62
64
|
*/
|
|
63
|
-
export declare class ResponseWrapperMiddleware<T> implements BaseMiddleware {
|
|
64
|
-
after(context: Context): Promise<void>;
|
|
65
|
+
export declare class ResponseWrapperMiddleware<T = unknown, TBody = unknown, TUser = unknown> implements BaseMiddleware<TBody, TUser> {
|
|
66
|
+
after(context: Context<TBody, TUser>): Promise<void>;
|
|
65
67
|
}
|
|
66
68
|
/**
|
|
67
69
|
* Factory function that creates a response wrapper middleware.
|
|
68
70
|
* Automatically wraps response data in a standardized format with success flag and timestamp.
|
|
69
71
|
*
|
|
70
72
|
* @template T - The type of response data being wrapped
|
|
73
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
74
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
71
75
|
* @returns BaseMiddleware object with response wrapping logic
|
|
72
76
|
*
|
|
73
77
|
* @example
|
|
@@ -118,12 +122,14 @@ export declare class ResponseWrapperMiddleware<T> implements BaseMiddleware {
|
|
|
118
122
|
* });
|
|
119
123
|
* ```
|
|
120
124
|
*/
|
|
121
|
-
export declare const responseWrapperMiddleware: <T>() => BaseMiddleware
|
|
125
|
+
export declare const responseWrapperMiddleware: <T = unknown, TBody = unknown, TUser = unknown>() => BaseMiddleware<TBody, TUser>;
|
|
122
126
|
/**
|
|
123
127
|
* Helper function to set response data in context for later wrapping.
|
|
124
128
|
* This function should be used in handlers when using ResponseWrapperMiddleware.
|
|
125
129
|
*
|
|
126
130
|
* @template T - The type of data being set
|
|
131
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
132
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
127
133
|
* @param context - The request context
|
|
128
134
|
* @param data - The data to be included in the response payload
|
|
129
135
|
*
|
|
@@ -176,5 +182,5 @@ export declare const responseWrapperMiddleware: <T>() => BaseMiddleware;
|
|
|
176
182
|
* });
|
|
177
183
|
* ```
|
|
178
184
|
*/
|
|
179
|
-
export declare function setResponseData<T>(context: Context, data: T): void;
|
|
185
|
+
export declare function setResponseData<T, TBody = unknown, TUser = unknown>(context: Context<TBody, TUser>, data: T): void;
|
|
180
186
|
//# sourceMappingURL=responseWrapperMiddleware.d.ts.map
|
|
@@ -18,6 +18,8 @@ const wrapResponse = (context) => {
|
|
|
18
18
|
* Automatically wraps the response with success flag, payload, and timestamp.
|
|
19
19
|
*
|
|
20
20
|
* @template T - The type of response data being wrapped
|
|
21
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
22
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
21
23
|
* @implements {BaseMiddleware}
|
|
22
24
|
*
|
|
23
25
|
* @example
|
|
@@ -84,6 +86,8 @@ exports.ResponseWrapperMiddleware = ResponseWrapperMiddleware;
|
|
|
84
86
|
* Automatically wraps response data in a standardized format with success flag and timestamp.
|
|
85
87
|
*
|
|
86
88
|
* @template T - The type of response data being wrapped
|
|
89
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
90
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
87
91
|
* @returns BaseMiddleware object with response wrapping logic
|
|
88
92
|
*
|
|
89
93
|
* @example
|
|
@@ -145,6 +149,8 @@ exports.responseWrapperMiddleware = responseWrapperMiddleware;
|
|
|
145
149
|
* This function should be used in handlers when using ResponseWrapperMiddleware.
|
|
146
150
|
*
|
|
147
151
|
* @template T - The type of data being set
|
|
152
|
+
* @template TBody - The type of the request body payload (preserves type chain)
|
|
153
|
+
* @template TUser - The type of the authenticated user (preserves type chain)
|
|
148
154
|
* @param context - The request context
|
|
149
155
|
* @param data - The data to be included in the response payload
|
|
150
156
|
*
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { BaseMiddleware, Context } from '../core';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
/**
|
|
4
|
+
* Middleware class that validates request data (body or query parameters) using Zod schemas.
|
|
5
|
+
* Automatically detects GET requests and validates query parameters, or validates body for other methods.
|
|
6
|
+
*
|
|
7
|
+
* @implements {BaseMiddleware}
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* User registration validation:
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { z } from 'zod';
|
|
13
|
+
* import { Handler, ValidationMiddleware } from '@noony-serverless/core';
|
|
14
|
+
*
|
|
15
|
+
* const userRegistrationSchema = z.object({
|
|
16
|
+
* email: z.string().email(),
|
|
17
|
+
* password: z.string().min(8),
|
|
18
|
+
* firstName: z.string().min(1),
|
|
19
|
+
* lastName: z.string().min(1),
|
|
20
|
+
* age: z.number().int().min(18).max(120)
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* const registerHandler = new Handler()
|
|
24
|
+
* .use(bodyParser())
|
|
25
|
+
* .use(new ValidationMiddleware(userRegistrationSchema))
|
|
26
|
+
* .handle(async (context) => {
|
|
27
|
+
* const validatedUser = context.req.validatedBody;
|
|
28
|
+
* const newUser = await createUser(validatedUser);
|
|
29
|
+
* return { success: true, userId: newUser.id };
|
|
30
|
+
* });
|
|
31
|
+
* ```
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* GET request query parameter validation:
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const searchSchema = z.object({
|
|
37
|
+
* q: z.string().min(1),
|
|
38
|
+
* page: z.string().regex(/^\d+$/).transform(Number).default('1'),
|
|
39
|
+
* limit: z.string().regex(/^\d+$/).transform(Number).default('10'),
|
|
40
|
+
* category: z.string().optional()
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* const searchHandler = new Handler()
|
|
44
|
+
* .use(new ValidationMiddleware(searchSchema))
|
|
45
|
+
* .handle(async (context) => {
|
|
46
|
+
* const { q, page, limit, category } = context.req.query;
|
|
47
|
+
* const results = await searchItems(q, { page, limit, category });
|
|
48
|
+
* return { success: true, results, query: { q, page, limit, category } };
|
|
49
|
+
* });
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* Product creation with nested validation:
|
|
54
|
+
* ```typescript
|
|
55
|
+
* const productSchema = z.object({
|
|
56
|
+
* name: z.string().min(1).max(100),
|
|
57
|
+
* description: z.string().max(1000),
|
|
58
|
+
* price: z.number().positive(),
|
|
59
|
+
* category: z.enum(['electronics', 'clothing', 'books', 'home']),
|
|
60
|
+
* specifications: z.record(z.string()),
|
|
61
|
+
* images: z.array(z.string().url()).max(5),
|
|
62
|
+
* inventory: z.object({
|
|
63
|
+
* inStock: z.boolean(),
|
|
64
|
+
* quantity: z.number().int().min(0),
|
|
65
|
+
* warehouse: z.string()
|
|
66
|
+
* })
|
|
67
|
+
* });
|
|
68
|
+
*
|
|
69
|
+
* const createProductHandler = new Handler()
|
|
70
|
+
* .use(bodyParser())
|
|
71
|
+
* .use(new ValidationMiddleware(productSchema))
|
|
72
|
+
* .handle(async (context) => {
|
|
73
|
+
* const productData = context.req.validatedBody;
|
|
74
|
+
* const product = await createProduct(productData);
|
|
75
|
+
* return { success: true, productId: product.id };
|
|
76
|
+
* });
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export declare class ValidationMiddleware implements BaseMiddleware {
|
|
80
|
+
private readonly schema;
|
|
81
|
+
constructor(schema: z.ZodSchema);
|
|
82
|
+
before(context: Context): Promise<void>;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Factory function that creates a validation middleware using Zod schema.
|
|
86
|
+
* Automatically validates request body for non-GET requests or query parameters for GET requests.
|
|
87
|
+
*
|
|
88
|
+
* @param schema - Zod schema to validate against
|
|
89
|
+
* @returns BaseMiddleware object with validation logic
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* Login endpoint validation:
|
|
93
|
+
* ```typescript
|
|
94
|
+
* import { z } from 'zod';
|
|
95
|
+
* import { Handler, validationMiddleware } from '@noony-serverless/core';
|
|
96
|
+
*
|
|
97
|
+
* const loginSchema = z.object({
|
|
98
|
+
* email: z.string().email(),
|
|
99
|
+
* password: z.string().min(1),
|
|
100
|
+
* rememberMe: z.boolean().optional()
|
|
101
|
+
* });
|
|
102
|
+
*
|
|
103
|
+
* const loginHandler = new Handler()
|
|
104
|
+
* .use(bodyParser())
|
|
105
|
+
* .use(validationMiddleware(loginSchema))
|
|
106
|
+
* .handle(async (context) => {
|
|
107
|
+
* const { email, password, rememberMe } = context.req.validatedBody;
|
|
108
|
+
* const token = await authenticate(email, password);
|
|
109
|
+
* return { success: true, token, rememberMe };
|
|
110
|
+
* });
|
|
111
|
+
* ```
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* API filtering with query validation:
|
|
115
|
+
* ```typescript
|
|
116
|
+
* const filterSchema = z.object({
|
|
117
|
+
* status: z.enum(['active', 'inactive', 'pending']).optional(),
|
|
118
|
+
* sort: z.enum(['name', 'date', 'status']).default('name'),
|
|
119
|
+
* order: z.enum(['asc', 'desc']).default('asc'),
|
|
120
|
+
* limit: z.coerce.number().int().min(1).max(100).default(10)
|
|
121
|
+
* });
|
|
122
|
+
*
|
|
123
|
+
* const getItemsHandler = new Handler()
|
|
124
|
+
* .use(validationMiddleware(filterSchema))
|
|
125
|
+
* .handle(async (context) => {
|
|
126
|
+
* const filters = context.req.query;
|
|
127
|
+
* const items = await getFilteredItems(filters);
|
|
128
|
+
* return { success: true, items, appliedFilters: filters };
|
|
129
|
+
* });
|
|
130
|
+
* ```
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* File upload validation:
|
|
134
|
+
* ```typescript
|
|
135
|
+
* const uploadSchema = z.object({
|
|
136
|
+
* filename: z.string().min(1),
|
|
137
|
+
* mimeType: z.string().regex(/^(image|document)\//),
|
|
138
|
+
* size: z.number().max(10 * 1024 * 1024), // 10MB max
|
|
139
|
+
* description: z.string().max(200).optional(),
|
|
140
|
+
* tags: z.array(z.string()).max(10).optional()
|
|
141
|
+
* });
|
|
142
|
+
*
|
|
143
|
+
* const uploadHandler = new Handler()
|
|
144
|
+
* .use(bodyParser())
|
|
145
|
+
* .use(validationMiddleware(uploadSchema))
|
|
146
|
+
* .handle(async (context) => {
|
|
147
|
+
* const fileData = context.req.validatedBody;
|
|
148
|
+
* const upload = await processFileUpload(fileData);
|
|
149
|
+
* return { success: true, fileId: upload.id };
|
|
150
|
+
* });
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
export declare const validationMiddleware: (schema: z.ZodSchema) => BaseMiddleware;
|
|
154
|
+
//# sourceMappingURL=validationMiddleware.d.ts.map
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validationMiddleware = exports.ValidationMiddleware = void 0;
|
|
4
|
+
const core_1 = require("../core");
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
const validate = async (schema, context) => {
|
|
7
|
+
try {
|
|
8
|
+
const data = context.req.method === 'GET' ? context.req.query : context.req.parsedBody;
|
|
9
|
+
const validated = await schema.parseAsync(data);
|
|
10
|
+
if (context.req.method === 'GET') {
|
|
11
|
+
context.req.query = validated;
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
context.req.validatedBody = validated;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (error instanceof zod_1.z.ZodError) {
|
|
19
|
+
throw new core_1.ValidationError('Validation error', JSON.stringify(error.issues));
|
|
20
|
+
}
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Middleware class that validates request data (body or query parameters) using Zod schemas.
|
|
26
|
+
* Automatically detects GET requests and validates query parameters, or validates body for other methods.
|
|
27
|
+
*
|
|
28
|
+
* @implements {BaseMiddleware}
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* User registration validation:
|
|
32
|
+
* ```typescript
|
|
33
|
+
* import { z } from 'zod';
|
|
34
|
+
* import { Handler, ValidationMiddleware } from '@noony-serverless/core';
|
|
35
|
+
*
|
|
36
|
+
* const userRegistrationSchema = z.object({
|
|
37
|
+
* email: z.string().email(),
|
|
38
|
+
* password: z.string().min(8),
|
|
39
|
+
* firstName: z.string().min(1),
|
|
40
|
+
* lastName: z.string().min(1),
|
|
41
|
+
* age: z.number().int().min(18).max(120)
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* const registerHandler = new Handler()
|
|
45
|
+
* .use(bodyParser())
|
|
46
|
+
* .use(new ValidationMiddleware(userRegistrationSchema))
|
|
47
|
+
* .handle(async (context) => {
|
|
48
|
+
* const validatedUser = context.req.validatedBody;
|
|
49
|
+
* const newUser = await createUser(validatedUser);
|
|
50
|
+
* return { success: true, userId: newUser.id };
|
|
51
|
+
* });
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* GET request query parameter validation:
|
|
56
|
+
* ```typescript
|
|
57
|
+
* const searchSchema = z.object({
|
|
58
|
+
* q: z.string().min(1),
|
|
59
|
+
* page: z.string().regex(/^\d+$/).transform(Number).default('1'),
|
|
60
|
+
* limit: z.string().regex(/^\d+$/).transform(Number).default('10'),
|
|
61
|
+
* category: z.string().optional()
|
|
62
|
+
* });
|
|
63
|
+
*
|
|
64
|
+
* const searchHandler = new Handler()
|
|
65
|
+
* .use(new ValidationMiddleware(searchSchema))
|
|
66
|
+
* .handle(async (context) => {
|
|
67
|
+
* const { q, page, limit, category } = context.req.query;
|
|
68
|
+
* const results = await searchItems(q, { page, limit, category });
|
|
69
|
+
* return { success: true, results, query: { q, page, limit, category } };
|
|
70
|
+
* });
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* Product creation with nested validation:
|
|
75
|
+
* ```typescript
|
|
76
|
+
* const productSchema = z.object({
|
|
77
|
+
* name: z.string().min(1).max(100),
|
|
78
|
+
* description: z.string().max(1000),
|
|
79
|
+
* price: z.number().positive(),
|
|
80
|
+
* category: z.enum(['electronics', 'clothing', 'books', 'home']),
|
|
81
|
+
* specifications: z.record(z.string()),
|
|
82
|
+
* images: z.array(z.string().url()).max(5),
|
|
83
|
+
* inventory: z.object({
|
|
84
|
+
* inStock: z.boolean(),
|
|
85
|
+
* quantity: z.number().int().min(0),
|
|
86
|
+
* warehouse: z.string()
|
|
87
|
+
* })
|
|
88
|
+
* });
|
|
89
|
+
*
|
|
90
|
+
* const createProductHandler = new Handler()
|
|
91
|
+
* .use(bodyParser())
|
|
92
|
+
* .use(new ValidationMiddleware(productSchema))
|
|
93
|
+
* .handle(async (context) => {
|
|
94
|
+
* const productData = context.req.validatedBody;
|
|
95
|
+
* const product = await createProduct(productData);
|
|
96
|
+
* return { success: true, productId: product.id };
|
|
97
|
+
* });
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
class ValidationMiddleware {
|
|
101
|
+
schema;
|
|
102
|
+
constructor(schema) {
|
|
103
|
+
this.schema = schema;
|
|
104
|
+
}
|
|
105
|
+
async before(context) {
|
|
106
|
+
await validate(this.schema, context);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
exports.ValidationMiddleware = ValidationMiddleware;
|
|
110
|
+
/**
|
|
111
|
+
* Factory function that creates a validation middleware using Zod schema.
|
|
112
|
+
* Automatically validates request body for non-GET requests or query parameters for GET requests.
|
|
113
|
+
*
|
|
114
|
+
* @param schema - Zod schema to validate against
|
|
115
|
+
* @returns BaseMiddleware object with validation logic
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* Login endpoint validation:
|
|
119
|
+
* ```typescript
|
|
120
|
+
* import { z } from 'zod';
|
|
121
|
+
* import { Handler, validationMiddleware } from '@noony-serverless/core';
|
|
122
|
+
*
|
|
123
|
+
* const loginSchema = z.object({
|
|
124
|
+
* email: z.string().email(),
|
|
125
|
+
* password: z.string().min(1),
|
|
126
|
+
* rememberMe: z.boolean().optional()
|
|
127
|
+
* });
|
|
128
|
+
*
|
|
129
|
+
* const loginHandler = new Handler()
|
|
130
|
+
* .use(bodyParser())
|
|
131
|
+
* .use(validationMiddleware(loginSchema))
|
|
132
|
+
* .handle(async (context) => {
|
|
133
|
+
* const { email, password, rememberMe } = context.req.validatedBody;
|
|
134
|
+
* const token = await authenticate(email, password);
|
|
135
|
+
* return { success: true, token, rememberMe };
|
|
136
|
+
* });
|
|
137
|
+
* ```
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* API filtering with query validation:
|
|
141
|
+
* ```typescript
|
|
142
|
+
* const filterSchema = z.object({
|
|
143
|
+
* status: z.enum(['active', 'inactive', 'pending']).optional(),
|
|
144
|
+
* sort: z.enum(['name', 'date', 'status']).default('name'),
|
|
145
|
+
* order: z.enum(['asc', 'desc']).default('asc'),
|
|
146
|
+
* limit: z.coerce.number().int().min(1).max(100).default(10)
|
|
147
|
+
* });
|
|
148
|
+
*
|
|
149
|
+
* const getItemsHandler = new Handler()
|
|
150
|
+
* .use(validationMiddleware(filterSchema))
|
|
151
|
+
* .handle(async (context) => {
|
|
152
|
+
* const filters = context.req.query;
|
|
153
|
+
* const items = await getFilteredItems(filters);
|
|
154
|
+
* return { success: true, items, appliedFilters: filters };
|
|
155
|
+
* });
|
|
156
|
+
* ```
|
|
157
|
+
*
|
|
158
|
+
* @example
|
|
159
|
+
* File upload validation:
|
|
160
|
+
* ```typescript
|
|
161
|
+
* const uploadSchema = z.object({
|
|
162
|
+
* filename: z.string().min(1),
|
|
163
|
+
* mimeType: z.string().regex(/^(image|document)\//),
|
|
164
|
+
* size: z.number().max(10 * 1024 * 1024), // 10MB max
|
|
165
|
+
* description: z.string().max(200).optional(),
|
|
166
|
+
* tags: z.array(z.string()).max(10).optional()
|
|
167
|
+
* });
|
|
168
|
+
*
|
|
169
|
+
* const uploadHandler = new Handler()
|
|
170
|
+
* .use(bodyParser())
|
|
171
|
+
* .use(validationMiddleware(uploadSchema))
|
|
172
|
+
* .handle(async (context) => {
|
|
173
|
+
* const fileData = context.req.validatedBody;
|
|
174
|
+
* const upload = await processFileUpload(fileData);
|
|
175
|
+
* return { success: true, fileId: upload.id };
|
|
176
|
+
* });
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
const validationMiddleware = (schema) => ({
|
|
180
|
+
before: async (context) => {
|
|
181
|
+
await validate(schema, context);
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
exports.validationMiddleware = validationMiddleware;
|
|
185
|
+
//# sourceMappingURL=validationMiddleware.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noony-serverless/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
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",
|
|
@@ -43,13 +43,11 @@
|
|
|
43
43
|
"@google-cloud/functions-framework": "^4.0.0",
|
|
44
44
|
"@google-cloud/pubsub": "^4.1.0",
|
|
45
45
|
"@types/jsonwebtoken": "^9.0.10",
|
|
46
|
-
"@types/qs": "^6.14.0",
|
|
47
46
|
"axios": "^1.11.0",
|
|
48
47
|
"fastify": "^5.6.0",
|
|
49
48
|
"firebase-admin": "^13.5.0",
|
|
50
49
|
"firebase-functions": "^6.4.0",
|
|
51
50
|
"jsonwebtoken": "^9.0.2",
|
|
52
|
-
"qs": "^6.14.0",
|
|
53
51
|
"reflect-metadata": "^0.2.2",
|
|
54
52
|
"typedi": "^0.10.0",
|
|
55
53
|
"zod": "^4.1.5"
|