1 | import { EventEmitter } from 'events';
|
2 | const defaultLambdaHandler = ()=>{};
|
3 | const defaultPlugin = {
|
4 | timeoutEarlyInMillis: 5,
|
5 | timeoutEarlyResponse: ()=>{
|
6 | throw new Error('Timeout');
|
7 | }
|
8 | };
|
9 | const middy = (lambdaHandler = defaultLambdaHandler, plugin = {})=>{
|
10 | if (typeof lambdaHandler !== 'function') {
|
11 | plugin = lambdaHandler;
|
12 | lambdaHandler = defaultLambdaHandler;
|
13 | }
|
14 | plugin = {
|
15 | ...defaultPlugin,
|
16 | ...plugin
|
17 | };
|
18 | plugin.timeoutEarly = plugin.timeoutEarlyInMillis > 0;
|
19 | plugin.beforePrefetch?.();
|
20 | const beforeMiddlewares = [];
|
21 | const afterMiddlewares = [];
|
22 | const onErrorMiddlewares = [];
|
23 | const middy1 = (event = {}, context = {})=>{
|
24 | plugin.requestStart?.();
|
25 | const request = {
|
26 | event,
|
27 | context,
|
28 | response: undefined,
|
29 | error: undefined,
|
30 | internal: plugin.internal ?? {}
|
31 | };
|
32 | return runRequest(request, [
|
33 | ...beforeMiddlewares
|
34 | ], lambdaHandler, [
|
35 | ...afterMiddlewares
|
36 | ], [
|
37 | ...onErrorMiddlewares
|
38 | ], plugin);
|
39 | };
|
40 | middy1.use = (middlewares)=>{
|
41 | if (!Array.isArray(middlewares)) {
|
42 | middlewares = [
|
43 | middlewares
|
44 | ];
|
45 | }
|
46 | for (const middleware of middlewares){
|
47 | const { before , after , onError } = middleware;
|
48 | if (!before && !after && !onError) {
|
49 | throw new Error('Middleware must be an object containing at least one key among "before", "after", "onError"');
|
50 | }
|
51 | if (before) middy1.before(before);
|
52 | if (after) middy1.after(after);
|
53 | if (onError) middy1.onError(onError);
|
54 | }
|
55 | return middy1;
|
56 | };
|
57 | middy1.before = (beforeMiddleware)=>{
|
58 | beforeMiddlewares.push(beforeMiddleware);
|
59 | return middy1;
|
60 | };
|
61 | middy1.after = (afterMiddleware)=>{
|
62 | afterMiddlewares.unshift(afterMiddleware);
|
63 | return middy1;
|
64 | };
|
65 | middy1.onError = (onErrorMiddleware)=>{
|
66 | onErrorMiddlewares.unshift(onErrorMiddleware);
|
67 | return middy1;
|
68 | };
|
69 | middy1.handler = (replaceLambdaHandler)=>{
|
70 | lambdaHandler = replaceLambdaHandler;
|
71 | return middy1;
|
72 | };
|
73 | return middy1;
|
74 | };
|
75 | const runRequest = async (request, beforeMiddlewares, lambdaHandler, afterMiddlewares, onErrorMiddlewares, plugin)=>{
|
76 | const timeoutEarly = plugin.timeoutEarly && request.context.getRemainingTimeInMillis;
|
77 | try {
|
78 | await runMiddlewares(request, beforeMiddlewares, plugin);
|
79 | if (typeof request.response === 'undefined') {
|
80 | plugin.beforeHandler?.();
|
81 | const handlerAbort = new AbortController();
|
82 | let timeoutAbort;
|
83 | if (timeoutEarly) timeoutAbort = new AbortController();
|
84 | request.response = await Promise.race([
|
85 | lambdaHandler(request.event, request.context, {
|
86 | signal: handlerAbort.signal
|
87 | }),
|
88 | timeoutEarly ? setTimeoutPromise(request.context.getRemainingTimeInMillis() - plugin.timeoutEarlyInMillis, {
|
89 | signal: timeoutAbort.signal
|
90 | }).then(()=>{
|
91 | handlerAbort.abort();
|
92 | return plugin.timeoutEarlyResponse();
|
93 | }) : Promise.race([])
|
94 | ]);
|
95 | if (timeoutEarly) timeoutAbort.abort();
|
96 | plugin.afterHandler?.();
|
97 | await runMiddlewares(request, afterMiddlewares, plugin);
|
98 | }
|
99 | } catch (e) {
|
100 | request.response = undefined;
|
101 | request.error = e;
|
102 | try {
|
103 | await runMiddlewares(request, onErrorMiddlewares, plugin);
|
104 | } catch (e) {
|
105 | e.originalError = request.error;
|
106 | request.error = e;
|
107 | throw request.error;
|
108 | }
|
109 | if (typeof request.response === 'undefined') throw request.error;
|
110 | } finally{
|
111 | await plugin.requestEnd?.(request);
|
112 | }
|
113 | return request.response;
|
114 | };
|
115 | const runMiddlewares = async (request, middlewares, plugin)=>{
|
116 | for (const nextMiddleware of middlewares){
|
117 | plugin.beforeMiddleware?.(nextMiddleware.name);
|
118 | const res = await nextMiddleware(request);
|
119 | plugin.afterMiddleware?.(nextMiddleware.name);
|
120 | if (typeof res !== 'undefined') {
|
121 | request.response = res;
|
122 | return;
|
123 | }
|
124 | }
|
125 | };
|
126 | const polyfillAbortController = ()=>{
|
127 | if (process.version < 'v15.0.0') {
|
128 | class AbortSignal {
|
129 | toString() {
|
130 | return '[object AbortSignal]';
|
131 | }
|
132 | get [Symbol.toStringTag]() {
|
133 | return 'AbortSignal';
|
134 | }
|
135 | removeEventListener(name, handler) {
|
136 | this.eventEmitter.removeListener(name, handler);
|
137 | }
|
138 | addEventListener(name, handler) {
|
139 | this.eventEmitter.on(name, handler);
|
140 | }
|
141 | dispatchEvent(type) {
|
142 | const event = {
|
143 | type,
|
144 | target: this
|
145 | };
|
146 | const handlerName = `on${type}`;
|
147 | if (typeof this[handlerName] === 'function') this[handlerName](event);
|
148 | this.eventEmitter.emit(type, event);
|
149 | }
|
150 | constructor(){
|
151 | this.eventEmitter = new EventEmitter();
|
152 | this.onabort = null;
|
153 | this.aborted = false;
|
154 | }
|
155 | }
|
156 | return class AbortController {
|
157 | abort() {
|
158 | if (this.signal.aborted) return;
|
159 | this.signal.aborted = true;
|
160 | this.signal.dispatchEvent('abort');
|
161 | }
|
162 | toString() {
|
163 | return '[object AbortController]';
|
164 | }
|
165 | get [Symbol.toStringTag]() {
|
166 | return 'AbortController';
|
167 | }
|
168 | constructor(){
|
169 | this.signal = new AbortSignal();
|
170 | }
|
171 | };
|
172 | } else {
|
173 | return AbortController;
|
174 | }
|
175 | };
|
176 | global.AbortController = polyfillAbortController();
|
177 | const polyfillSetTimeoutPromise = ()=>{
|
178 | return (ms, { signal })=>{
|
179 | if (signal.aborted) {
|
180 | return Promise.reject(new Error('Aborted', 'AbortError'));
|
181 | }
|
182 | return new Promise((resolve, reject)=>{
|
183 | const abortHandler = ()=>{
|
184 | clearTimeout(timeout);
|
185 | reject(new Error('Aborted', 'AbortError'));
|
186 | };
|
187 | const timeout = setTimeout(()=>{
|
188 | resolve();
|
189 | signal.removeEventListener('abort', abortHandler);
|
190 | }, ms);
|
191 | signal.addEventListener('abort', abortHandler);
|
192 | });
|
193 | };
|
194 | };
|
195 | const setTimeoutPromise = polyfillSetTimeoutPromise();
|
196 | export default middy;
|
197 |
|
198 |
|
199 |
|
\ | No newline at end of file |