@noony-serverless/core 0.2.2 → 0.3.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.
Files changed (32) hide show
  1. package/README.md +7 -7
  2. package/build/core/containerPool.d.ts +44 -0
  3. package/build/core/containerPool.js +100 -0
  4. package/build/core/core.d.ts +68 -17
  5. package/build/core/core.js +63 -2
  6. package/build/core/errors.d.ts +43 -0
  7. package/build/core/errors.js +74 -1
  8. package/build/core/handler.d.ts +16 -37
  9. package/build/core/handler.js +42 -131
  10. package/build/core/index.d.ts +1 -0
  11. package/build/core/index.js +1 -0
  12. package/build/index.d.ts +1 -0
  13. package/build/index.js +4 -0
  14. package/build/middlewares/bodyValidationMiddleware.d.ts +10 -12
  15. package/build/middlewares/bodyValidationMiddleware.js +7 -9
  16. package/build/middlewares/guards/RouteGuards.d.ts +2 -2
  17. package/build/middlewares/guards/RouteGuards.js +2 -2
  18. package/build/middlewares/guards/adapters/CustomTokenVerificationPortAdapter.d.ts +1 -1
  19. package/build/middlewares/guards/guards/FastAuthGuard.d.ts +5 -5
  20. package/build/middlewares/guards/guards/PermissionGuardFactory.d.ts +10 -2
  21. package/build/middlewares/guards/guards/PermissionGuardFactory.js +1 -1
  22. package/build/middlewares/guards/resolvers/ExpressionPermissionResolver.d.ts +1 -1
  23. package/build/middlewares/guards/resolvers/ExpressionPermissionResolver.js +1 -1
  24. package/build/middlewares/guards/resolvers/PermissionResolver.d.ts +1 -1
  25. package/build/middlewares/guards/resolvers/PlainPermissionResolver.d.ts +1 -1
  26. package/build/middlewares/guards/resolvers/WildcardPermissionResolver.d.ts +1 -1
  27. package/build/middlewares/guards/services/FastUserContextService.d.ts +34 -10
  28. package/build/middlewares/index.d.ts +1 -3
  29. package/build/middlewares/index.js +1 -6
  30. package/build/middlewares/validationMiddleware.d.ts +154 -0
  31. package/build/middlewares/validationMiddleware.js +185 -0
  32. package/package.json +1 -3
package/README.md CHANGED
@@ -9,7 +9,7 @@ A powerful and flexible serverless middleware framework for Google Cloud Functio
9
9
  The `Handler` class manages the middleware execution pipeline with `before`, `after`, and `onError` lifecycle hooks:
10
10
 
11
11
  ```typescript
12
- const handler = new Handler<RequestType>()
12
+ const handler = new Handler<RequestType, UserType>()
13
13
  .use(errorHandler())
14
14
  .use(bodyParser())
15
15
  .use(bodyValidator(schema))
@@ -23,7 +23,7 @@ const handler = new Handler<RequestType>()
23
23
  The context system provides full TypeScript support with generic typing:
24
24
 
25
25
  ```typescript
26
- interface Context<T = unknown> {
26
+ interface Context<T = unknown, U = unknown> {
27
27
  req: CustomRequest<T>; // Request with parsedBody and validatedBody
28
28
  res: CustomResponse; // Response object
29
29
  container?: Container; // TypeDI dependency injection
@@ -72,7 +72,7 @@ const userSchema = z.object({
72
72
  type UserRequest = z.infer<typeof userSchema>;
73
73
 
74
74
  // Create handler with full type safety
75
- const createUserHandler = new Handler<UserRequest>()
75
+ const createUserHandler = new Handler<UserRequest, unknown>()
76
76
  .use(new ErrorHandlerMiddleware())
77
77
  .use(new BodyValidationMiddleware(userSchema))
78
78
  .use(new ResponseWrapperMiddleware())
@@ -117,7 +117,7 @@ const messageSchema = z.object({
117
117
  type PubSubMessage = z.infer<typeof messageSchema>;
118
118
 
119
119
  // Create Pub/Sub handler
120
- const pubsubHandler = new Handler<PubSubMessage>()
120
+ const pubsubHandler = new Handler<PubSubMessage, unknown>()
121
121
  .use(new ErrorHandlerMiddleware())
122
122
  .use(new BodyParserMiddleware()) // Decodes base64 Pub/Sub messages
123
123
  .use(new BodyValidationMiddleware(messageSchema))
@@ -296,7 +296,7 @@ app.post('/users', async (req, res) => {
296
296
  ### 1. Middleware Order
297
297
 
298
298
  ```typescript
299
- const handler = new Handler<RequestType>()
299
+ const handler = new Handler<RequestType, UserType>()
300
300
  .use(new ErrorHandlerMiddleware()) // Always first
301
301
  .use(new HeaderVariablesMiddleware(...)) // Required headers
302
302
  .use(new AuthenticationMiddleware(...)) // Authentication
@@ -324,7 +324,7 @@ interface UserContext {
324
324
  }
325
325
 
326
326
  // Use throughout the handler
327
- const handler = new Handler<UserRequest>();
327
+ const handler = new Handler<UserRequest, UserContext>();
328
328
  ```
329
329
 
330
330
  ### 3. Error Handling
@@ -361,7 +361,7 @@ import {
361
361
  } from '@noony/serverless';
362
362
 
363
363
  // No type casting needed with proper generics
364
- const handler = new Handler<UserRequest>()
364
+ const handler = new Handler<UserRequest, UserContext>()
365
365
  .handle(async (context) => {
366
366
  // TypeScript knows validatedBody is UserRequest
367
367
  const { name, email } = context.req.validatedBody!;
@@ -0,0 +1,44 @@
1
+ import { ContainerInstance } from 'typedi';
2
+ /**
3
+ * Performance optimization: Container Pool for reusing TypeDI containers
4
+ * This reduces object creation overhead and improves memory efficiency
5
+ */
6
+ declare class ContainerPool {
7
+ private availableContainers;
8
+ private maxPoolSize;
9
+ private createdContainers;
10
+ constructor(maxPoolSize?: number);
11
+ /**
12
+ * Get a container from the pool or create a new one
13
+ */
14
+ acquire(): ContainerInstance;
15
+ /**
16
+ * Return a container to the pool for reuse
17
+ */
18
+ release(container: ContainerInstance): void;
19
+ /**
20
+ * Reset container state to prevent cross-request contamination
21
+ * Note: TypeDI containers are isolated by default, so we mainly need
22
+ * to clear any manually set values
23
+ */
24
+ private resetContainer;
25
+ /**
26
+ * Get pool statistics for monitoring
27
+ */
28
+ getStats(): {
29
+ available: number;
30
+ created: number;
31
+ maxSize: number;
32
+ };
33
+ /**
34
+ * Warm up the pool by pre-creating containers
35
+ */
36
+ warmUp(count?: number): void;
37
+ /**
38
+ * Clear all containers from the pool
39
+ */
40
+ clear(): void;
41
+ }
42
+ declare const containerPool: ContainerPool;
43
+ export { ContainerPool, containerPool };
44
+ //# sourceMappingURL=containerPool.d.ts.map
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.containerPool = exports.ContainerPool = void 0;
4
+ const typedi_1 = require("typedi");
5
+ /**
6
+ * Performance optimization: Container Pool for reusing TypeDI containers
7
+ * This reduces object creation overhead and improves memory efficiency
8
+ */
9
+ class ContainerPool {
10
+ availableContainers = [];
11
+ maxPoolSize = 10;
12
+ createdContainers = 0;
13
+ constructor(maxPoolSize = 10) {
14
+ this.maxPoolSize = maxPoolSize;
15
+ }
16
+ /**
17
+ * Get a container from the pool or create a new one
18
+ */
19
+ acquire() {
20
+ if (this.availableContainers.length > 0) {
21
+ return this.availableContainers.pop();
22
+ }
23
+ // Create new container if pool is empty and under limit
24
+ if (this.createdContainers < this.maxPoolSize) {
25
+ this.createdContainers++;
26
+ return typedi_1.Container.of();
27
+ }
28
+ // If pool is at capacity, create a temporary container
29
+ // This should rarely happen in normal usage
30
+ return typedi_1.Container.of();
31
+ }
32
+ /**
33
+ * Return a container to the pool for reuse
34
+ */
35
+ release(container) {
36
+ if (this.availableContainers.length < this.maxPoolSize) {
37
+ // Reset container state by removing all instances
38
+ // This prevents memory leaks and cross-request contamination
39
+ this.resetContainer(container);
40
+ this.availableContainers.push(container);
41
+ }
42
+ // If pool is full, let the container be garbage collected
43
+ }
44
+ /**
45
+ * Reset container state to prevent cross-request contamination
46
+ * Note: TypeDI containers are isolated by default, so we mainly need
47
+ * to clear any manually set values
48
+ */
49
+ resetContainer(_container) {
50
+ try {
51
+ // For TypeDI containers created with Container.of(), each container
52
+ // is already isolated. We just need to ensure no memory leaks.
53
+ // The container will be garbage collected when released from pool
54
+ // if it contains too much data
55
+ // TypeDI containers are self-contained and don't need explicit reset
56
+ // This is a placeholder for future enhancements if needed
57
+ }
58
+ catch (error) {
59
+ // If any issues occur, don't add back to pool
60
+ console.warn('Failed to reset container, discarding:', error);
61
+ }
62
+ }
63
+ /**
64
+ * Get pool statistics for monitoring
65
+ */
66
+ getStats() {
67
+ return {
68
+ available: this.availableContainers.length,
69
+ created: this.createdContainers,
70
+ maxSize: this.maxPoolSize,
71
+ };
72
+ }
73
+ /**
74
+ * Warm up the pool by pre-creating containers
75
+ */
76
+ warmUp(count = 5) {
77
+ const warmUpCount = Math.min(count, this.maxPoolSize);
78
+ for (let i = 0; i < warmUpCount; i++) {
79
+ if (this.createdContainers < this.maxPoolSize) {
80
+ const container = typedi_1.Container.of();
81
+ this.createdContainers++;
82
+ this.availableContainers.push(container);
83
+ }
84
+ }
85
+ }
86
+ /**
87
+ * Clear all containers from the pool
88
+ */
89
+ clear() {
90
+ this.availableContainers = [];
91
+ this.createdContainers = 0;
92
+ }
93
+ }
94
+ exports.ContainerPool = ContainerPool;
95
+ // Global container pool instance
96
+ const containerPool = new ContainerPool(15); // Slightly higher limit for serverless
97
+ exports.containerPool = containerPool;
98
+ // Warm up the pool for better cold start performance
99
+ containerPool.warmUp(3);
100
+ //# sourceMappingURL=containerPool.js.map
@@ -1,4 +1,5 @@
1
- import { ContainerInstance } from 'typedi';
1
+ import { Request, Response } from '@google-cloud/functions-framework';
2
+ import { Container, ContainerInstance } from 'typedi';
2
3
  /**
3
4
  * Framework-agnostic HTTP method enum
4
5
  */
@@ -14,7 +15,7 @@ export declare enum HttpMethod {
14
15
  /**
15
16
  * Framework-agnostic request interface that can work with any HTTP framework
16
17
  */
17
- export interface NoonyRequest<T = unknown> {
18
+ export interface GenericRequest<T = unknown> {
18
19
  method: HttpMethod | string;
19
20
  url: string;
20
21
  path?: string;
@@ -31,20 +32,30 @@ export interface NoonyRequest<T = unknown> {
31
32
  /**
32
33
  * Framework-agnostic response interface that can work with any HTTP framework
33
34
  */
34
- export interface NoonyResponse {
35
- status(code: number): NoonyResponse;
36
- json(data: unknown): NoonyResponse | void;
37
- send(data: unknown): NoonyResponse | void;
38
- header(name: string, value: string): NoonyResponse;
39
- headers(headers: Record<string, string>): NoonyResponse;
35
+ export interface GenericResponse {
36
+ status(code: number): GenericResponse;
37
+ json(data: unknown): GenericResponse | void;
38
+ send(data: unknown): GenericResponse | void;
39
+ header(name: string, value: string): GenericResponse;
40
+ headers(headers: Record<string, string>): GenericResponse;
40
41
  end(): void;
41
42
  statusCode?: number;
42
43
  headersSent?: boolean;
43
44
  }
44
- /** @deprecated Use NoonyRequest instead */
45
- export type GenericRequest<T = unknown> = NoonyRequest<T>;
46
- /** @deprecated Use NoonyResponse instead */
47
- export type GenericResponse = NoonyResponse;
45
+ /**
46
+ * Legacy GCP Functions-specific request interface for backward compatibility
47
+ * @deprecated Use GenericRequest instead
48
+ */
49
+ export interface CustomRequest<T = unknown> extends Request {
50
+ parsedBody?: T;
51
+ validatedBody?: T;
52
+ }
53
+ /**
54
+ * Legacy GCP Functions-specific response interface for backward compatibility
55
+ * @deprecated Use GenericResponse instead
56
+ */
57
+ export interface CustomResponse extends Response {
58
+ }
48
59
  /**
49
60
  * Security configuration for request processing
50
61
  */
@@ -63,29 +74,69 @@ export interface HandlerOptions {
63
74
  security?: SecurityConfig;
64
75
  enableAsyncContext?: boolean;
65
76
  }
77
+ /**
78
+ * Base authenticated user interface that projects can extend
79
+ */
80
+ export interface BaseAuthenticatedUser {
81
+ id: string;
82
+ email?: string;
83
+ name?: string;
84
+ [key: string]: unknown;
85
+ }
86
+ /**
87
+ * Modern request type alias (recommended)
88
+ */
89
+ export type NoonyRequest<T = unknown> = GenericRequest<T>;
90
+ /**
91
+ * Modern response type alias (recommended)
92
+ */
93
+ export type NoonyResponse = GenericResponse;
66
94
  /**
67
95
  * Represents the execution context for handling a request and response in an application.
68
96
  *
69
- * @template T Specifies the type of the custom request payload.
97
+ * @template TBody Specifies the type of the request body payload.
98
+ * @template TUser Specifies the type of the authenticated user (defaults to unknown).
70
99
  */
71
- export interface Context<T = unknown> {
72
- readonly req: NoonyRequest<T>;
100
+ export interface Context<TBody = unknown, TUser = unknown> {
101
+ readonly req: NoonyRequest<TBody>;
73
102
  readonly res: NoonyResponse;
74
103
  container: ContainerInstance;
75
104
  error?: Error | null;
76
105
  readonly businessData: Map<string, unknown>;
77
- user?: unknown;
106
+ user?: TUser;
78
107
  readonly startTime: number;
79
108
  readonly requestId: string;
80
109
  timeoutSignal?: AbortSignal;
81
110
  responseData?: unknown;
82
111
  }
112
+ /**
113
+ * Legacy context interface for backward compatibility
114
+ * @deprecated Use Context with GenericRequest/GenericResponse instead
115
+ */
116
+ export interface LegacyContext<T = unknown, V = unknown> {
117
+ req: CustomRequest<T>;
118
+ res: CustomResponse;
119
+ container?: Container;
120
+ error?: Error | null;
121
+ businessData: Map<string, unknown>;
122
+ user?: V;
123
+ }
83
124
  /**
84
125
  * Utility function to generate unique request IDs
85
126
  */
86
127
  export declare function generateRequestId(): string;
128
+ /**
129
+ * Adapter to convert GCP Functions Request to GenericRequest
130
+ */
131
+ export declare function adaptGCPRequest<T = unknown>(gcpRequest: Request): GenericRequest<T>;
132
+ /**
133
+ * Adapter to convert GCP Functions Response to GenericResponse
134
+ */
135
+ export declare function adaptGCPResponse(gcpResponse: Response): GenericResponse;
87
136
  /**
88
137
  * Creates a context object for framework-agnostic handlers
138
+ * @template TBody The type of the request body
139
+ * @template TUser The type of the authenticated user
89
140
  */
90
- export declare function createContext<T = unknown>(req: NoonyRequest<T>, res: NoonyResponse, options?: Partial<Context<T>>): Context<T>;
141
+ export declare function createContext<TBody = unknown, TUser = unknown>(req: NoonyRequest<TBody>, res: NoonyResponse, options?: Partial<Context<TBody, TUser>>): Context<TBody, TUser>;
91
142
  //# sourceMappingURL=core.d.ts.map
@@ -2,6 +2,8 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.HttpMethod = void 0;
4
4
  exports.generateRequestId = generateRequestId;
5
+ exports.adaptGCPRequest = adaptGCPRequest;
6
+ exports.adaptGCPResponse = adaptGCPResponse;
5
7
  exports.createContext = createContext;
6
8
  const typedi_1 = require("typedi");
7
9
  /**
@@ -17,16 +19,75 @@ var HttpMethod;
17
19
  HttpMethod["OPTIONS"] = "OPTIONS";
18
20
  HttpMethod["HEAD"] = "HEAD";
19
21
  })(HttpMethod || (exports.HttpMethod = HttpMethod = {}));
20
- // Legacy context interface removed - use Context with NoonyRequest/NoonyResponse instead
21
22
  /**
22
23
  * Utility function to generate unique request IDs
23
24
  */
24
25
  function generateRequestId() {
25
26
  return `req_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
26
27
  }
27
- // Complex adapter functions removed - using smart universal adapter in Handler instead
28
+ /**
29
+ * Adapter to convert GCP Functions Request to GenericRequest
30
+ */
31
+ function adaptGCPRequest(gcpRequest) {
32
+ return {
33
+ method: gcpRequest.method || HttpMethod.GET,
34
+ url: gcpRequest.url || '/',
35
+ path: gcpRequest.path,
36
+ headers: gcpRequest.headers || {},
37
+ query: gcpRequest.query || {},
38
+ params: gcpRequest.params || {},
39
+ body: gcpRequest.body,
40
+ rawBody: gcpRequest.rawBody,
41
+ ip: gcpRequest.ip,
42
+ userAgent: gcpRequest.get?.('user-agent'),
43
+ };
44
+ }
45
+ /**
46
+ * Adapter to convert GCP Functions Response to GenericResponse
47
+ */
48
+ function adaptGCPResponse(gcpResponse) {
49
+ let currentStatusCode = 200;
50
+ let isHeadersSent = false;
51
+ return {
52
+ status: (code) => {
53
+ currentStatusCode = code;
54
+ gcpResponse.status(code);
55
+ return adaptGCPResponse(gcpResponse);
56
+ },
57
+ json: (data) => {
58
+ isHeadersSent = true;
59
+ gcpResponse.json(data);
60
+ },
61
+ send: (data) => {
62
+ isHeadersSent = true;
63
+ gcpResponse.send(data);
64
+ },
65
+ header: (name, value) => {
66
+ gcpResponse.header(name, value);
67
+ return adaptGCPResponse(gcpResponse);
68
+ },
69
+ headers: (headers) => {
70
+ Object.entries(headers).forEach(([key, value]) => {
71
+ gcpResponse.header(key, value);
72
+ });
73
+ return adaptGCPResponse(gcpResponse);
74
+ },
75
+ end: () => {
76
+ isHeadersSent = true;
77
+ gcpResponse.end();
78
+ },
79
+ get statusCode() {
80
+ return gcpResponse.statusCode || currentStatusCode;
81
+ },
82
+ get headersSent() {
83
+ return gcpResponse.headersSent || isHeadersSent;
84
+ },
85
+ };
86
+ }
28
87
  /**
29
88
  * Creates a context object for framework-agnostic handlers
89
+ * @template TBody The type of the request body
90
+ * @template TUser The type of the authenticated user
30
91
  */
31
92
  function createContext(req, res, options = {}) {
32
93
  return {
@@ -22,4 +22,47 @@ export declare class TimeoutError extends HttpError {
22
22
  export declare class TooLargeError extends HttpError {
23
23
  constructor(message?: string, details?: unknown);
24
24
  }
25
+ /**
26
+ * 401 Unauthorized - Authentication required
27
+ * Alias for AuthenticationError for better semantic clarity
28
+ */
29
+ export declare class UnauthorizedError extends HttpError {
30
+ constructor(message?: string);
31
+ }
32
+ /**
33
+ * 403 Forbidden - Insufficient permissions
34
+ * Use this for authorization failures (user is authenticated but lacks permission)
35
+ */
36
+ export declare class ForbiddenError extends HttpError {
37
+ constructor(message?: string, details?: unknown);
38
+ }
39
+ /**
40
+ * 404 Not Found - Resource not found
41
+ */
42
+ export declare class NotFoundError extends HttpError {
43
+ constructor(message?: string, details?: unknown);
44
+ }
45
+ /**
46
+ * 409 Conflict - Resource already exists or state conflict
47
+ */
48
+ export declare class ConflictError extends HttpError {
49
+ constructor(message?: string, details?: unknown);
50
+ }
51
+ /**
52
+ * 500 Internal Server Error - Unexpected errors with optional cause chaining
53
+ */
54
+ export declare class InternalServerError extends HttpError {
55
+ cause?: Error | undefined;
56
+ constructor(message?: string, cause?: Error | undefined, details?: unknown);
57
+ }
58
+ /**
59
+ * Service layer error with error code
60
+ * Use this in service classes for business logic errors
61
+ * Not tied to specific HTTP status codes
62
+ */
63
+ export declare class ServiceError extends Error {
64
+ code: string;
65
+ details?: unknown | undefined;
66
+ constructor(message: string, code: string, details?: unknown | undefined);
67
+ }
25
68
  //# sourceMappingURL=errors.d.ts.map
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TooLargeError = exports.TimeoutError = exports.SecurityError = exports.BusinessError = exports.AuthenticationError = exports.ValidationError = exports.HttpError = void 0;
3
+ exports.ServiceError = exports.InternalServerError = exports.ConflictError = exports.NotFoundError = exports.ForbiddenError = exports.UnauthorizedError = exports.TooLargeError = exports.TimeoutError = exports.SecurityError = exports.BusinessError = exports.AuthenticationError = exports.ValidationError = exports.HttpError = void 0;
4
4
  class HttpError extends Error {
5
5
  status;
6
6
  code;
@@ -56,4 +56,77 @@ class TooLargeError extends HttpError {
56
56
  }
57
57
  }
58
58
  exports.TooLargeError = TooLargeError;
59
+ /**
60
+ * 401 Unauthorized - Authentication required
61
+ * Alias for AuthenticationError for better semantic clarity
62
+ */
63
+ class UnauthorizedError extends HttpError {
64
+ constructor(message = 'Authentication required') {
65
+ super(401, message, 'UNAUTHORIZED_ERROR');
66
+ this.name = 'UnauthorizedError';
67
+ }
68
+ }
69
+ exports.UnauthorizedError = UnauthorizedError;
70
+ /**
71
+ * 403 Forbidden - Insufficient permissions
72
+ * Use this for authorization failures (user is authenticated but lacks permission)
73
+ */
74
+ class ForbiddenError extends HttpError {
75
+ constructor(message = 'Access denied', details) {
76
+ super(403, message, 'FORBIDDEN_ERROR', details);
77
+ this.name = 'ForbiddenError';
78
+ }
79
+ }
80
+ exports.ForbiddenError = ForbiddenError;
81
+ /**
82
+ * 404 Not Found - Resource not found
83
+ */
84
+ class NotFoundError extends HttpError {
85
+ constructor(message = 'Resource not found', details) {
86
+ super(404, message, 'NOT_FOUND_ERROR', details);
87
+ this.name = 'NotFoundError';
88
+ }
89
+ }
90
+ exports.NotFoundError = NotFoundError;
91
+ /**
92
+ * 409 Conflict - Resource already exists or state conflict
93
+ */
94
+ class ConflictError extends HttpError {
95
+ constructor(message = 'Resource already exists', details) {
96
+ super(409, message, 'CONFLICT_ERROR', details);
97
+ this.name = 'ConflictError';
98
+ }
99
+ }
100
+ exports.ConflictError = ConflictError;
101
+ /**
102
+ * 500 Internal Server Error - Unexpected errors with optional cause chaining
103
+ */
104
+ class InternalServerError extends HttpError {
105
+ cause;
106
+ constructor(message = 'Internal server error', cause, details) {
107
+ super(500, message, 'INTERNAL_SERVER_ERROR', details);
108
+ this.cause = cause;
109
+ this.name = 'InternalServerError';
110
+ if (cause) {
111
+ this.stack = `${this.stack}\nCaused by: ${cause.stack}`;
112
+ }
113
+ }
114
+ }
115
+ exports.InternalServerError = InternalServerError;
116
+ /**
117
+ * Service layer error with error code
118
+ * Use this in service classes for business logic errors
119
+ * Not tied to specific HTTP status codes
120
+ */
121
+ class ServiceError extends Error {
122
+ code;
123
+ details;
124
+ constructor(message, code, details) {
125
+ super(message);
126
+ this.code = code;
127
+ this.details = details;
128
+ this.name = 'ServiceError';
129
+ }
130
+ }
131
+ exports.ServiceError = ServiceError;
59
132
  //# sourceMappingURL=errors.js.map
@@ -1,4 +1,4 @@
1
- import { Context, NoonyRequest, NoonyResponse } from './core';
1
+ import { Context, CustomRequest, CustomResponse, GenericRequest, GenericResponse } from './core';
2
2
  /**
3
3
  * Interface representing a base structure for middleware with optional lifecycle methods.
4
4
  *
@@ -7,12 +7,13 @@ import { Context, NoonyRequest, NoonyResponse } from './core';
7
7
  * executed before and after a process, as well as handling errors that
8
8
  * occur during the process.
9
9
  *
10
- * @template T - The type of the request data. Defaults to unknown.
10
+ * @template T - The type of the request or input context. Defaults to unknown.
11
+ * @template U - The type of the response or output context. Defaults to unknown.
11
12
  */
12
- export interface BaseMiddleware<T = unknown> {
13
- before?: (context: Context<T>) => Promise<void>;
14
- after?: (context: Context<T>) => Promise<void>;
15
- onError?: (error: Error, context: Context<T>) => Promise<void>;
13
+ export interface BaseMiddleware<T = unknown, U = unknown> {
14
+ before?: (context: Context<T, U>) => Promise<void>;
15
+ after?: (context: Context<T, U>) => Promise<void>;
16
+ onError?: (error: Error, context: Context<T, U>) => Promise<void>;
16
17
  }
17
18
  /**
18
19
  * The Handler class is responsible for managing and executing middleware functions
@@ -22,8 +23,6 @@ export interface BaseMiddleware<T = unknown> {
22
23
  * process a request/response flow either before the main handler (via `before`),
23
24
  * after the main handler (via `after`), or handle errors (via `onError`).
24
25
  *
25
- * @example
26
- * ```typescript
27
26
  * interface MessagePayload {
28
27
  * action: string;
29
28
  * data: Record<string, unknown>;
@@ -34,29 +33,25 @@ export interface BaseMiddleware<T = unknown> {
34
33
  * .use(bodyParser())
35
34
  * .handle(async (context) => {
36
35
  * const { req } = context;
37
- * // Handle the request with type-safe access to req.validatedBody
36
+ * // Handle the request
38
37
  * });
39
- * ```
40
38
  * @template T Type for the input request data.
39
+ * @template U Type for the additional context or response data.
41
40
  */
42
- export declare class Handler<T = unknown> {
41
+ export declare class Handler<T = unknown, U = unknown> {
43
42
  private baseMiddlewares;
44
43
  private handler;
45
44
  private reversedMiddlewares;
46
45
  private errorMiddlewares;
47
46
  private middlewaresPrecomputed;
48
- static use<T = unknown>(middleware: BaseMiddleware<T>): Handler<T>;
49
- use(middleware: BaseMiddleware<T>): Handler<T>;
50
- handle(handler: (context: Context<T>) => Promise<void>): Handler<T>;
47
+ 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>;
49
+ handle(handler: (context: Context<T, U>) => Promise<void>): Handler<T, U>;
51
50
  /**
52
51
  * Performance optimization: Pre-compute middleware arrays to avoid runtime array operations
53
52
  */
54
53
  private precomputeMiddlewareArrays;
55
- /**
56
- * Universal execute method that works with any HTTP framework
57
- * Automatically detects and adapts GCP, Express, AWS Lambda, Fastify, etc.
58
- */
59
- execute(nativeReq: unknown, nativeRes: unknown): Promise<void>;
54
+ execute(req: CustomRequest<T>, res: CustomResponse): Promise<void>;
60
55
  /**
61
56
  * Execute before middlewares with optimized batching for independent middlewares
62
57
  */
@@ -70,24 +65,8 @@ export declare class Handler<T = unknown> {
70
65
  */
71
66
  private executeErrorMiddlewares;
72
67
  /**
73
- * Universal request adapter - converts any framework's request to NoonyRequest
74
- */
75
- private adaptToNoonyRequest;
76
- /**
77
- * Universal response adapter - converts any framework's response to NoonyResponse
78
- */
79
- private adaptToNoonyResponse;
80
- /**
81
- * Create response adapter for AWS Lambda
82
- */
83
- private createAWSLambdaResponse;
84
- /**
85
- * Create response adapter for standard HTTP frameworks (GCP, Express, Fastify)
86
- */
87
- private createStandardHTTPResponse;
88
- /**
89
- * @deprecated Use execute() instead - automatically detects framework
68
+ * Framework-agnostic execute method that works with GenericRequest/GenericResponse
90
69
  */
91
- executeGeneric(req: NoonyRequest<T>, res: NoonyResponse): Promise<void>;
70
+ executeGeneric(req: GenericRequest<T>, res: GenericResponse): Promise<void>;
92
71
  }
93
72
  //# sourceMappingURL=handler.d.ts.map