-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.ts
More file actions
159 lines (146 loc) · 4.28 KB
/
Copy pathindex.ts
File metadata and controls
159 lines (146 loc) · 4.28 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
import type {
APIGatewayProxyEvent,
APIGatewayProxyResult,
Context,
} from "aws-lambda";
/**
* Extended Context with dynamic properties and error support.
*/
export type ExtendedContext = Context & {
error?: unknown;
[key: string]: unknown;
};
/**
* Middleware hook type definition.
* Allows modification of event, context, and optional response.
*/
export type MiddlewareHook = (
event: APIGatewayProxyEvent,
context: ExtendedContext,
response?: APIGatewayProxyResult | null,
// biome-ignore lint/suspicious/noConfusingVoidType: <explanation>
) => Promise<APIGatewayProxyResult | void>;
/**
* Middleware utility class.
*/
class MiddlewareUtil {
private beforeHooks: MiddlewareHook[] = [];
private afterHooks: MiddlewareHook[] = [];
private onErrorHooks: MiddlewareHook[] = [];
private finallyHooks: MiddlewareHook[] = [];
/**
* Register a before hook.
*
* @param hook - The hook function to be executed before the main handler.
*/
useBefore(hook: MiddlewareHook): void {
this.beforeHooks.push(hook);
}
/**
* Register an after hook.
*
* @param hook - The hook function to be executed after the main handler.
*/
useAfter(hook: MiddlewareHook): void {
this.afterHooks.push(hook);
}
/**
* Register an onError hook.
*
* @param hook - The hook function to be executed if an error occurs.
*/
useOnError(hook: MiddlewareHook): void {
this.onErrorHooks.push(hook);
}
/**
* Register a finally hook.
*
* @param hook - The hook function to be executed at the end, regardless of success or error.
*/
useFinally(hook: MiddlewareHook): void {
this.finallyHooks.push(hook);
}
/**
* Executes hooks in the provided list.
* Allows modification of event, context, and response.
*
* @param hooks - The list of hooks to be executed.
* @param event - The event object.
* @param context - The extended context object.
* @param response - The current response, if available.
* @returns A modified response if provided by a hook.
*/
private async executeHooks(
hooks: MiddlewareHook[],
event: APIGatewayProxyEvent,
context: ExtendedContext,
response?: APIGatewayProxyResult | null,
): Promise<APIGatewayProxyResult | null> {
for (const hook of hooks) {
const hookResponse = await hook(event, context, response);
if (hookResponse) {
return hookResponse; // Short-circuit if a hook returns a response
}
}
return null;
}
/**
* Wraps the handler with middleware hooks.
*
* @param handler - The original handler function.
* @returns A function that applies middleware before and after the handler.
*/
handler(
handler: (
event: APIGatewayProxyEvent,
context: ExtendedContext,
) => Promise<APIGatewayProxyResult>,
): (
event: APIGatewayProxyEvent,
context: ExtendedContext,
) => Promise<APIGatewayProxyResult> {
return async (
event: APIGatewayProxyEvent,
context: ExtendedContext,
): Promise<APIGatewayProxyResult> => {
let response: APIGatewayProxyResult | null = null;
try {
// Execute before hooks
const beforeResponse = await this.executeHooks(
this.beforeHooks,
event,
context,
);
if (beforeResponse) {
return beforeResponse; // Short-circuit if a before hook returns a response
}
// Execute the main handler
response = await handler(event, context);
// Execute after hooks
const afterResponse = await this.executeHooks(
this.afterHooks,
event,
context,
response,
);
return afterResponse || response; // Use modified response if provided
} catch (error) {
context.error = error; // Attach error to context for onError hooks
// Execute onError hooks
const errorResponse = await this.executeHooks(
this.onErrorHooks,
event,
context,
);
if (errorResponse) {
return errorResponse; // Use error response if provided
}
throw error; // Rethrow if not handled
} finally {
// Execute finally hooks
await this.executeHooks(this.finallyHooks, event, context, response);
}
};
}
}
export default MiddlewareUtil;