@middy/core 5.0.0-alpha.1 → 5.0.0-alpha.2
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/index.js +168 -201
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -1,209 +1,176 @@
|
|
|
1
|
-
/* global awslambda */
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const defaultLambdaHandler = () => {}
|
|
1
|
+
/* global awslambda */ import { Readable } from 'node:stream';
|
|
2
|
+
import { pipeline } from 'node:stream/promises';
|
|
3
|
+
import { setTimeout } from 'node:timers/promises';
|
|
4
|
+
const defaultLambdaHandler = ()=>{};
|
|
7
5
|
const defaultPlugin = {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
plugin = { ...defaultPlugin, ...plugin }
|
|
26
|
-
plugin.timeoutEarly = plugin.timeoutEarlyInMillis > 0
|
|
27
|
-
|
|
28
|
-
plugin.beforePrefetch?.()
|
|
29
|
-
const beforeMiddlewares = []
|
|
30
|
-
const afterMiddlewares = []
|
|
31
|
-
const onErrorMiddlewares = []
|
|
32
|
-
|
|
33
|
-
const middyHandler = (event = {}, context = {}) => {
|
|
34
|
-
plugin.requestStart?.()
|
|
35
|
-
const request = {
|
|
36
|
-
event,
|
|
37
|
-
context,
|
|
38
|
-
response: undefined,
|
|
39
|
-
error: undefined,
|
|
40
|
-
internal: plugin.internal ?? {}
|
|
6
|
+
timeoutEarlyInMillis: 5,
|
|
7
|
+
timeoutEarlyResponse: ()=>{
|
|
8
|
+
const err = new Error('[AbortError]: The operation was aborted.', {
|
|
9
|
+
cause: {
|
|
10
|
+
package: '@middy/core'
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
err.name = 'TimeoutError';
|
|
14
|
+
throw err;
|
|
15
|
+
},
|
|
16
|
+
streamifyResponse: false // Deprecate need for this when AWS provides a flag for when it's looking for it
|
|
17
|
+
};
|
|
18
|
+
const middy = (lambdaHandler = defaultLambdaHandler, plugin = {})=>{
|
|
19
|
+
// Allow base handler to be set using .handler()
|
|
20
|
+
if (typeof lambdaHandler !== 'function') {
|
|
21
|
+
plugin = lambdaHandler;
|
|
22
|
+
lambdaHandler = defaultLambdaHandler;
|
|
41
23
|
}
|
|
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
|
-
while (position < length) {
|
|
75
|
-
yield input.substring(position, position + size)
|
|
76
|
-
position += size
|
|
77
|
-
}
|
|
24
|
+
plugin = {
|
|
25
|
+
...defaultPlugin,
|
|
26
|
+
...plugin
|
|
27
|
+
};
|
|
28
|
+
plugin.timeoutEarly = plugin.timeoutEarlyInMillis > 0;
|
|
29
|
+
plugin.beforePrefetch?.();
|
|
30
|
+
const beforeMiddlewares = [];
|
|
31
|
+
const afterMiddlewares = [];
|
|
32
|
+
const onErrorMiddlewares = [];
|
|
33
|
+
const middyHandler = (event = {}, context = {})=>{
|
|
34
|
+
plugin.requestStart?.();
|
|
35
|
+
const request = {
|
|
36
|
+
event,
|
|
37
|
+
context,
|
|
38
|
+
response: undefined,
|
|
39
|
+
error: undefined,
|
|
40
|
+
internal: plugin.internal ?? {}
|
|
41
|
+
};
|
|
42
|
+
return runRequest(request, [
|
|
43
|
+
...beforeMiddlewares
|
|
44
|
+
], lambdaHandler, [
|
|
45
|
+
...afterMiddlewares
|
|
46
|
+
], [
|
|
47
|
+
...onErrorMiddlewares
|
|
48
|
+
], plugin);
|
|
49
|
+
};
|
|
50
|
+
const middy = plugin.streamifyResponse ? awslambda.streamifyResponse(async (event, responseStream, context)=>{
|
|
51
|
+
const handlerResponse = await middyHandler(event, context);
|
|
52
|
+
let handlerBody = handlerResponse;
|
|
53
|
+
if (handlerResponse.statusCode) {
|
|
54
|
+
handlerBody = handlerResponse.body ?? '';
|
|
55
|
+
responseStream = awslambda.HttpResponseStream.from(responseStream, handlerResponse);
|
|
78
56
|
}
|
|
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
|
-
plugin.timeoutEarly && request.context.getRemainingTimeInMillis // disable when AWS context missing (tests, containers)
|
|
142
|
-
try {
|
|
143
|
-
await runMiddlewares(request, beforeMiddlewares, plugin)
|
|
144
|
-
// Check if before stack hasn't exit early
|
|
145
|
-
if (typeof request.response === 'undefined') {
|
|
146
|
-
plugin.beforeHandler?.()
|
|
147
|
-
|
|
148
|
-
const handlerAbort = new AbortController()
|
|
149
|
-
|
|
150
|
-
if (timeoutEarly) timeoutAbort = new AbortController()
|
|
151
|
-
request.response = await Promise.race([
|
|
152
|
-
lambdaHandler(request.event, request.context, {
|
|
153
|
-
signal: handlerAbort.signal
|
|
154
|
-
}),
|
|
155
|
-
timeoutEarly
|
|
156
|
-
? setTimeout(
|
|
157
|
-
request.context.getRemainingTimeInMillis() -
|
|
158
|
-
plugin.timeoutEarlyInMillis,
|
|
159
|
-
undefined,
|
|
160
|
-
{ signal: timeoutAbort.signal }
|
|
161
|
-
).then(() => {
|
|
162
|
-
handlerAbort.abort()
|
|
163
|
-
return plugin.timeoutEarlyResponse()
|
|
164
|
-
})
|
|
165
|
-
: Promise.race([])
|
|
166
|
-
])
|
|
167
|
-
timeoutAbort?.abort() // lambdaHandler may not be a promise
|
|
168
|
-
|
|
169
|
-
plugin.afterHandler?.()
|
|
170
|
-
await runMiddlewares(request, afterMiddlewares, plugin)
|
|
171
|
-
}
|
|
172
|
-
} catch (e) {
|
|
173
|
-
timeoutAbort?.abort() // timeout should be aborted on errors
|
|
174
|
-
|
|
175
|
-
// Reset response changes made by after stack before error thrown
|
|
176
|
-
request.response = undefined
|
|
177
|
-
request.error = e
|
|
57
|
+
// Source @datastream/core (MIT)
|
|
58
|
+
let handlerStream;
|
|
59
|
+
if (handlerBody._readableState) {
|
|
60
|
+
handlerStream = handlerBody;
|
|
61
|
+
} else if (typeof handlerBody === 'string') {
|
|
62
|
+
function* iterator(input) {
|
|
63
|
+
const size = 16384 // 16 * 1024 // Node.js default
|
|
64
|
+
;
|
|
65
|
+
let position = 0;
|
|
66
|
+
const length = input.length;
|
|
67
|
+
while(position < length){
|
|
68
|
+
yield input.substring(position, position + size);
|
|
69
|
+
position += size;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
handlerStream = Readable.from(iterator(handlerBody));
|
|
73
|
+
}
|
|
74
|
+
if (!handlerStream) {
|
|
75
|
+
throw new Error('handler response not a ReadableStream');
|
|
76
|
+
}
|
|
77
|
+
await pipeline(handlerStream, responseStream);
|
|
78
|
+
}) : middyHandler;
|
|
79
|
+
middy.use = (middlewares)=>{
|
|
80
|
+
if (!Array.isArray(middlewares)) {
|
|
81
|
+
middlewares = [
|
|
82
|
+
middlewares
|
|
83
|
+
];
|
|
84
|
+
}
|
|
85
|
+
for (const middleware of middlewares){
|
|
86
|
+
const { before, after, onError } = middleware;
|
|
87
|
+
if (!before && !after && !onError) {
|
|
88
|
+
throw new Error('Middleware must be an object containing at least one key among "before", "after", "onError"');
|
|
89
|
+
}
|
|
90
|
+
if (before) middy.before(before);
|
|
91
|
+
if (after) middy.after(after);
|
|
92
|
+
if (onError) middy.onError(onError);
|
|
93
|
+
}
|
|
94
|
+
return middy;
|
|
95
|
+
};
|
|
96
|
+
// Inline Middlewares
|
|
97
|
+
middy.before = (beforeMiddleware)=>{
|
|
98
|
+
beforeMiddlewares.push(beforeMiddleware);
|
|
99
|
+
return middy;
|
|
100
|
+
};
|
|
101
|
+
middy.after = (afterMiddleware)=>{
|
|
102
|
+
afterMiddlewares.unshift(afterMiddleware);
|
|
103
|
+
return middy;
|
|
104
|
+
};
|
|
105
|
+
middy.onError = (onErrorMiddleware)=>{
|
|
106
|
+
onErrorMiddlewares.unshift(onErrorMiddleware);
|
|
107
|
+
return middy;
|
|
108
|
+
};
|
|
109
|
+
middy.handler = (replaceLambdaHandler)=>{
|
|
110
|
+
lambdaHandler = replaceLambdaHandler;
|
|
111
|
+
return middy;
|
|
112
|
+
};
|
|
113
|
+
return middy;
|
|
114
|
+
};
|
|
115
|
+
const runRequest = async (request, beforeMiddlewares, lambdaHandler, afterMiddlewares, onErrorMiddlewares, plugin)=>{
|
|
116
|
+
let timeoutAbort;
|
|
117
|
+
const timeoutEarly = plugin.timeoutEarly && request.context.getRemainingTimeInMillis // disable when AWS context missing (tests, containers)
|
|
118
|
+
;
|
|
178
119
|
try {
|
|
179
|
-
|
|
120
|
+
await runMiddlewares(request, beforeMiddlewares, plugin);
|
|
121
|
+
// Check if before stack hasn't exit early
|
|
122
|
+
if (typeof request.response === 'undefined') {
|
|
123
|
+
plugin.beforeHandler?.();
|
|
124
|
+
const handlerAbort = new AbortController();
|
|
125
|
+
if (timeoutEarly) timeoutAbort = new AbortController();
|
|
126
|
+
request.response = await Promise.race([
|
|
127
|
+
lambdaHandler(request.event, request.context, {
|
|
128
|
+
signal: handlerAbort.signal
|
|
129
|
+
}),
|
|
130
|
+
timeoutEarly ? setTimeout(request.context.getRemainingTimeInMillis() - plugin.timeoutEarlyInMillis, undefined, {
|
|
131
|
+
signal: timeoutAbort.signal
|
|
132
|
+
}).then(()=>{
|
|
133
|
+
handlerAbort.abort();
|
|
134
|
+
return plugin.timeoutEarlyResponse();
|
|
135
|
+
}) : Promise.race([])
|
|
136
|
+
]);
|
|
137
|
+
timeoutAbort?.abort() // lambdaHandler may not be a promise
|
|
138
|
+
;
|
|
139
|
+
plugin.afterHandler?.();
|
|
140
|
+
await runMiddlewares(request, afterMiddlewares, plugin);
|
|
141
|
+
}
|
|
180
142
|
} catch (e) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
143
|
+
timeoutAbort?.abort() // timeout should be aborted on errors
|
|
144
|
+
;
|
|
145
|
+
// Reset response changes made by after stack before error thrown
|
|
146
|
+
request.response = undefined;
|
|
147
|
+
request.error = e;
|
|
148
|
+
try {
|
|
149
|
+
await runMiddlewares(request, onErrorMiddlewares, plugin);
|
|
150
|
+
} catch (e) {
|
|
151
|
+
// Save error that wasn't handled
|
|
152
|
+
e.originalError = request.error;
|
|
153
|
+
request.error = e;
|
|
154
|
+
throw request.error;
|
|
155
|
+
}
|
|
156
|
+
// Catch if onError stack hasn't handled the error
|
|
157
|
+
if (typeof request.response === 'undefined') throw request.error;
|
|
158
|
+
} finally{
|
|
159
|
+
await plugin.requestEnd?.(request);
|
|
186
160
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const res = await nextMiddleware(request)
|
|
200
|
-
plugin.afterMiddleware?.(nextMiddleware.name)
|
|
201
|
-
// short circuit chaining and respond early
|
|
202
|
-
if (typeof res !== 'undefined') {
|
|
203
|
-
request.response = res
|
|
204
|
-
return
|
|
161
|
+
return request.response;
|
|
162
|
+
};
|
|
163
|
+
const runMiddlewares = async (request, middlewares, plugin)=>{
|
|
164
|
+
for (const nextMiddleware of middlewares){
|
|
165
|
+
plugin.beforeMiddleware?.(nextMiddleware.name);
|
|
166
|
+
const res = await nextMiddleware(request);
|
|
167
|
+
plugin.afterMiddleware?.(nextMiddleware.name);
|
|
168
|
+
// short circuit chaining and respond early
|
|
169
|
+
if (typeof res !== 'undefined') {
|
|
170
|
+
request.response = res;
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
205
173
|
}
|
|
206
|
-
|
|
207
|
-
|
|
174
|
+
};
|
|
175
|
+
export default middy;
|
|
208
176
|
|
|
209
|
-
export default middy
|