Skip to content

Commit

Permalink
feat(core): refactor to use koa style middlewares
Browse files Browse the repository at this point in the history
  • Loading branch information
nfantone committed Apr 13, 2021
1 parent 471953d commit eb61326
Show file tree
Hide file tree
Showing 12 changed files with 3,512 additions and 17,015 deletions.
8 changes: 8 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"tabWidth": 2,
"printWidth": 100,
"singleQuote": true,
"semi": false,
"arrowParens": "always",
"trailingComma": "none"
}
16,933 changes: 86 additions & 16,847 deletions package-lock.json

Large diffs are not rendered by default.

71 changes: 40 additions & 31 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
{
"name": "middy-monorepo",
"name": "middy",
"version": "2.0.1",
"description": "🛵 The stylish Node.js middleware engine for AWS Lambda",
"type": "commonjs",
"engines": {
"node": ">=12"
"keywords": [
"Lambda",
"Middleware",
"Serverless",
"Framework",
"AWS",
"AWS Lambda"
],
"homepage": "https://middy.js.org",
"bugs": {
"url": "https://github.com/middyjs/middy/issues"
},
"engineStrict": true,
"repository": {
"type": "git",
"url": "git+https://github.com/middyjs/middy.git"
},
"license": "MIT",
"author": {
"name": "Middy contributors",
"url": "https://github.com/middyjs/middy/graphs/contributors"
},
"type": "commonjs",
"scripts": {
"install": "lerna bootstrap",
"test": "npm run test:lint && npm run test:packages",
Expand All @@ -27,47 +44,39 @@
"lerna:sync": "lerna exec --bail --concurrency 2 npm install && lerna publish --yes --skip-npm --skip-git --repo-version $npm_package_version",
"lerna:publish": "lerna publish --yes --skip-git --repo-version $npm_package_version"
},
"repository": {
"type": "git",
"url": "git+https://github.com/middyjs/middy.git"
},
"keywords": [
"Lambda",
"Middleware",
"Serverless",
"Framework",
"AWS",
"AWS Lambda"
],
"author": {
"name": "Middy contributors",
"url": "https://github.com/middyjs/middy/graphs/contributors"
},
"license": "MIT",
"bugs": {
"url": "https://github.com/middyjs/middy/issues"
"lint-staged": {
"*.{js,ts}": [
"npm run test:lint"
]
},
"homepage": "https://middy.js.org",
"devDependencies": {
"@babel/cli": "^7.13.14",
"@babel/core": "^7.13.15",
"@babel/preset-env": "^7.13.15",
"ava": "^3.15.0",
"c8": "^7.7.1",
"eslint": "^7.24.0",
"eslint-config-prettier": "^8.1.0",
"eslint-config-standard": "^16.0.2",
"eslint-import-resolver-node": "^0.3.4",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^3.3.1",
"eslint-plugin-promise": "^5.1.0",
"eslint-plugin-sort-destructure-keys": "^1.3.5",
"eslint-plugin-standard": "^4.1.0",
"husky": "^6.0.0",
"lerna": "^4.0.0",
"lint-staged": "^10.5.4",
"prettier": "2.2.1",
"prettier": "^2.2.1",
"rewire": "5.0.0",
"sinon": "^10.0.1",
"ts-standard": "^10.0.0",
"tsd": "^0.14.0",
"typescript": "^4.2.4"
},
"lint-staged": {
"*.{js,ts}": [
"npm run test:lint"
]
}
"engines": {
"node": ">=12"
},
"engineStrict": true
}
4 changes: 4 additions & 0 deletions packages/core/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"extends": "@middy/eslint-config",
"parser": "@babel/eslint-parser"
}
181 changes: 75 additions & 106 deletions packages/core/index.js
Original file line number Diff line number Diff line change
@@ -1,124 +1,93 @@
const middy = (handler = () => {}, plugin) => {
plugin?.beforePrefetch?.()
const beforeMiddlewares = []
const afterMiddlewares = []
const onErrorMiddlewares = []

const instance = (event = {}, context = {}) => {
plugin?.requestStart?.()
const request = {
event,
context,
response: undefined,
error: undefined,
internal: {}
}
'use strict'

return runRequest(
request,
[...beforeMiddlewares],
handler,
[...afterMiddlewares],
[...onErrorMiddlewares],
plugin
)
const assertIsFunction = (fn, message) => {
if (typeof fn !== 'function') {
throw new TypeError(message)
}
}

function compose(middlewares) {
return function runMiddlewares(request) {
let currentIndex = 0

async function dispatch(index) {
if (index < currentIndex) {
throw new Error('`next` was already called in this middleware')
}

if (index < middlewares.length) {
const fn = middlewares[index]
currentIndex = index

function next() {
return dispatch(index + 1)
}

instance.use = (middlewares) => {
if (Array.isArray(middlewares)) {
for (const middleware of middlewares) {
instance.applyMiddleware(middleware)
return fn(request, next)
}
return instance
}
return instance.applyMiddleware(middlewares)

return dispatch(0)
}
}

instance.applyMiddleware = (middleware) => {
const { before, after, onError } = middleware
function middy() {
let errorHandler
const middlewares = []

if (!before && !after && !onError) {
throw new Error(
'Middleware must an object containing at least one key among "before", "after", "onError"'
)
}
return {
get middlewares() {
return [...middlewares]
},

if (before) instance.before(before)
if (after) instance.after(after)
if (onError) instance.onError(onError)
use(middleware) {
assertIsFunction(middleware, '`middleware` must be a function')
middlewares.push(middleware)
return this
},

return instance
}
error(cb) {
assertIsFunction(cb, '`cb` must be a function')
errorHandler = cb
return this
},

// Inline Middlewares
instance.before = (beforeMiddleware) => {
beforeMiddlewares.push(beforeMiddleware)
return instance
}
instance.after = (afterMiddleware) => {
afterMiddlewares.unshift(afterMiddleware)
return instance
}
instance.onError = (onErrorMiddleware) => {
onErrorMiddlewares.push(onErrorMiddleware)
return instance
}
lambda(handler) {
assertIsFunction(handler, '`handler` must be a function')

instance.__middlewares = {
before: beforeMiddlewares,
after: afterMiddlewares,
onError: onErrorMiddlewares
}
async function handlerMiddleware(request, next) {
request.response = await handler(request.event, request.context)
return next()
}

return instance
}
middlewares.push(handlerMiddleware)
return this
},

const runRequest = async (
request,
beforeMiddlewares,
handler,
afterMiddlewares,
onErrorMiddlewares,
plugin
) => {
try {
await runMiddlewares(request, beforeMiddlewares, plugin)
// Check if before stack hasn't exit early
if (request.response === undefined) {
plugin?.beforeHandler?.()
request.response = await handler(request.event, request.context)
plugin?.afterHandler?.()
await runMiddlewares(request, afterMiddlewares, plugin)
}
} catch (e) {
// Reset response changes made by after stack before error thrown
request.response = undefined
request.error = e
try {
await runMiddlewares(request, onErrorMiddlewares, plugin)
// Catch if onError stack hasn't handled the error
if (request.response === undefined) throw request.error
} catch (e) {
// Save error that wasn't handled
e.originalError = request.error
request.error = e
throw request.error
}
} finally {
await plugin?.requestEnd?.()
}
return request.response
}
handler() {
const middlewaresFn = compose(middlewares)

async function handleEvent(event, context = {}) {
const request = {
event,
context,
response: undefined,
_internal: {}
}

try {
await middlewaresFn(request)
return request.response
} catch (err) {
if (errorHandler) {
return errorHandler(err, request)
}

throw err
}
}

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 (res !== undefined) {
request.response = res
return
return handleEvent
}
}
}
Expand Down
Loading

0 comments on commit eb61326

Please sign in to comment.