Socket
Socket
Sign inDemoInstall

throwback

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

throwback - npm Package Compare versions

Comparing version 2.0.1 to 3.0.0

40

dist/index.d.ts

@@ -1,24 +0,20 @@

export declare type NextFunction<R> = () => Promise<R>;
export declare type Middleware5<T1, T2, T3, T4, T5, R> = (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, next: NextFunction<R>) => R | Promise<R>;
export declare type Middleware4<T1, T2, T3, T4, R> = (arg1: T1, arg2: T2, arg3: T3, arg4: T4, next: NextFunction<R>) => R | Promise<R>;
export declare type Middleware3<T1, T2, T3, R> = (arg1: T1, arg2: T2, arg3: T3, next: NextFunction<R>) => R | Promise<R>;
export declare type Middleware2<T1, T2, R> = (arg1: T1, arg2: T2, next: NextFunction<R>) => R | Promise<R>;
export declare type Middleware1<T1, R> = (arg1: T1, next: NextFunction<R>) => R | Promise<R>;
export declare type Middleware0<R> = (next: NextFunction<R>) => R | Promise<R>;
export declare type Middleware<R> = (...args: any[]) => R | Promise<R>;
export declare type OutFunction5<T1, T2, T3, T4, T5, R> = (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, next: Middleware5<T1, T2, T3, T4, T5, R>) => Promise<R>;
export declare type OutFunction4<T1, T2, T3, T4, R> = (arg1: T1, arg2: T2, arg3: T3, arg4: T4, next: Middleware4<T1, T2, T3, T4, R>) => Promise<R>;
export declare type OutFunction3<T1, T2, T3, R> = (arg1: T1, arg2: T2, arg3: T3, next: Middleware3<T1, T2, T3, R>) => Promise<R>;
export declare type OutFunction2<T1, T2, R> = (arg1: T1, arg2: T2, next: Middleware2<T1, T2, R>) => Promise<R>;
export declare type OutFunction1<T1, R> = (arg1: T1, next: Middleware1<T1, R>) => Promise<R>;
export declare type OutFunction0<R> = (next: Middleware0<R>) => Promise<R>;
export declare type OutFunction<R> = (...args: any[]) => Promise<R>;
/**
* Compose an array of middleware functions into a chain.
* Next function supports optional `ctx` replacement for following middleware.
*/
export declare function compose<R>(middleware: Array<Middleware0<R>>): OutFunction0<R>;
export declare function compose<T1, R>(middleware: Array<Middleware1<T1, R>>): OutFunction1<T1, R>;
export declare function compose<T1, T2, R>(middleware: Array<Middleware2<T1, T2, R>>): OutFunction2<T1, T2, R>;
export declare function compose<T1, T2, T3, R>(middleware: Array<Middleware3<T1, T2, T3, R>>): OutFunction3<T1, T2, T3, R>;
export declare function compose<T1, T2, T3, T4, R>(middleware: Array<Middleware4<T1, T2, T3, T4, R>>): OutFunction4<T1, T2, T3, T4, R>;
export declare function compose<T1, T2, T3, T4, T5, R>(middleware: Array<Middleware5<T1, T2, T3, T4, T5, R>>): OutFunction5<T1, T2, T3, T4, T5, R>;
export declare type Next<T, U> = (ctx?: T) => Promise<U>;
/**
* Middleware function pattern.
*/
export declare type Middleware<T, U> = (ctx: T, next: Next<T, U>) => U | Promise<U>;
/**
* Final function has no `next()`.
*/
export declare type Done<T, U> = (ctx: T) => U | Promise<U>;
/**
* Composed function signature.
*/
export declare type Composed<T, U> = (ctx: T, done: Done<T, U>) => Promise<U>;
/**
* Compose an array of middleware functions into a single function.
*/
export declare function compose<T, U>(middleware: Array<Middleware<T, U>>, debug?: boolean): Composed<T, U>;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function compose(middleware) {
/**
* Default to "debug" mode when in development.
*/
const debugMode = process.env.NODE_ENV !== 'production';
/**
* Debug mode wrapper for middleware functions.
*/
function debugMiddleware(middleware) {
if (!Array.isArray(middleware)) {
throw new TypeError("Expected middleware to be an array, got " + typeof middleware);
throw new TypeError(`Expected middleware to be an array, got ${typeof middleware}`);
}
for (var _i = 0, middleware_1 = middleware; _i < middleware_1.length; _i++) {
var fn = middleware_1[_i];
if (typeof fn !== 'function') {
throw new TypeError("Expected middleware to contain functions, got " + typeof fn);
for (const fn of middleware) {
if (typeof fn !== 'function') { // tslint:disable-line
throw new TypeError(`Expected middleware to contain functions, but got ${typeof fn}`);
}
}
return function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
return function debugComposed(ctx, done) {
if (typeof done !== 'function') { // tslint:disable-line
throw new TypeError(`Expected the last argument to be \`done(ctx)\`, but got ${typeof done}`);
}
var index = -1;
var done = args.pop();
if (typeof done !== 'function') {
throw new TypeError("Expected the last argument to be `next()`, got " + typeof done);
}
function dispatch(pos) {
if (pos <= index) {
throw new TypeError('`next()` called multiple times');
function dispatcher(startIndex, ctx) {
let index = startIndex;
function dispatch(pos) {
const fn = middleware[pos] || done;
if (pos > middleware.length) {
throw new TypeError('Composed `done(ctx)` function should not call `next()`');
}
index = pos;
return new Promise(resolve => {
const result = fn(ctx, function next(newCtx) {
if (index > pos)
throw new TypeError('`next()` called multiple times');
if (newCtx === undefined)
return dispatch(pos + 1);
return dispatcher(pos + 1, newCtx);
});
if (result === undefined) { // tslint:disable-line
throw new TypeError('Expected middleware to return `next()` or a value');
}
return resolve(result);
});
}
index = pos;
var fn = middleware[pos] || done;
return new Promise(function (resolve) {
return resolve(fn.apply(void 0, args.concat([function next() {
return dispatch(pos + 1);
}])));
});
return dispatch(startIndex);
}
return dispatch(0);
return dispatcher(0, ctx);
};
}
/**
* Production-mode middleware composition (no errors thrown).
*/
function composeMiddleware(middleware) {
function dispatch(pos, ctx, done) {
const fn = middleware[pos];
if (!fn)
return new Promise(resolve => resolve(done(ctx)));
return new Promise(resolve => {
return resolve(fn(ctx, function next(newCtx) {
return dispatch(pos + 1, newCtx === undefined ? ctx : newCtx, done);
}));
});
}
return function composed(ctx, done) {
return dispatch(0, ctx, done);
};
}
/**
* Compose an array of middleware functions into a single function.
*/
function compose(middleware, debug = debugMode) {
return debug ? debugMiddleware(middleware) : composeMiddleware(middleware);
}
exports.compose = compose;
//# sourceMappingURL=index.js.map
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
var test = require("blue-tape");
var index_1 = require("./index");
test('async middleware', function (t) {
t.test('middleware', function (t) {
var arr = [];
var fn = index_1.compose([
function (req, res, next) {
arr.push(1);
return next().then(function (value) {
arr.push(5);
t.equal(value, 'propagate');
return 'done';
});
},
function (req, res, next) {
arr.push(2);
return next().then(function (value) {
arr.push(4);
t.equal(value, 'hello');
return 'propagate';
});
}
]);
return fn({}, {}, function () {
arr.push(3);
return 'hello';
})
.then(function () {
t.deepEqual(arr, [1, 2, 3, 4, 5]);
const index_1 = require("./index");
describe('throwback', () => {
// Run tests on each code path.
runTests(false);
runTests(true);
describe('debug mode', () => {
it('should select debug mode based on node env by default', () => {
const fn = index_1.compose([]);
const expectedName = process.env.NODE_ENV !== 'production' ? 'debugComposed' : 'composed';
expect(fn.name).toEqual(expectedName);
});
});
t.test('branch middleware by composing', function (t) {
var arr = [];
var fn = index_1.compose([
index_1.compose([
describe('debug errors', () => {
it('throw when input is not an array', () => {
expect(() => index_1.compose('test', true)).toThrow('Expected middleware to be an array, got string');
});
it('throw when values are not functions', () => {
expect(() => index_1.compose([1, 2, 3], true)).toThrow('Expected middleware to contain functions, but got number');
});
it('throw when next is not a function', () => {
const fn = index_1.compose([], true);
expect(() => fn(true)).toThrow('Expected the last argument to be `done(ctx)`, but got undefined');
});
it('throw when calling next() multiple times', () => __awaiter(this, void 0, void 0, function* () {
const fn = index_1.compose([
function (value, next) {
return next().then(() => next());
}
], true);
yield expect(fn({}, () => Promise.resolve())).rejects.toEqual(new Error('`next()` called multiple times'));
}));
it('should throw if final function attempts to call `next()`', () => __awaiter(this, void 0, void 0, function* () {
const fn = index_1.compose([], true);
yield expect(fn({}, ((ctx, next) => next()))).rejects.toEqual(new TypeError('Composed `done(ctx)` function should not call `next()`'));
}));
it('should throw if function returns `undefined`', () => __awaiter(this, void 0, void 0, function* () {
const fn = index_1.compose([
function (ctx) { }
], true);
yield expect(fn(true, () => Promise.resolve())).rejects.toEqual(new TypeError('Expected middleware to return `next()` or a value'));
}));
});
});
/**
* Execute tests in each "mode".
*/
function runTests(debugMode) {
describe(`compose middleware with debug mode: ${debugMode}`, () => {
it('should compose middleware functions', () => __awaiter(this, void 0, void 0, function* () {
const arr = [];
const fn = index_1.compose([
function (ctx, next) {
arr.push(1);
return next().catch(function () {
arr.push(3);
return next().then(value => {
arr.push(5);
expect(value).toEqual('propagate');
return 'done';
});

@@ -46,41 +72,79 @@ },

arr.push(2);
return Promise.reject(new Error('Boom!'));
return next().then(value => {
arr.push(4);
expect(value).toEqual('hello');
return 'propagate';
});
}
]),
function (ctx, next) {
arr.push(4);
return next();
], debugMode);
yield fn({}, () => {
arr.push(3);
return 'hello';
});
expect(arr).toEqual([1, 2, 3, 4, 5]);
}));
it('branch middleware by composing', () => __awaiter(this, void 0, void 0, function* () {
const arr = [];
const fn = index_1.compose([
index_1.compose([
function (ctx, next) {
arr.push(1);
return next().catch(() => {
arr.push(3);
});
},
function (ctx, next) {
arr.push(2);
return Promise.reject(new Error('Boom!'));
}
], debugMode),
function (ctx, next) {
arr.push(4);
return next();
}
], debugMode);
yield fn({}, () => undefined);
expect(arr).toEqual([1, 2, 3]);
}));
it('should compose multiple layers', () => __awaiter(this, void 0, void 0, function* () {
const arr = [];
function middleware(n, next) {
arr.push(n);
return next(n + 1);
}
]);
return fn({}, function () { return undefined; })
.then(function () {
t.deepEqual(arr, [1, 2, 3]);
});
const fn = index_1.compose([
middleware,
index_1.compose([
index_1.compose([
middleware,
middleware
], debugMode),
middleware
], debugMode),
index_1.compose([
middleware
], debugMode)
], debugMode);
const res = yield fn(0, ctx => ctx);
expect(res).toEqual(5);
expect(arr).toEqual([0, 1, 2, 3, 4]);
}));
it('should replace context object', () => __awaiter(this, void 0, void 0, function* () {
const fn = index_1.compose([
function (ctx, next) {
return __awaiter(this, void 0, void 0, function* () {
expect(ctx.original).toBe(true);
const res = yield next();
expect(ctx.original).toBe(true);
return res;
});
},
function (ctx, next) {
return next({ original: false });
}
], debugMode);
expect(yield fn({ original: true }, ctx => ctx.original)).toEqual(false);
}));
});
t.test('throw when input is not an array', function (t) {
t.throws(function () { return index_1.compose('test'); }, 'Expected middleware to be an array, got string');
t.end();
});
t.test('throw when values are not functions', function (t) {
t.throws(function () { return index_1.compose([1, 2, 3]); }, 'Expected middleware to contain functions, got number');
t.end();
});
t.test('throw when next is not a function', function (t) {
var fn = index_1.compose([]);
t.throws(function () { return fn(true); }, 'Expected the last argument to be `next()`, got boolean');
t.end();
});
t.test('throw when calling next() multiple times', function (t) {
var fn = index_1.compose([
function (value, next) {
return next().then(next);
}
]);
t.plan(1);
return fn({}, function () { return undefined; })
.catch(function (err) {
t.equal(err.message, '`next()` called multiple times');
});
});
});
}
//# sourceMappingURL=index.spec.js.map
{
"name": "throwback",
"version": "2.0.1",
"description": "An asynchronous middleware pattern",
"version": "3.0.0",
"description": "Simple asynchronous middleware pattern",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
"files": [
"dist/",
"README.md",
"LICENSE"
"dist/"
],
"scripts": {
"lint": "tslint \"src/**/*.ts\"",
"build": "rm -rf dist && tsc",
"test-spec": "blue-tape 'dist/**/*.spec.js' | tap-spec",
"test-cov": "istanbul cover --print none -x '*.spec.js' node_modules/blue-tape/bin/blue-tape.js -- 'dist/**/*.spec.js' | tap-spec",
"test": "npm run build && npm run lint && npm run test-cov",
"prepublish": "typings install && npm run build"
"lint": "tslint \"src/**/*.ts\" --project tsconfig.json",
"build": "rimraf dist && tsc",
"specs": "jest --coverage",
"test": "npm run build && npm run lint && npm run specs",
"prepublish": "npm run build"
},

@@ -42,13 +39,29 @@ "repository": {

"homepage": "https://github.com/serviejs/throwback",
"jest": {
"roots": [
"<rootDir>/src/"
],
"transform": {
"\\.tsx?$": "ts-jest"
},
"testRegex": "(/__tests__/.*|\\.(test|spec))\\.(tsx?|jsx?)$",
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
]
},
"devDependencies": {
"blue-tape": "^1.0.0",
"bluebird": "^3.3.5",
"chai": "^3.2.0",
"istanbul": "^0.4.4",
"tap-spec": "^4.1.1",
"tslint": "^4.3.1",
"tslint-config-standard": "^2.0.0",
"typescript": "^2.0.3",
"typings": "^2.1.0"
"@types/jest": "^22.2.3",
"@types/node": "^10.1.2",
"jest": "^22.4.4",
"rimraf": "^2.6.2",
"ts-jest": "^22.4.6",
"tslint": "^5.10.0",
"tslint-config-standard": "^7.0.0",
"typescript": "^2.8.3"
}
}

@@ -8,7 +8,7 @@ # Throwback

> An asynchronous middleware pattern.
> Simple asynchronous middleware pattern.
## Installation
```sh
```
npm install throwback --save

@@ -19,9 +19,9 @@ ```

Compose asynchronous functions (promise-based)
Compose asynchronous (promise-returning) functions.
```js
import { compose } from 'throwback'
const { compose } = require('throwback')
const fn = compose([
async function (req, res, next) {
async function (ctx, next) {
console.log(1)

@@ -37,3 +37,3 @@

},
async function (req, res, next) {
async function (ctx, next) {
console.log(2)

@@ -47,12 +47,62 @@

// the middleware bubbles back to the beginning.
fn({}, {}, function (req, res) {
fn({}, function (ctx) {
console.log(3)
res.status = 404
ctx.status = 404
})
```
**Tip:** In development mode, `debug` mode will throw errors when you do something unexpected. In production, faster non-error code paths are used.
### Example
Build a micro HTTP server!
```js
const { createServer } = require('http')
const finalhandler = require('finalhandler') // Example only, not compatible with single `ctx` arg.
const { compose } = require('throwback')
const app = compose([
function ({ req, res }, next) {
res.end('Hello world!')
}
])
createServer(function (req, res) {
return app({ req, res }, finalhandler())
}).listen(3000)
```
### Advanced
Did you know `next(ctx?)` accepts an optional `ctx` argument which will override `ctx` for all following middleware functions? This enables advanced functionality such as `Request` cloning and retries in [`popsicle`](https://github.com/serviejs/popsicle).
```js
async function retryRequest (req, next) {
let retries = 5
while (retries--) {
try {
const res = await next(req.clone())
return res
} catch (e) {
continue
}
}
throw new Error('Retry limit exceeded')
}
```
## Use Cases
* HTTP requests (e.g. [`popsicle`](https://github.com/serviejs/popsicle))
* HTTP servers (e.g. [`servie`](https://github.com/serviejs/servie))
* Processing pipelines (e.g. [`scrappy`](https://github.com/blakeembrey/node-scrappy))
## Inspiration
Built for [`popsicle`](https://github.com/blakeembrey/popsicle) and inspired by [`koa-compose`](https://github.com/koajs/compose).
Built for [`servie`](https://github.com/serviejs) and inspired by [`koa-compose`](https://github.com/koajs/compose).

@@ -59,0 +109,0 @@ ## License

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc