@middy/core 3.0.0-alpha.2 → 3.0.0-alpha.3
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.d.ts +3 -1
- package/index.js +207 -125
- package/package.json +4 -6
package/index.d.ts
CHANGED
|
@@ -42,8 +42,10 @@ export interface MiddlewareObj<TEvent = any, TResult = any, TErr = Error, TConte
|
|
|
42
42
|
// The AWS provided Handler type uses void | Promise<TResult> so we have no choice but to follow and suppress the linter warning
|
|
43
43
|
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
|
44
44
|
type MiddyInputHandler<TEvent, TResult, TContext extends LambdaContext = LambdaContext> = (event: TEvent, context: TContext, callback: LambdaCallback<TResult>) => void | Promise<TResult>
|
|
45
|
+
type MiddyInputPromiseHandler<TEvent, TResult, TContext extends LambdaContext = LambdaContext> = ( event: TEvent, context: TContext, ) => Promise<TResult>;
|
|
45
46
|
|
|
46
|
-
export interface MiddyfiedHandler<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext> extends MiddyInputHandler<TEvent, TResult, TContext
|
|
47
|
+
export interface MiddyfiedHandler<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext> extends MiddyInputHandler<TEvent, TResult, TContext>,
|
|
48
|
+
MiddyInputPromiseHandler<TEvent, TResult, TContext> {
|
|
47
49
|
use: UseFn<TEvent, TResult, TErr, TContext>
|
|
48
50
|
before: AttachMiddlewareFn<TEvent, TResult, TErr, TContext>
|
|
49
51
|
after: AttachMiddlewareFn<TEvent, TResult, TErr, TContext>
|
package/index.js
CHANGED
|
@@ -1,168 +1,250 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const defaultBaseHandler = () => {};
|
|
4
|
-
|
|
1
|
+
const defaultLambdaHandler = () => {}
|
|
5
2
|
const defaultPlugin = {
|
|
6
3
|
timeoutEarlyInMillis: 5,
|
|
7
|
-
timeoutEarlyResponse: () => {
|
|
8
|
-
|
|
4
|
+
timeoutEarlyResponse: () => { throw new Error('Timeout') }
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const middy = (lambdaHandler = defaultLambdaHandler, plugin = {}) => {
|
|
8
|
+
// Allow base handler to be set using .handler()
|
|
9
|
+
if (typeof lambdaHandler !== 'function') {
|
|
10
|
+
plugin = lambdaHandler
|
|
11
|
+
lambdaHandler = defaultLambdaHandler
|
|
9
12
|
}
|
|
10
|
-
}
|
|
13
|
+
plugin = { ...defaultPlugin, ...plugin }
|
|
14
|
+
plugin.timeoutEarly = plugin.timeoutEarlyInMillis > 0
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
plugin = baseHandler;
|
|
17
|
-
baseHandler = defaultBaseHandler;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
plugin = { ...defaultPlugin,
|
|
21
|
-
...plugin
|
|
22
|
-
};
|
|
23
|
-
(_plugin$beforePrefetc = (_plugin = plugin).beforePrefetch) === null || _plugin$beforePrefetc === void 0 ? void 0 : _plugin$beforePrefetc.call(_plugin);
|
|
24
|
-
const beforeMiddlewares = [];
|
|
25
|
-
const afterMiddlewares = [];
|
|
26
|
-
const onErrorMiddlewares = [];
|
|
16
|
+
plugin.beforePrefetch?.()
|
|
17
|
+
const beforeMiddlewares = []
|
|
18
|
+
const afterMiddlewares = []
|
|
19
|
+
const onErrorMiddlewares = []
|
|
27
20
|
|
|
28
21
|
const middy = (event = {}, context = {}) => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
(_plugin$requestStart = (_plugin2 = plugin).requestStart) === null || _plugin$requestStart === void 0 ? void 0 : _plugin$requestStart.call(_plugin2);
|
|
22
|
+
plugin.requestStart?.()
|
|
32
23
|
const request = {
|
|
33
24
|
event,
|
|
34
25
|
context,
|
|
35
26
|
response: undefined,
|
|
36
27
|
error: undefined,
|
|
37
28
|
internal: plugin.internal ?? {}
|
|
38
|
-
}
|
|
39
|
-
return runRequest(request, [...beforeMiddlewares], baseHandler, [...afterMiddlewares], [...onErrorMiddlewares], plugin);
|
|
40
|
-
};
|
|
29
|
+
}
|
|
41
30
|
|
|
42
|
-
|
|
31
|
+
return runRequest(
|
|
32
|
+
request,
|
|
33
|
+
[...beforeMiddlewares],
|
|
34
|
+
lambdaHandler,
|
|
35
|
+
[...afterMiddlewares],
|
|
36
|
+
[...onErrorMiddlewares],
|
|
37
|
+
plugin
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
middy.use = (middlewares) => {
|
|
43
42
|
if (!Array.isArray(middlewares)) {
|
|
44
|
-
middlewares = [middlewares]
|
|
43
|
+
middlewares = [middlewares]
|
|
45
44
|
}
|
|
46
|
-
|
|
47
45
|
for (const middleware of middlewares) {
|
|
48
|
-
const {
|
|
49
|
-
before,
|
|
50
|
-
after,
|
|
51
|
-
onError
|
|
52
|
-
} = middleware;
|
|
46
|
+
const { before, after, onError } = middleware
|
|
53
47
|
|
|
54
48
|
if (!before && !after && !onError) {
|
|
55
|
-
throw new Error(
|
|
49
|
+
throw new Error(
|
|
50
|
+
'Middleware must be an object containing at least one key among "before", "after", "onError"'
|
|
51
|
+
)
|
|
56
52
|
}
|
|
57
53
|
|
|
58
|
-
if (before) middy.before(before)
|
|
59
|
-
if (after) middy.after(after)
|
|
60
|
-
if (onError) middy.onError(onError)
|
|
54
|
+
if (before) middy.before(before)
|
|
55
|
+
if (after) middy.after(after)
|
|
56
|
+
if (onError) middy.onError(onError)
|
|
61
57
|
}
|
|
58
|
+
return middy
|
|
59
|
+
}
|
|
62
60
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
middy.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
middy.handler = replaceBaseHandler => {
|
|
82
|
-
baseHandler = replaceBaseHandler;
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
return middy;
|
|
86
|
-
};
|
|
61
|
+
// Inline Middlewares
|
|
62
|
+
middy.before = (beforeMiddleware) => {
|
|
63
|
+
beforeMiddlewares.push(beforeMiddleware)
|
|
64
|
+
return middy
|
|
65
|
+
}
|
|
66
|
+
middy.after = (afterMiddleware) => {
|
|
67
|
+
afterMiddlewares.unshift(afterMiddleware)
|
|
68
|
+
return middy
|
|
69
|
+
}
|
|
70
|
+
middy.onError = (onErrorMiddleware) => {
|
|
71
|
+
onErrorMiddlewares.unshift(onErrorMiddleware)
|
|
72
|
+
return middy
|
|
73
|
+
}
|
|
74
|
+
middy.handler = (replaceLambdaHandler) => {
|
|
75
|
+
lambdaHandler = replaceLambdaHandler
|
|
76
|
+
}
|
|
87
77
|
|
|
88
|
-
|
|
78
|
+
return middy
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const runRequest = async (
|
|
82
|
+
request,
|
|
83
|
+
beforeMiddlewares,
|
|
84
|
+
lambdaHandler,
|
|
85
|
+
afterMiddlewares,
|
|
86
|
+
onErrorMiddlewares,
|
|
87
|
+
plugin
|
|
88
|
+
) => {
|
|
89
|
+
const { timeoutEarly } = plugin
|
|
89
90
|
try {
|
|
90
|
-
await runMiddlewares(request, beforeMiddlewares, plugin)
|
|
91
|
-
|
|
91
|
+
await runMiddlewares(request, beforeMiddlewares, plugin)
|
|
92
|
+
// Check if before stack hasn't exit early
|
|
92
93
|
if (request.response === undefined) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
request.response = await Promise.race([
|
|
99
|
-
signal: handlerAbort.signal
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
94
|
+
plugin.beforeHandler?.()
|
|
95
|
+
|
|
96
|
+
const handlerAbort = new AbortController()
|
|
97
|
+
let timeoutAbort
|
|
98
|
+
if (timeoutEarly) timeoutAbort = new AbortController()
|
|
99
|
+
request.response = await Promise.race([
|
|
100
|
+
lambdaHandler(request.event, request.context, { signal: handlerAbort.signal }),
|
|
101
|
+
timeoutEarly
|
|
102
|
+
? setTimeoutPromise(request.context.getRemainingTimeInMillis() - plugin.timeoutEarlyInMillis, { signal: timeoutAbort.signal })
|
|
103
|
+
.then(() => {
|
|
104
|
+
handlerAbort.abort()
|
|
105
|
+
return plugin.timeoutEarlyResponse()
|
|
106
|
+
})
|
|
107
|
+
: Promise.race([])
|
|
108
|
+
])
|
|
109
|
+
if (timeoutEarly) timeoutAbort.abort() // lambdaHandler may not be a promise
|
|
110
|
+
|
|
111
|
+
plugin.afterHandler?.()
|
|
112
|
+
await runMiddlewares(request, afterMiddlewares, plugin)
|
|
109
113
|
}
|
|
110
114
|
} catch (e) {
|
|
111
|
-
|
|
112
|
-
request.
|
|
113
|
-
|
|
115
|
+
// Reset response changes made by after stack before error thrown
|
|
116
|
+
request.response = undefined
|
|
117
|
+
request.error = e
|
|
114
118
|
try {
|
|
115
|
-
await runMiddlewares(request, onErrorMiddlewares, plugin)
|
|
119
|
+
await runMiddlewares(request, onErrorMiddlewares, plugin)
|
|
116
120
|
} catch (e) {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
+
// Save error that wasn't handled
|
|
122
|
+
e.originalError = request.error
|
|
123
|
+
request.error = e
|
|
121
124
|
|
|
122
|
-
|
|
125
|
+
throw request.error
|
|
126
|
+
}
|
|
127
|
+
// Catch if onError stack hasn't handled the error
|
|
128
|
+
if (request.response === undefined) throw request.error
|
|
123
129
|
} finally {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
await ((_plugin$requestEnd = plugin.requestEnd) === null || _plugin$requestEnd === void 0 ? void 0 : _plugin$requestEnd.call(plugin, request));
|
|
130
|
+
await plugin.requestEnd?.(request)
|
|
127
131
|
}
|
|
128
132
|
|
|
129
|
-
return request.response
|
|
130
|
-
}
|
|
133
|
+
return request.response
|
|
134
|
+
}
|
|
131
135
|
|
|
132
136
|
const runMiddlewares = async (request, middlewares, plugin) => {
|
|
133
137
|
for (const nextMiddleware of middlewares) {
|
|
134
|
-
|
|
138
|
+
plugin.beforeMiddleware?.(nextMiddleware.name)
|
|
139
|
+
const res = await nextMiddleware(request)
|
|
140
|
+
plugin.afterMiddleware?.(nextMiddleware.name)
|
|
141
|
+
// short circuit chaining and respond early
|
|
142
|
+
if (res !== undefined) {
|
|
143
|
+
request.response = res
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Start Polyfill (Nodejs v14)
|
|
150
|
+
/*
|
|
151
|
+
MIT License
|
|
152
|
+
|
|
153
|
+
Copyright (c) 2019 Steve Faulkner
|
|
154
|
+
|
|
155
|
+
node-abort-controller
|
|
156
|
+
*/
|
|
157
|
+
const polyfillAbortController = async () => {
|
|
158
|
+
if (process.version < 'v15.0.0') {
|
|
159
|
+
const events = await import('events')
|
|
160
|
+
const { EventEmitter } = events
|
|
161
|
+
|
|
162
|
+
class AbortSignal {
|
|
163
|
+
constructor () {
|
|
164
|
+
this.eventEmitter = new EventEmitter()
|
|
165
|
+
this.onabort = null
|
|
166
|
+
this.aborted = false
|
|
167
|
+
}
|
|
135
168
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
169
|
+
toString () {
|
|
170
|
+
return '[object AbortSignal]'
|
|
171
|
+
}
|
|
139
172
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
173
|
+
get [Symbol.toStringTag] () {
|
|
174
|
+
return 'AbortSignal'
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
removeEventListener (name, handler) {
|
|
178
|
+
this.eventEmitter.removeListener(name, handler)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
addEventListener (name, handler) {
|
|
182
|
+
this.eventEmitter.on(name, handler)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
dispatchEvent (type) {
|
|
186
|
+
const event = { type, target: this }
|
|
187
|
+
const handlerName = `on${type}`
|
|
188
|
+
|
|
189
|
+
if (typeof this[handlerName] === 'function') this[handlerName](event)
|
|
190
|
+
|
|
191
|
+
this.eventEmitter.emit(type, event)
|
|
192
|
+
}
|
|
143
193
|
}
|
|
194
|
+
|
|
195
|
+
return class AbortController {
|
|
196
|
+
constructor () {
|
|
197
|
+
this.signal = new AbortSignal()
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
abort () {
|
|
201
|
+
if (this.signal.aborted) return
|
|
202
|
+
|
|
203
|
+
this.signal.aborted = true
|
|
204
|
+
this.signal.dispatchEvent('abort')
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
toString () {
|
|
208
|
+
return '[object AbortController]'
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
get [Symbol.toStringTag] () {
|
|
212
|
+
return 'AbortController'
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
return AbortController
|
|
144
217
|
}
|
|
145
|
-
}
|
|
218
|
+
}
|
|
219
|
+
global.AbortController = await polyfillAbortController()
|
|
146
220
|
|
|
147
|
-
const
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
221
|
+
const polyfillSetTimeoutPromise = async () => {
|
|
222
|
+
|
|
223
|
+
if (process.version < 'v15.0.0') {
|
|
224
|
+
return (ms, { signal }) => {
|
|
225
|
+
if (signal.aborted) {
|
|
226
|
+
return Promise.reject(new Error('Aborted', 'AbortError'))
|
|
227
|
+
}
|
|
228
|
+
return new Promise((resolve, reject) => {
|
|
229
|
+
const abortHandler = () => {
|
|
230
|
+
clearTimeout(timeout)
|
|
231
|
+
reject(new Error('Aborted', 'AbortError'))
|
|
232
|
+
}
|
|
233
|
+
// start async operation
|
|
234
|
+
const timeout = setTimeout(() => {
|
|
235
|
+
resolve()
|
|
236
|
+
signal.removeEventListener('abort', abortHandler)
|
|
237
|
+
}, ms)
|
|
238
|
+
signal.addEventListener('abort', abortHandler)
|
|
239
|
+
})
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
const timers = await import('timers/promises')
|
|
243
|
+
return timers.setTimeout
|
|
152
244
|
}
|
|
245
|
+
}
|
|
246
|
+
global.setTimeoutPromise = await polyfillSetTimeoutPromise()
|
|
247
|
+
// import { setTimeout as setTimeoutPromise } from 'timers/promises'
|
|
248
|
+
// End Polyfill
|
|
153
249
|
|
|
154
|
-
|
|
155
|
-
const abortHandler = () => {
|
|
156
|
-
clearTimeout(timeout);
|
|
157
|
-
reject(new Error('Aborted', 'AbortError'));
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
const timeout = setTimeout(() => {
|
|
161
|
-
resolve();
|
|
162
|
-
signal.removeEventListener('abort', abortHandler);
|
|
163
|
-
}, ms);
|
|
164
|
-
signal.addEventListener('abort', abortHandler);
|
|
165
|
-
});
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
export default middy;
|
|
250
|
+
export default middy
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@middy/core",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.3",
|
|
4
4
|
"description": "🛵 The stylish Node.js middleware engine for AWS Lambda (core package)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
],
|
|
19
19
|
"scripts": {
|
|
20
20
|
"test": "npm run test:unit",
|
|
21
|
-
"test:unit": "ava"
|
|
21
|
+
"test:unit": "ava",
|
|
22
|
+
"test:benchmark": "node __benchmarks__/index.js"
|
|
22
23
|
},
|
|
23
24
|
"license": "MIT",
|
|
24
25
|
"keywords": [
|
|
@@ -46,8 +47,5 @@
|
|
|
46
47
|
"@types/aws-lambda": "^8.10.76",
|
|
47
48
|
"@types/node": "^17.0.0"
|
|
48
49
|
},
|
|
49
|
-
"gitHead": "
|
|
50
|
-
"dependencies": {
|
|
51
|
-
"node-abort-controller": "3.0.1"
|
|
52
|
-
}
|
|
50
|
+
"gitHead": "1441158711580313765e6d156046ef0fade0d156"
|
|
53
51
|
}
|