@webtask/middleware-compiler
Advanced tools
Comparing version 1.1.1 to 1.2.0
45
index.js
@@ -81,14 +81,10 @@ 'use strict'; | ||
const middlewareSpec = middlewareSpecs[nextMiddlewareIdx]; | ||
const middlewareIdx = nextMiddlewareIdx++; | ||
const invokeMiddlewareFn = middlewareFn => { | ||
debuglog( | ||
'Invoking middleware %d: %s', | ||
middlewareIdx, | ||
middlewareSpec.name || middlewareSpec | ||
); | ||
debuglog( | ||
'Invoking middleware %d: %s', | ||
nextMiddlewareIdx, | ||
middlewareSpec.name || middlewareSpec | ||
); | ||
nextMiddlewareIdx++; | ||
try { | ||
const middlewareFn = Util.resolveCompiler(middlewareSpec); | ||
try { | ||
@@ -105,11 +101,24 @@ return middlewareFn(req, res, invokeNextMiddleware); | ||
} | ||
} catch (e) { | ||
debuglog( | ||
'Error loading middleware "%s": %s', | ||
middlewareSpec, | ||
e.stack || e | ||
); | ||
}; | ||
return respondWithError(e, res); | ||
// The middleware is either automatic (default) or has been cached so we can immediately run it | ||
if (typeof middlewareSpec === 'function') { | ||
return invokeMiddlewareFn(middlewareSpec); | ||
} | ||
return Util.resolveMiddlewareFunction( | ||
middlewareSpec, | ||
{ debuglog, nodejsCompiler }, | ||
(error, middlewareFn) => { | ||
if (error) { | ||
return respondWithError(error, res); | ||
} | ||
// Cache the resulting middleware for the next invocation. | ||
// Util.resolveMiddlewareFunction operates as an identify function for function arguments. | ||
middlewareSpecs[middlewareIdx] = middlewareFn; | ||
return invokeMiddlewareFn(middlewareFn); | ||
} | ||
); | ||
} | ||
@@ -116,0 +125,0 @@ |
117
lib/util.js
'use strict'; | ||
const MIDDLEWARE_SPEC_RX = /^(@[^/(]+\/[^/(]+|[^@/(]+)(?:\/([^/(]+))?$/; | ||
const URL_MIDDLEWARE_MAX_REDIRECTS = 5; | ||
const URL_MIDDLEWARE_RX = /^https?:\/\//; | ||
const URL_MIDDLEWARE_TIMEOUT = 2000; | ||
let Wreck; | ||
module.exports = { | ||
parseMiddlewareSpecString, | ||
resolveCompiler, | ||
resolveMiddlewareFunction, | ||
validateMiddlewareFunction, | ||
}; | ||
@@ -36,14 +42,107 @@ | ||
*/ | ||
function resolveCompiler(spec) { | ||
function resolveMiddlewareFunction(spec, { debuglog, nodejsCompiler }, cb) { | ||
// Already a function, no resolution to do. | ||
if (typeof spec === 'function') return spec; | ||
if (typeof spec !== 'string') { | ||
return process.nextTick(cb, new Error('Unexpected spec type')); | ||
} | ||
const parsedSpec = parseMiddlewareSpecString(spec); | ||
const module = require(parsedSpec.moduleName); | ||
const middlewareFn = parsedSpec.exportName | ||
? module[parsedSpec.exportName]() | ||
: module(); | ||
if (URL_MIDDLEWARE_RX.test(spec)) { | ||
return resolveUrlMiddlewareFunction( | ||
spec, | ||
{ debuglog, nodejsCompiler }, | ||
cb | ||
); | ||
} | ||
return resolveNpmMiddlewareFunction(spec, { debuglog }, cb); | ||
} | ||
/** | ||
* Resolve an npm-based middleware specification into a middleware function | ||
* | ||
* @param {string} spec NPM middleware spec | ||
* @param {object} options Options | ||
* @param {function} cb Callback | ||
*/ | ||
function resolveNpmMiddlewareFunction(spec, { debuglog }, cb) { | ||
debuglog('resolving an npm module middleware "%s"'.spec); | ||
try { | ||
const parsedSpec = parseMiddlewareSpecString(spec); | ||
const module = require(parsedSpec.moduleName); | ||
const middlewareFn = parsedSpec.exportName | ||
? module[parsedSpec.exportName]() | ||
: module(); | ||
return process.nextTick( | ||
cb, | ||
null, | ||
validateMiddlewareFunction(middlewareFn) | ||
); | ||
} catch (error) { | ||
return process.nextTick(cb, error); | ||
} | ||
} | ||
/** | ||
* Resolve a url-based middleware specification into a middleware function | ||
* | ||
* @param {string} url Url of the middleware code | ||
* @param {object} options Options | ||
* @param {function} cb Callback | ||
*/ | ||
function resolveUrlMiddlewareFunction(url, { debuglog, nodejsCompiler }, cb) { | ||
if (!Wreck) Wreck = require('wreck'); | ||
const wreckOptions = { | ||
redirects: URL_MIDDLEWARE_MAX_REDIRECTS, | ||
timeout: URL_MIDDLEWARE_TIMEOUT, | ||
}; | ||
return Wreck.get(url, wreckOptions, (error, response, payload) => { | ||
if (error) { | ||
debuglog( | ||
'Error fetching middleware from "%s": $s', | ||
url, | ||
error.message | ||
); | ||
return cb(error); | ||
} | ||
const middlewareCode = payload.toString('utf-8'); | ||
return nodejsCompiler(middlewareCode, (error, middlewareFn) => { | ||
if (error) { | ||
debuglog('Error compiling webtask code: %s', error.stack); | ||
return cb(error); | ||
} | ||
try { | ||
validateMiddlewareFunction(middlewareFn); | ||
} catch (e) { | ||
debuglog( | ||
'The code at "%s", when compiled, exported an invalid middlware function: %s', | ||
url, | ||
e.message | ||
); | ||
return cb(e); | ||
} | ||
return cb(null, middlewareFn); | ||
}); | ||
}); | ||
} | ||
/** | ||
* Validate a middleware function | ||
* | ||
* @param {function} middlewareFn A middleware function to be validated | ||
*/ | ||
function validateMiddlewareFunction(middlewareFn) { | ||
if (typeof middlewareFn !== 'function' || middlewareFn.length !== 3) { | ||
throw new Error('A Webtask middleware must export a function that returns a function with the signature `function(req, res, next)`'); | ||
throw new Error( | ||
'A Webtask middleware must export a function that returns a function with the signature `function(req, res, next)`' | ||
); | ||
} | ||
@@ -50,0 +149,0 @@ |
{ | ||
"name": "@webtask/middleware-compiler", | ||
"version": "1.1.1", | ||
"version": "1.2.0", | ||
"description": "Webtask compiler that provides a configurable middleware pipeline", | ||
@@ -10,3 +10,3 @@ "main": "index.js", | ||
"scripts": { | ||
"test": "lab -vL" | ||
"test": "lab -cvL -t 90" | ||
}, | ||
@@ -20,4 +20,7 @@ "keywords": [], | ||
"devDependencies": { | ||
"lab": "^14.2.0" | ||
"@webtask/bearer-auth-middleware": "^1.1.1", | ||
"body-parser": "^1.17.2", | ||
"lab": "^14.2.0", | ||
"shot": "^3.4.2" | ||
} | ||
} |
@@ -19,3 +19,6 @@ # Middleware compiler | ||
3. *Optionally*, set the `wt-middleware` metadata property to a comma-separated list of middleware references. These references can be the name of an npm module, in which case the module's default export is used. These can also be references like `module_name/name_of_export_function`, which would be equivalent to `require('module_name').name_of_export_function`. These middleware will be invoked sequentially and the next middleware will only be invoked if the previous middleware calls `next()` without argument. | ||
3. *Optionally*, set the `wt-middleware` metadata property to a comma-separated list of middleware references. Middleware references can be any combination of: | ||
- `module_name` - The name of an npm module, in which case the module's default export is used. This would be equivalent to `require('module_name')`. | ||
- `module_name/name_of_export_function` - The name of an npm module with the name of the desired export. This wouldbe equivalent to `require('module_name').name_of_export_function`. | ||
- `http(s)://url.of/a/file/exporting/a/middleware.js` - A publicly accessible url from which the middleware's code will be downloaded and evaluated. The code must export a suitable middleware factory function as the default export. | ||
@@ -22,0 +25,0 @@ 3. *Optionally*, set the `wt-debug` metadata property to a comma-separated list of debug references that contains `wt-middleware`. This will result in additional debug information being sent to real-time logs. |
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
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 2 instances in 1 package
46883
11
1085
48
4
4
2