Skip to content

Commit 2872620

Browse files
committed
Initial commit
0 parents  commit 2872620

File tree

11 files changed

+5993
-0
lines changed

11 files changed

+5993
-0
lines changed

.editorconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
root = true
2+
3+
[*]
4+
indent_style = space
5+
indent_size = 2
6+
end_of_line = lf
7+
charset = utf-8
8+
trim_trailing_whitespace = true
9+
insert_final_newline = true

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* text=auto

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.DS_Store
2+
*.log
3+
.idea
4+
node_modules
5+
coverage
6+
.nyc_output

.remarkignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test/snapshots/**/*.md

.travis.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
language: node_js
2+
node_js:
3+
- '8'
4+
after_success:
5+
npm run coverage

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2017 Nick Baugh <niftylettuce@gmail.com> (http://niftylettuce.com/)
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# koa-better-timeout
2+
3+
[![build status](https://img.shields.io/travis/ladjs/koa-better-timeout.svg)](https://travis-ci.org/ladjs/koa-better-timeout)
4+
[![code coverage](https://img.shields.io/codecov/c/github/ladjs/koa-better-timeout.svg)](https://codecov.io/gh/ladjs/koa-better-timeout)
5+
[![code style](https://img.shields.io/badge/code_style-XO-5ed9c7.svg)](https://github.com/sindresorhus/xo)
6+
[![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
7+
[![made with lass](https://img.shields.io/badge/made_with-lass-95CC28.svg)](https://lass.js.org)
8+
[![license](https://img.shields.io/github/license/ladjs/koa-better-timeout.svg)](<>)
9+
10+
> Response timeout middleware for Koa and Lad (uses Boom by default)
11+
12+
13+
## Table of Contents
14+
15+
* [Install](#install)
16+
* [Usage](#usage)
17+
* [Options](#options)
18+
* [Contributors](#contributors)
19+
* [License](#license)
20+
21+
22+
## Install
23+
24+
[npm][]:
25+
26+
```sh
27+
npm install koa-better-timeout
28+
```
29+
30+
[yarn][]:
31+
32+
```sh
33+
yarn add koa-better-timeout
34+
```
35+
36+
37+
## Usage
38+
39+
> Default middleware usage:
40+
41+
```js
42+
const Timeout = require('koa-better-timeout');
43+
44+
// ...
45+
46+
app.use(new Timeout().middleware);
47+
```
48+
49+
> Advanced middleware usage (e.g. using [Lad][] and its [@ladjs/i18n][ladjs-i18n] middleware) with translated response message:
50+
51+
```js
52+
const Timeout = require('koa-better-timeout');
53+
54+
// ...
55+
56+
app.use(async (ctx, next) => {
57+
try {
58+
const timeout = new Timeout({
59+
message: ctx.translate('REQUEST_TIMED_OUT')
60+
});
61+
await timeout.middleware(ctx, next);
62+
} catch (err) {
63+
ctx.throw(err);
64+
}
65+
});
66+
```
67+
68+
69+
## Options
70+
71+
You can optinally pass an object of options to `Timeout(opts)`.
72+
73+
The default option values use [Boom][] and are:
74+
75+
```js
76+
{
77+
ms: 6000,
78+
message: Boom.clientTimeout().message,
79+
sendResponse: Boom.clientTimeout
80+
}
81+
```
82+
83+
84+
## Contributors
85+
86+
| Name | Website |
87+
| -------------- | -------------------------- |
88+
| **Nick Baugh** | <http://niftylettuce.com/> |
89+
90+
91+
## License
92+
93+
[MIT](LICENSE) © [Nick Baugh](http://niftylettuce.com/)
94+
95+
96+
##
97+
98+
[npm]: https://www.npmjs.com/
99+
100+
[yarn]: https://yarnpkg.com/
101+
102+
[lad]: https://lad.js.org
103+
104+
[boom]: https://github.com/hapijs/boom
105+
106+
[ladjs-i18n]: https://github.com/ladjs/i18n

index.js

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
const Boom = require('boom');
2+
3+
// inspired by `koa-timeout`
4+
// and refactored for async
5+
6+
class Timeout {
7+
constructor(config) {
8+
this.config = Object.assign(
9+
{
10+
ms: 6000,
11+
message: Boom.clientTimeout().message,
12+
sendResponse: Boom.clientTimeout
13+
},
14+
config
15+
);
16+
if (typeof this.config.ms !== 'number')
17+
throw new Error('timeout `ms` was not a number');
18+
if (typeof this.config.message !== 'string')
19+
throw new Error('timeout `message` was not a string');
20+
if (typeof this.config.sendResponse !== 'function')
21+
throw new Error('timeout `sendResponse` function is missing');
22+
}
23+
middleware(ctx, next) {
24+
ctx.req._timeout = null;
25+
26+
return Promise.race([
27+
new Promise((resolve, reject) => {
28+
ctx.req._timeout = setTimeout(() => {
29+
reject(this.config.sendResponse(this.config.message));
30+
}, this.config.ms);
31+
}),
32+
new Promise(async (resolve, reject) => {
33+
try {
34+
await next();
35+
clearTimeout(ctx.req._timeout);
36+
resolve();
37+
} catch (err) {
38+
clearTimeout(ctx.req._timeout);
39+
reject(err);
40+
}
41+
})
42+
]);
43+
}
44+
}
45+
46+
module.exports = Timeout;

package.json

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
{
2+
"name": "koa-better-timeout",
3+
"description":
4+
"Response timeout middleware for Koa and Lad (uses Boom by default)",
5+
"version": "0.0.1",
6+
"author": "Nick Baugh <niftylettuce@gmail.com> (http://niftylettuce.com/)",
7+
"bugs": {
8+
"url": "https://github.com/ladjs/koa-better-timeout/issues",
9+
"email": "niftylettuce@gmail.com"
10+
},
11+
"contributors": [
12+
"Nick Baugh <niftylettuce@gmail.com> (http://niftylettuce.com/)"
13+
],
14+
"dependencies": {
15+
"boom": "^5.2.0"
16+
},
17+
"devDependencies": {
18+
"ava": "^0.22.0",
19+
"codecov": "^2.3.0",
20+
"cross-env": "^5.0.5",
21+
"eslint": "^4.5.0",
22+
"eslint-config-prettier": "^2.3.0",
23+
"eslint-plugin-prettier": "^2.2.0",
24+
"husky": "^0.14.3",
25+
"lint-staged": "^4.0.4",
26+
"nyc": "^11.1.0",
27+
"prettier": "^1.6.1",
28+
"remark-cli": "^4.0.0",
29+
"remark-preset-github": "^0.0.6",
30+
"xo": "^0.19.0"
31+
},
32+
"engines": {
33+
"node": ">=8.3"
34+
},
35+
"homepage": "https://github.com/ladjs/koa-better-timeout",
36+
"keywords": [
37+
"lad",
38+
"lass",
39+
"timeout",
40+
"koa",
41+
"v2",
42+
"next",
43+
"response",
44+
"handler",
45+
"lag",
46+
"delay",
47+
"settimeout"
48+
],
49+
"license": "MIT",
50+
"lint-staged": {
51+
"*.{js,jsx,mjs,ts,tsx,css,less,scss,json,graphql}": [
52+
"prettier --write --single-quote --trailing-comma none",
53+
"git add"
54+
],
55+
"*.md": ["remark . -qfo", "git add"]
56+
},
57+
"main": "index.js",
58+
"nyc": {
59+
"check-coverage": true,
60+
"lines": 100,
61+
"functions": 100,
62+
"branches": 100,
63+
"reporter": ["lcov", "html", "text"]
64+
},
65+
"remarkConfig": {
66+
"plugins": ["preset-github"]
67+
},
68+
"repository": {
69+
"type": "git",
70+
"url": "https://github.com/ladjs/koa-better-timeout"
71+
},
72+
"scripts": {
73+
"coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
74+
"lint": "xo && remark . -qfo",
75+
"precommit": "lint-staged && npm test",
76+
"test": "npm run lint && npm run test-coverage",
77+
"test-coverage": "cross-env NODE_ENV=test nyc ava"
78+
},
79+
"xo": {
80+
"extends": "prettier",
81+
"plugins": ["prettier"],
82+
"parserOptions": {
83+
"sourceType": "script"
84+
},
85+
"rules": {
86+
"prettier/prettier": [
87+
"error",
88+
{
89+
"singleQuote": true,
90+
"bracketSpacing": true,
91+
"trailingComma": "none"
92+
}
93+
],
94+
"max-len": [
95+
"error",
96+
{
97+
"code": 80,
98+
"ignoreUrls": true
99+
}
100+
],
101+
"capitalized-comments": "off",
102+
"camelcase": "off",
103+
"no-warning-comments": "off"
104+
},
105+
"space": true
106+
}
107+
}

test/test.js

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
const test = require('ava');
2+
const Boom = require('boom');
3+
4+
const Timeout = require('../');
5+
6+
const next = () => {
7+
return new Promise(resolve => {
8+
resolve();
9+
});
10+
};
11+
12+
test('returns itself', t => {
13+
t.true(new Timeout() instanceof Timeout);
14+
});
15+
16+
test('sets a default config object', t => {
17+
const timeout = new Timeout();
18+
t.is(timeout.config.ms, 6000);
19+
t.is(timeout.config.message, Boom.clientTimeout().message);
20+
t.is(timeout.config.sendResponse, Boom.clientTimeout);
21+
});
22+
23+
test('times out and sends response', async t => {
24+
const timeout = new Timeout();
25+
const ctx = { req: {} };
26+
const error = await t.throws(
27+
timeout.middleware(ctx, () => {
28+
return new Promise(resolve => {
29+
setTimeout(resolve, 7000);
30+
});
31+
})
32+
);
33+
t.true(ctx.req._timeout._called);
34+
t.is(error.message, Boom.clientTimeout().message);
35+
});
36+
37+
test('clears timeout after response', async t => {
38+
const timeout = new Timeout();
39+
const ctx = { req: {} };
40+
await timeout.middleware(ctx, next);
41+
t.false(ctx.req._timeout._called);
42+
});
43+
44+
test('clears timeout after error', async t => {
45+
const timeout = new Timeout();
46+
const ctx = { req: {} };
47+
const error = await t.throws(
48+
timeout.middleware(ctx, () => {
49+
return Promise.reject(new Error('done'));
50+
})
51+
);
52+
t.false(ctx.req._timeout._called);
53+
t.is(error.message, 'done');
54+
});
55+
56+
test('throws error if ms not a number', t => {
57+
const error = t.throws(() => {
58+
// eslint-disable-next-line no-new
59+
new Timeout({ ms: '' });
60+
});
61+
t.is(error.message, 'timeout `ms` was not a number');
62+
});
63+
64+
test('throws error if message not a string', t => {
65+
const error = t.throws(() => {
66+
// eslint-disable-next-line no-new
67+
new Timeout({ message: false });
68+
});
69+
t.is(error.message, 'timeout `message` was not a string');
70+
});
71+
72+
test('throws error if sendResponse not a function', t => {
73+
const error = t.throws(() => {
74+
// eslint-disable-next-line no-new
75+
new Timeout({ sendResponse: '' });
76+
});
77+
t.is(error.message, 'timeout `sendResponse` function is missing');
78+
});

0 commit comments

Comments
 (0)