@middy/core
Advanced tools
Comparing version 5.0.0-alpha.0 to 5.0.0-alpha.1
151
index.d.ts
@@ -9,3 +9,5 @@ import { | ||
declare type PluginHookWithMiddlewareName = (middlewareName: string) => void | ||
declare type PluginHookPromise = (request: Request) => Promise<unknown> | unknown | ||
declare type PluginHookPromise = ( | ||
request: Request | ||
) => Promise<unknown> | unknown | ||
@@ -23,5 +25,12 @@ interface PluginObject { | ||
requestEnd?: PluginHookPromise | ||
streamifyResponse?: Boolean | ||
} | ||
export interface Request<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext> { | ||
export interface Request< | ||
TEvent = any, | ||
TResult = any, | ||
TErr = Error, | ||
TContext extends LambdaContext = LambdaContext, | ||
TInternal extends Record<string, unknown> = {} | ||
> { | ||
event: TEvent | ||
@@ -31,13 +40,23 @@ context: TContext | ||
error: TErr | null | ||
internal: { | ||
[key: string]: any | ||
} | ||
internal: TInternal | ||
} | ||
declare type MiddlewareFn<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext> = (request: Request<TEvent, TResult, TErr, TContext>) => any | ||
declare type MiddlewareFn< | ||
TEvent = any, | ||
TResult = any, | ||
TErr = Error, | ||
TContext extends LambdaContext = LambdaContext, | ||
TInternal extends Record<string, unknown> = {} | ||
> = (request: Request<TEvent, TResult, TErr, TContext, TInternal>) => any | ||
export interface MiddlewareObj<TEvent = unknown, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext> { | ||
before?: MiddlewareFn<TEvent, TResult, TErr, TContext> | ||
after?: MiddlewareFn<TEvent, TResult, TErr, TContext> | ||
onError?: MiddlewareFn<TEvent, TResult, TErr, TContext> | ||
export interface MiddlewareObj< | ||
TEvent = unknown, | ||
TResult = any, | ||
TErr = Error, | ||
TContext extends LambdaContext = LambdaContext, | ||
TInternal extends Record<string, unknown> = {} | ||
> { | ||
before?: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal> | ||
after?: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal> | ||
onError?: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal> | ||
} | ||
@@ -47,29 +66,88 @@ | ||
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type | ||
type MiddyInputHandler<TEvent, TResult, TContext extends LambdaContext = LambdaContext> = (event: TEvent, context: TContext, callback: LambdaCallback<TResult>) => void | Promise<TResult> | ||
type MiddyInputPromiseHandler<TEvent, TResult, TContext extends LambdaContext = LambdaContext> = (event: TEvent, context: TContext,) => Promise<TResult> | ||
type MiddyInputHandler< | ||
TEvent, | ||
TResult, | ||
TContext extends LambdaContext = LambdaContext | ||
> = ( | ||
event: TEvent, | ||
context: TContext, | ||
callback: LambdaCallback<TResult> | ||
) => // eslint-disable-next-line @typescript-eslint/no-invalid-void-type | ||
void | Promise<TResult> | TResult | ||
type MiddyInputPromiseHandler< | ||
TEvent, | ||
TResult, | ||
TContext extends LambdaContext = LambdaContext | ||
> = (event: TEvent, context: TContext) => Promise<TResult> | ||
export interface MiddyfiedHandler<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext> extends MiddyInputHandler<TEvent, TResult, TContext>, | ||
export interface MiddyfiedHandler< | ||
TEvent = any, | ||
TResult = any, | ||
TErr = Error, | ||
TContext extends LambdaContext = LambdaContext, | ||
TInternal extends Record<string, unknown> = {} | ||
> extends MiddyInputHandler<TEvent, TResult, TContext>, | ||
MiddyInputPromiseHandler<TEvent, TResult, TContext> { | ||
use: UseFn<TEvent, TResult, TErr, TContext> | ||
before: AttachMiddlewareFn<TEvent, TResult, TErr, TContext> | ||
after: AttachMiddlewareFn<TEvent, TResult, TErr, TContext> | ||
onError: AttachMiddlewareFn<TEvent, TResult, TErr, TContext> | ||
handler: <TAdditional>(handler: MiddlewareHandler<LambdaHandler<TEvent & TAdditional, TResult>, TContext>) => MiddyfiedHandler<TEvent, TResult, TErr, TContext> | ||
use: UseFn<TEvent, TResult, TErr, TContext, TInternal> | ||
before: AttachMiddlewareFn<TEvent, TResult, TErr, TContext, TInternal> | ||
after: AttachMiddlewareFn<TEvent, TResult, TErr, TContext, TInternal> | ||
onError: AttachMiddlewareFn<TEvent, TResult, TErr, TContext, TInternal> | ||
handler: <TAdditional>( | ||
handler: MiddlewareHandler< | ||
LambdaHandler<TEvent & TAdditional, TResult>, | ||
TContext | ||
> | ||
) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal> | ||
} | ||
declare type AttachMiddlewareFn<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext> = | ||
(middleware: MiddlewareFn<TEvent, TResult, TErr, TContext>) => MiddyfiedHandler<TEvent, TResult, TErr, TContext> | ||
declare type AttachMiddlewareFn< | ||
TEvent = any, | ||
TResult = any, | ||
TErr = Error, | ||
TContext extends LambdaContext = LambdaContext, | ||
TInternal extends Record<string, unknown> = {} | ||
> = ( | ||
middleware: MiddlewareFn<TEvent, TResult, TErr, TContext, TInternal> | ||
) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal> | ||
declare type AttachMiddlewareObj<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext> = | ||
(middleware: MiddlewareObj<TEvent, TResult, TErr, TContext>) => MiddyfiedHandler<TEvent, TResult, TErr, TContext> | ||
declare type AttachMiddlewareObj< | ||
TEvent = any, | ||
TResult = any, | ||
TErr = Error, | ||
TContext extends LambdaContext = LambdaContext, | ||
TInternal extends Record<string, unknown> = {} | ||
> = ( | ||
middleware: MiddlewareObj<TEvent, TResult, TErr, TContext, TInternal> | ||
) => MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal> | ||
declare type UseFn<TEvent = any, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext> = | ||
<TMiddleware extends MiddlewareObj<any, any, Error, any>>(middlewares: TMiddleware | TMiddleware[]) => TMiddleware extends MiddlewareObj<infer TMiddlewareEvent, any, Error, infer TMiddlewareContext> | ||
? MiddyfiedHandler<TMiddlewareEvent & TEvent, TResult, TErr, TMiddlewareContext & TContext> // always true | ||
: never | ||
declare type UseFn< | ||
TEvent = any, | ||
TResult = any, | ||
TErr = Error, | ||
TContext extends LambdaContext = LambdaContext, | ||
TInternal extends Record<string, unknown> = {} | ||
> = <TMiddleware extends MiddlewareObj<any, any, Error, any, any>>( | ||
middlewares: TMiddleware | TMiddleware[] | ||
) => TMiddleware extends MiddlewareObj< | ||
infer TMiddlewareEvent, | ||
any, | ||
Error, | ||
infer TMiddlewareContext, | ||
infer TMiddlewareInternal | ||
> | ||
? MiddyfiedHandler< | ||
TMiddlewareEvent & TEvent, | ||
TResult, | ||
TErr, | ||
TMiddlewareContext & TContext, | ||
TMiddlewareInternal & TInternal | ||
> // always true | ||
: never | ||
declare type MiddlewareHandler<THandler extends LambdaHandler<any, any>, TContext extends LambdaContext = LambdaContext> = | ||
THandler extends LambdaHandler<infer TEvent, infer TResult> // always true | ||
? MiddyInputHandler<TEvent, TResult, TContext> | ||
: never | ||
declare type MiddlewareHandler< | ||
THandler extends LambdaHandler<any, any>, | ||
TContext extends LambdaContext = LambdaContext | ||
> = THandler extends LambdaHandler<infer TEvent, infer TResult> // always true | ||
? MiddyInputHandler<TEvent, TResult, TContext> | ||
: never | ||
@@ -81,3 +159,14 @@ /** | ||
*/ | ||
declare function middy<TEvent = unknown, TResult = any, TErr = Error, TContext extends LambdaContext = LambdaContext> (handler?: MiddlewareHandler<LambdaHandler<TEvent, TResult>, TContext>, plugin?: PluginObject): MiddyfiedHandler<TEvent, TResult, TErr, TContext> | ||
declare function middy< | ||
TEvent = unknown, | ||
TResult = any, | ||
TErr = Error, | ||
TContext extends LambdaContext = LambdaContext, | ||
TInternal extends Record<string, unknown> = {} | ||
> ( | ||
handler?: | ||
| MiddlewareHandler<LambdaHandler<TEvent, TResult>, TContext> | ||
| PluginObject, | ||
plugin?: PluginObject | ||
): MiddyfiedHandler<TEvent, TResult, TErr, TContext, TInternal> | ||
@@ -84,0 +173,0 @@ declare namespace middy { |
321
index.js
@@ -1,128 +0,209 @@ | ||
import { setTimeout } from 'node:timers/promises'; | ||
const defaultLambdaHandler = ()=>{}; | ||
/* global awslambda */ | ||
import { Readable } from 'node:stream' | ||
import { pipeline } from 'node:stream/promises' | ||
import { setTimeout } from 'node:timers/promises' | ||
const defaultLambdaHandler = () => {} | ||
const defaultPlugin = { | ||
timeoutEarlyInMillis: 5, | ||
timeoutEarlyResponse: ()=>{ | ||
throw new Error('Timeout'); | ||
timeoutEarlyInMillis: 5, | ||
timeoutEarlyResponse: () => { | ||
const err = new Error('[AbortError]: The operation was aborted.', { | ||
cause: { package: '@middy/core' } | ||
}) | ||
err.name = 'TimeoutError' | ||
throw err | ||
}, | ||
streamifyResponse: false // Deprecate need for this when AWS provides a flag for when it's looking for it | ||
} | ||
const middy = (lambdaHandler = defaultLambdaHandler, plugin = {}) => { | ||
// Allow base handler to be set using .handler() | ||
if (typeof lambdaHandler !== 'function') { | ||
plugin = lambdaHandler | ||
lambdaHandler = defaultLambdaHandler | ||
} | ||
plugin = { ...defaultPlugin, ...plugin } | ||
plugin.timeoutEarly = plugin.timeoutEarlyInMillis > 0 | ||
plugin.beforePrefetch?.() | ||
const beforeMiddlewares = [] | ||
const afterMiddlewares = [] | ||
const onErrorMiddlewares = [] | ||
const middyHandler = (event = {}, context = {}) => { | ||
plugin.requestStart?.() | ||
const request = { | ||
event, | ||
context, | ||
response: undefined, | ||
error: undefined, | ||
internal: plugin.internal ?? {} | ||
} | ||
}; | ||
const middy = (lambdaHandler = defaultLambdaHandler, plugin = {})=>{ | ||
if (typeof lambdaHandler !== 'function') { | ||
plugin = lambdaHandler; | ||
lambdaHandler = defaultLambdaHandler; | ||
return runRequest( | ||
request, | ||
[...beforeMiddlewares], | ||
lambdaHandler, | ||
[...afterMiddlewares], | ||
[...onErrorMiddlewares], | ||
plugin | ||
) | ||
} | ||
const middy = plugin.streamifyResponse | ||
? awslambda.streamifyResponse(async (event, responseStream, context) => { | ||
const handlerResponse = await middyHandler(event, context) | ||
let handlerBody = handlerResponse | ||
if (handlerResponse.statusCode) { | ||
handlerBody = handlerResponse.body ?? '' | ||
responseStream = awslambda.HttpResponseStream.from( | ||
responseStream, | ||
handlerResponse | ||
) | ||
} | ||
// Source @datastream/core (MIT) | ||
let handlerStream | ||
if (handlerBody._readableState) { | ||
handlerStream = handlerBody | ||
} else if (typeof handlerBody === 'string') { | ||
function * iterator (input) { | ||
const size = 16384 // 16 * 1024 // Node.js default | ||
let position = 0 | ||
const length = input.length | ||
while (position < length) { | ||
yield input.substring(position, position + size) | ||
position += size | ||
} | ||
} | ||
handlerStream = Readable.from(iterator(handlerBody)) | ||
} | ||
if (!handlerStream) { | ||
throw new Error('handler response not a ReadableStream') | ||
} | ||
await pipeline(handlerStream, responseStream) | ||
}) | ||
: middyHandler | ||
middy.use = (middlewares) => { | ||
if (!Array.isArray(middlewares)) { | ||
middlewares = [middlewares] | ||
} | ||
plugin = { | ||
...defaultPlugin, | ||
...plugin | ||
}; | ||
plugin.timeoutEarly = plugin.timeoutEarlyInMillis > 0; | ||
plugin.beforePrefetch?.(); | ||
const beforeMiddlewares = []; | ||
const afterMiddlewares = []; | ||
const onErrorMiddlewares = []; | ||
const middy = (event = {}, context = {})=>{ | ||
plugin.requestStart?.(); | ||
const request = { | ||
event, | ||
context, | ||
response: undefined, | ||
error: undefined, | ||
internal: plugin.internal ?? {} | ||
}; | ||
return runRequest(request, [ | ||
...beforeMiddlewares | ||
], lambdaHandler, [ | ||
...afterMiddlewares | ||
], [ | ||
...onErrorMiddlewares | ||
], plugin); | ||
}; | ||
middy.use = (middlewares)=>{ | ||
if (!Array.isArray(middlewares)) { | ||
middlewares = [ | ||
middlewares | ||
]; | ||
} | ||
for (const middleware of middlewares){ | ||
const { before , after , onError } = middleware; | ||
if (!before && !after && !onError) { | ||
throw new Error('Middleware must be an object containing at least one key among "before", "after", "onError"'); | ||
} | ||
if (before) middy.before(before); | ||
if (after) middy.after(after); | ||
if (onError) middy.onError(onError); | ||
} | ||
return middy; | ||
}; | ||
middy.before = (beforeMiddleware)=>{ | ||
beforeMiddlewares.push(beforeMiddleware); | ||
return middy; | ||
}; | ||
middy.after = (afterMiddleware)=>{ | ||
afterMiddlewares.unshift(afterMiddleware); | ||
return middy; | ||
}; | ||
middy.onError = (onErrorMiddleware)=>{ | ||
onErrorMiddlewares.unshift(onErrorMiddleware); | ||
return middy; | ||
}; | ||
middy.handler = (replaceLambdaHandler)=>{ | ||
lambdaHandler = replaceLambdaHandler; | ||
return middy; | ||
}; | ||
return middy; | ||
}; | ||
const runRequest = async (request, beforeMiddlewares, lambdaHandler, afterMiddlewares, onErrorMiddlewares, plugin)=>{ | ||
let timeoutAbort; | ||
const timeoutEarly = plugin.timeoutEarly && request.context.getRemainingTimeInMillis; | ||
for (const middleware of middlewares) { | ||
const { before, after, onError } = middleware | ||
if (!before && !after && !onError) { | ||
throw new Error( | ||
'Middleware must be an object containing at least one key among "before", "after", "onError"' | ||
) | ||
} | ||
if (before) middy.before(before) | ||
if (after) middy.after(after) | ||
if (onError) middy.onError(onError) | ||
} | ||
return middy | ||
} | ||
// Inline Middlewares | ||
middy.before = (beforeMiddleware) => { | ||
beforeMiddlewares.push(beforeMiddleware) | ||
return middy | ||
} | ||
middy.after = (afterMiddleware) => { | ||
afterMiddlewares.unshift(afterMiddleware) | ||
return middy | ||
} | ||
middy.onError = (onErrorMiddleware) => { | ||
onErrorMiddlewares.unshift(onErrorMiddleware) | ||
return middy | ||
} | ||
middy.handler = (replaceLambdaHandler) => { | ||
lambdaHandler = replaceLambdaHandler | ||
return middy | ||
} | ||
return middy | ||
} | ||
const runRequest = async ( | ||
request, | ||
beforeMiddlewares, | ||
lambdaHandler, | ||
afterMiddlewares, | ||
onErrorMiddlewares, | ||
plugin | ||
) => { | ||
let timeoutAbort | ||
const timeoutEarly = | ||
plugin.timeoutEarly && request.context.getRemainingTimeInMillis // disable when AWS context missing (tests, containers) | ||
try { | ||
await runMiddlewares(request, beforeMiddlewares, plugin) | ||
// Check if before stack hasn't exit early | ||
if (typeof request.response === 'undefined') { | ||
plugin.beforeHandler?.() | ||
const handlerAbort = new AbortController() | ||
if (timeoutEarly) timeoutAbort = new AbortController() | ||
request.response = await Promise.race([ | ||
lambdaHandler(request.event, request.context, { | ||
signal: handlerAbort.signal | ||
}), | ||
timeoutEarly | ||
? setTimeout( | ||
request.context.getRemainingTimeInMillis() - | ||
plugin.timeoutEarlyInMillis, | ||
undefined, | ||
{ signal: timeoutAbort.signal } | ||
).then(() => { | ||
handlerAbort.abort() | ||
return plugin.timeoutEarlyResponse() | ||
}) | ||
: Promise.race([]) | ||
]) | ||
timeoutAbort?.abort() // lambdaHandler may not be a promise | ||
plugin.afterHandler?.() | ||
await runMiddlewares(request, afterMiddlewares, plugin) | ||
} | ||
} catch (e) { | ||
timeoutAbort?.abort() // timeout should be aborted on errors | ||
// Reset response changes made by after stack before error thrown | ||
request.response = undefined | ||
request.error = e | ||
try { | ||
await runMiddlewares(request, beforeMiddlewares, plugin); | ||
if (typeof request.response === 'undefined') { | ||
plugin.beforeHandler?.(); | ||
const handlerAbort = new AbortController(); | ||
if (timeoutEarly) timeoutAbort = new AbortController(); | ||
request.response = await Promise.race([ | ||
lambdaHandler(request.event, request.context, { | ||
signal: handlerAbort.signal | ||
}), | ||
timeoutEarly ? setTimeout(request.context.getRemainingTimeInMillis() - plugin.timeoutEarlyInMillis, undefined, { | ||
signal: timeoutAbort.signal | ||
}).then(()=>{ | ||
handlerAbort.abort(); | ||
return plugin.timeoutEarlyResponse(); | ||
}) : Promise.race([]) | ||
]); | ||
timeoutAbort?.abort(); | ||
plugin.afterHandler?.(); | ||
await runMiddlewares(request, afterMiddlewares, plugin); | ||
} | ||
await runMiddlewares(request, onErrorMiddlewares, plugin) | ||
} catch (e) { | ||
timeoutAbort?.abort(); | ||
request.response = undefined; | ||
request.error = e; | ||
try { | ||
await runMiddlewares(request, onErrorMiddlewares, plugin); | ||
} catch (e) { | ||
e.originalError = request.error; | ||
request.error = e; | ||
throw request.error; | ||
} | ||
if (typeof request.response === 'undefined') throw request.error; | ||
} finally{ | ||
await plugin.requestEnd?.(request); | ||
// Save error that wasn't handled | ||
e.originalError = request.error | ||
request.error = e | ||
throw request.error | ||
} | ||
return request.response; | ||
}; | ||
const runMiddlewares = async (request, middlewares, plugin)=>{ | ||
for (const nextMiddleware of middlewares){ | ||
plugin.beforeMiddleware?.(nextMiddleware.name); | ||
const res = await nextMiddleware(request); | ||
plugin.afterMiddleware?.(nextMiddleware.name); | ||
if (typeof res !== 'undefined') { | ||
request.response = res; | ||
return; | ||
} | ||
// Catch if onError stack hasn't handled the error | ||
if (typeof request.response === 'undefined') throw request.error | ||
} finally { | ||
await plugin.requestEnd?.(request) | ||
} | ||
return request.response | ||
} | ||
const runMiddlewares = async (request, middlewares, plugin) => { | ||
for (const nextMiddleware of middlewares) { | ||
plugin.beforeMiddleware?.(nextMiddleware.name) | ||
const res = await nextMiddleware(request) | ||
plugin.afterMiddleware?.(nextMiddleware.name) | ||
// short circuit chaining and respond early | ||
if (typeof res !== 'undefined') { | ||
request.response = res | ||
return | ||
} | ||
}; | ||
export default middy; | ||
} | ||
} | ||
export default middy |
{ | ||
"name": "@middy/core", | ||
"version": "5.0.0-alpha.0", | ||
"version": "5.0.0-alpha.1", | ||
"description": "🛵 The stylish Node.js middleware engine for AWS Lambda (core package)", | ||
@@ -13,3 +13,2 @@ "type": "module", | ||
}, | ||
"main": "./index.cjs", | ||
"module": "./index.js", | ||
@@ -21,6 +20,2 @@ "exports": { | ||
"default": "./index.js" | ||
}, | ||
"require": { | ||
"types": "./index.d.ts", | ||
"default": "./index.cjs" | ||
} | ||
@@ -32,3 +27,2 @@ } | ||
"index.js", | ||
"index.cjs", | ||
"index.d.ts" | ||
@@ -69,5 +63,8 @@ ], | ||
"@types/aws-lambda": "^8.10.76", | ||
"@types/node": "^18.0.0" | ||
"@types/node": "^20.0.0" | ||
}, | ||
"gitHead": "08c35e3dba9efdad0b86666ce206ce302cc65d07" | ||
"gitHead": "ebce8d5df8783077fa49ba62ee9be20e8486a7f1", | ||
"dependencies": { | ||
"@datastream/core": "0.0.35" | ||
} | ||
} |
@@ -22,4 +22,5 @@ <div align="center"> | ||
</a> | ||
<a href="https://lgtm.com/projects/g/middyjs/middy/context:javascript"> | ||
<img src="https://img.shields.io/lgtm/grade/javascript/g/middyjs/middy.svg?logo=lgtm&logoWidth=18" alt="Language grade: JavaScript" style="max-width:100%;"> | ||
<a href="https://github.com/middyjs/middy/actions/workflows/sast.yml"> | ||
<img src="https://github.com/middyjs/middy/actions/workflows/sast.yml/badge.svg | ||
?branch=main&event=push" alt="CodeQL" style="max-width:100%;"> | ||
</a> | ||
@@ -26,0 +27,0 @@ <a href="https://bestpractices.coreinfrastructure.org/projects/5280"> |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
67
17327
1
5
352
1
+ Added@datastream/core@0.0.35
+ Added@datastream/core@0.0.35(transitive)
+ Addedabort-controller@3.0.0(transitive)
+ Addedbase64-js@1.5.1(transitive)
+ Addedbuffer@6.0.3(transitive)
+ Addedcloneable-readable@3.0.0(transitive)
+ Addedevent-target-shim@5.0.1(transitive)
+ Addedevents@3.3.0(transitive)
+ Addedieee754@1.2.1(transitive)
+ Addedprocess@0.11.10(transitive)
+ Addedreadable-stream@4.5.2(transitive)
+ Addedsafe-buffer@5.2.1(transitive)
+ Addedstring_decoder@1.3.0(transitive)