koa 2.2.0 → 2.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/History.md +51 -0
- package/LICENSE +1 -1
- package/Readme.md +21 -12
- package/lib/application.js +18 -7
- package/lib/context.js +6 -3
- package/lib/request.js +53 -9
- package/lib/response.js +10 -5
- package/package.json +24 -14
package/History.md
CHANGED
|
@@ -1,4 +1,55 @@
|
|
|
1
1
|
|
|
2
|
+
2.5.1 / 2018-04-27
|
|
3
|
+
==================
|
|
4
|
+
|
|
5
|
+
* test: node v10 on travis (#1182)
|
|
6
|
+
* fix tests: remove unnecessary assert doesNotThrow and api calls (#1170)
|
|
7
|
+
* use this.response insteadof this.ctx.response (#1163)
|
|
8
|
+
* deps: remove istanbul (#1151)
|
|
9
|
+
* Update guide.md (#1150)
|
|
10
|
+
|
|
11
|
+
2.5.0 / 2018-02-11
|
|
12
|
+
==================
|
|
13
|
+
|
|
14
|
+
* feat: ignore set header/status when header sent (#1137)
|
|
15
|
+
* run coverage using --runInBand (#1141)
|
|
16
|
+
* [Update] license year to 2018 (#1130)
|
|
17
|
+
* docs: small grammatical fix in api docs index (#1111)
|
|
18
|
+
* docs: fixed typo (#1112)
|
|
19
|
+
* docs: capitalize K in word koa (#1126)
|
|
20
|
+
* Error handling: on non-error throw try to stringify if error is an object (#1113)
|
|
21
|
+
* Use eslint-config-koa (#1105)
|
|
22
|
+
* Update mgol's name in AUTHORS, add .mailmap (#1100)
|
|
23
|
+
* Avoid generating package locks instead of ignoring them (#1108)
|
|
24
|
+
* chore: update copyright year to 2017 (#1095)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
2.4.1 / 2017-11-06
|
|
28
|
+
==================
|
|
29
|
+
|
|
30
|
+
* fix bad merge w/ 2.4.0
|
|
31
|
+
|
|
32
|
+
2.4.0 / 2017-11-06
|
|
33
|
+
==================
|
|
34
|
+
|
|
35
|
+
UNPUBLISHED
|
|
36
|
+
|
|
37
|
+
* update `package.engines.node` to be more strict
|
|
38
|
+
* update `fresh@^0.5.2`
|
|
39
|
+
* fix: `inspect()` no longer crashes `context`
|
|
40
|
+
* fix: gated `res.statusMessage` for HTTP/2
|
|
41
|
+
* added: `app.handleRequest()` is exposed
|
|
42
|
+
|
|
43
|
+
2.3.0 / 2017-06-20
|
|
44
|
+
==================
|
|
45
|
+
|
|
46
|
+
* fix: use `Buffer.from()`
|
|
47
|
+
* test on node 7 & 8
|
|
48
|
+
* add `package-lock.json` to `.gitignore`
|
|
49
|
+
* run `lint --fix`
|
|
50
|
+
* add `request.header` in addition to `request.headers`
|
|
51
|
+
* add IPv6 hostname support
|
|
52
|
+
|
|
2
53
|
2.2.0 / 2017-03-14
|
|
3
54
|
==================
|
|
4
55
|
|
package/LICENSE
CHANGED
package/Readme.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
<img src="
|
|
1
|
+
<img src="/docs/logo.png" alt="Koa middleware framework for nodejs"/>
|
|
2
2
|
|
|
3
3
|
[![gitter][gitter-image]][gitter-url]
|
|
4
4
|
[![NPM version][npm-image]][npm-url]
|
|
@@ -22,7 +22,7 @@ Koa requires __node v7.6.0__ or higher for ES2015 and async function support.
|
|
|
22
22
|
$ npm install koa
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
## Hello
|
|
25
|
+
## Hello Koa
|
|
26
26
|
|
|
27
27
|
```js
|
|
28
28
|
const Koa = require('koa');
|
|
@@ -38,17 +38,17 @@ app.listen(3000);
|
|
|
38
38
|
|
|
39
39
|
## Getting started
|
|
40
40
|
|
|
41
|
-
- [Kick-Off-Koa](https://github.com/koajs/kick-off-koa) - An intro to
|
|
42
|
-
- [Workshop](https://github.com/koajs/workshop) - A workshop to learn the basics of
|
|
41
|
+
- [Kick-Off-Koa](https://github.com/koajs/kick-off-koa) - An intro to Koa via a set of self-guided workshops.
|
|
42
|
+
- [Workshop](https://github.com/koajs/workshop) - A workshop to learn the basics of Koa, Express' spiritual successor.
|
|
43
43
|
- [Introduction Screencast](http://knowthen.com/episode-3-koajs-quickstart-guide/) - An introduction to installing and getting started with Koa
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
## Middleware
|
|
47
47
|
|
|
48
|
-
Koa is a middleware framework that can take
|
|
48
|
+
Koa is a middleware framework that can take two different kinds of functions as middleware:
|
|
49
49
|
|
|
50
|
-
* common function
|
|
51
50
|
* async function
|
|
51
|
+
* common function
|
|
52
52
|
|
|
53
53
|
Here is an example of logger middleware with each of the different functions:
|
|
54
54
|
|
|
@@ -56,9 +56,9 @@ Here is an example of logger middleware with each of the different functions:
|
|
|
56
56
|
|
|
57
57
|
```js
|
|
58
58
|
app.use(async (ctx, next) => {
|
|
59
|
-
const start =
|
|
59
|
+
const start = Date.now();
|
|
60
60
|
await next();
|
|
61
|
-
const ms =
|
|
61
|
+
const ms = Date.now() - start;
|
|
62
62
|
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
|
|
63
63
|
});
|
|
64
64
|
```
|
|
@@ -70,9 +70,9 @@ app.use(async (ctx, next) => {
|
|
|
70
70
|
// next is a function that is invoked to execute the downstream middleware. It returns a Promise with a then function for running code after completion.
|
|
71
71
|
|
|
72
72
|
app.use((ctx, next) => {
|
|
73
|
-
const start =
|
|
73
|
+
const start = Date.now();
|
|
74
74
|
return next().then(() => {
|
|
75
|
-
const ms =
|
|
75
|
+
const ms = Date.now() - start;
|
|
76
76
|
console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
|
|
77
77
|
});
|
|
78
78
|
});
|
|
@@ -195,7 +195,7 @@ the general Koa guide.
|
|
|
195
195
|
## Running tests
|
|
196
196
|
|
|
197
197
|
```
|
|
198
|
-
$
|
|
198
|
+
$ npm test
|
|
199
199
|
```
|
|
200
200
|
|
|
201
201
|
## Authors
|
|
@@ -211,9 +211,18 @@ See [AUTHORS](AUTHORS).
|
|
|
211
211
|
- [G+ Community](https://plus.google.com/communities/101845768320796750641)
|
|
212
212
|
- [Reddit Community](https://www.reddit.com/r/koajs)
|
|
213
213
|
- [Mailing list](https://groups.google.com/forum/#!forum/koajs)
|
|
214
|
-
- [中文文档](https://github.com/guo-yu/koa-guide)
|
|
214
|
+
- [中文文档 v1.x](https://github.com/guo-yu/koa-guide)
|
|
215
|
+
- [中文文档 v2.x](https://github.com/demopark/koa-docs-Zh-CN)
|
|
215
216
|
- __[#koajs]__ on freenode
|
|
216
217
|
|
|
218
|
+
## Job Board
|
|
219
|
+
|
|
220
|
+
Looking for a career upgrade?
|
|
221
|
+
|
|
222
|
+
<a href="https://astro.netlify.com/automattic"><img src="https://astro.netlify.com/static/automattic.png"></a>
|
|
223
|
+
<a href="https://astro.netlify.com/segment"><img src="https://astro.netlify.com/static/segment.png"></a>
|
|
224
|
+
<a href="https://astro.netlify.com/auth0"><img src="https://astro.netlify.com/static/auth0.png"/></a>
|
|
225
|
+
|
|
217
226
|
## Backers
|
|
218
227
|
|
|
219
228
|
Support us with a monthly donation and help us continue our activities.
|
package/lib/application.js
CHANGED
|
@@ -58,10 +58,10 @@ module.exports = class Application extends Emitter {
|
|
|
58
58
|
* @api public
|
|
59
59
|
*/
|
|
60
60
|
|
|
61
|
-
listen() {
|
|
61
|
+
listen(...args) {
|
|
62
62
|
debug('listen');
|
|
63
63
|
const server = http.createServer(this.callback());
|
|
64
|
-
return server.listen
|
|
64
|
+
return server.listen(...args);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/**
|
|
@@ -128,17 +128,28 @@ module.exports = class Application extends Emitter {
|
|
|
128
128
|
if (!this.listeners('error').length) this.on('error', this.onerror);
|
|
129
129
|
|
|
130
130
|
const handleRequest = (req, res) => {
|
|
131
|
-
res.statusCode = 404;
|
|
132
131
|
const ctx = this.createContext(req, res);
|
|
133
|
-
|
|
134
|
-
const handleResponse = () => respond(ctx);
|
|
135
|
-
onFinished(res, onerror);
|
|
136
|
-
return fn(ctx).then(handleResponse).catch(onerror);
|
|
132
|
+
return this.handleRequest(ctx, fn);
|
|
137
133
|
};
|
|
138
134
|
|
|
139
135
|
return handleRequest;
|
|
140
136
|
}
|
|
141
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Handle request in callback.
|
|
140
|
+
*
|
|
141
|
+
* @api private
|
|
142
|
+
*/
|
|
143
|
+
|
|
144
|
+
handleRequest(ctx, fnMiddleware) {
|
|
145
|
+
const res = ctx.res;
|
|
146
|
+
res.statusCode = 404;
|
|
147
|
+
const onerror = err => ctx.onerror(err);
|
|
148
|
+
const handleResponse = () => respond(ctx);
|
|
149
|
+
onFinished(res, onerror);
|
|
150
|
+
return fnMiddleware(ctx).then(handleResponse).catch(onerror);
|
|
151
|
+
}
|
|
152
|
+
|
|
142
153
|
/**
|
|
143
154
|
* Initialize a new context.
|
|
144
155
|
*
|
package/lib/context.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Module dependencies.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
const util = require('util');
|
|
8
9
|
const createError = require('http-errors');
|
|
9
10
|
const httpAssert = require('http-assert');
|
|
10
11
|
const delegate = require('delegates');
|
|
@@ -25,6 +26,7 @@ const proto = module.exports = {
|
|
|
25
26
|
*/
|
|
26
27
|
|
|
27
28
|
inspect() {
|
|
29
|
+
if (this === proto) return this;
|
|
28
30
|
return this.toJSON();
|
|
29
31
|
},
|
|
30
32
|
|
|
@@ -87,8 +89,8 @@ const proto = module.exports = {
|
|
|
87
89
|
* @api public
|
|
88
90
|
*/
|
|
89
91
|
|
|
90
|
-
throw() {
|
|
91
|
-
throw createError
|
|
92
|
+
throw(...args) {
|
|
93
|
+
throw createError(...args);
|
|
92
94
|
},
|
|
93
95
|
|
|
94
96
|
/**
|
|
@@ -104,7 +106,7 @@ const proto = module.exports = {
|
|
|
104
106
|
// to node-style callbacks.
|
|
105
107
|
if (null == err) return;
|
|
106
108
|
|
|
107
|
-
if (!(err instanceof Error)) err = new Error(
|
|
109
|
+
if (!(err instanceof Error)) err = new Error(util.format('non-error thrown: %j', err));
|
|
108
110
|
|
|
109
111
|
let headerSent = false;
|
|
110
112
|
if (this.headerSent || !this.writable) {
|
|
@@ -198,6 +200,7 @@ delegate(proto, 'request')
|
|
|
198
200
|
.getter('protocol')
|
|
199
201
|
.getter('host')
|
|
200
202
|
.getter('hostname')
|
|
203
|
+
.getter('URL')
|
|
201
204
|
.getter('header')
|
|
202
205
|
.getter('headers')
|
|
203
206
|
.getter('secure')
|
package/lib/request.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Module dependencies.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
+
const URL = require('url').URL;
|
|
8
9
|
const net = require('net');
|
|
9
10
|
const contentType = require('content-type');
|
|
10
11
|
const stringify = require('url').format;
|
|
@@ -31,6 +32,16 @@ module.exports = {
|
|
|
31
32
|
return this.req.headers;
|
|
32
33
|
},
|
|
33
34
|
|
|
35
|
+
/**
|
|
36
|
+
* Set request header.
|
|
37
|
+
*
|
|
38
|
+
* @api public
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
set header(val) {
|
|
42
|
+
this.req.headers = val;
|
|
43
|
+
},
|
|
44
|
+
|
|
34
45
|
/**
|
|
35
46
|
* Return request header, alias as request.header
|
|
36
47
|
*
|
|
@@ -42,6 +53,16 @@ module.exports = {
|
|
|
42
53
|
return this.req.headers;
|
|
43
54
|
},
|
|
44
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Set request header, alias as request.header
|
|
58
|
+
*
|
|
59
|
+
* @api public
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
set headers(val) {
|
|
63
|
+
this.req.headers = val;
|
|
64
|
+
},
|
|
65
|
+
|
|
45
66
|
/**
|
|
46
67
|
* Get request URL.
|
|
47
68
|
*
|
|
@@ -244,9 +265,32 @@ module.exports = {
|
|
|
244
265
|
get hostname() {
|
|
245
266
|
const host = this.host;
|
|
246
267
|
if (!host) return '';
|
|
268
|
+
if ('[' == host[0]) return this.URL.hostname || ''; // IPv6
|
|
247
269
|
return host.split(':')[0];
|
|
248
270
|
},
|
|
249
271
|
|
|
272
|
+
/**
|
|
273
|
+
* Get WHATWG parsed URL.
|
|
274
|
+
* Lazily memoized.
|
|
275
|
+
*
|
|
276
|
+
* @return {URL|Object}
|
|
277
|
+
* @api public
|
|
278
|
+
*/
|
|
279
|
+
|
|
280
|
+
get URL() {
|
|
281
|
+
if (!this.memoizedURL) {
|
|
282
|
+
const protocol = this.protocol;
|
|
283
|
+
const host = this.host;
|
|
284
|
+
const originalUrl = this.originalUrl || ''; // avoid undefined in template string
|
|
285
|
+
try {
|
|
286
|
+
this.memoizedURL = new URL(`${protocol}://${host}${originalUrl}`);
|
|
287
|
+
} catch (err) {
|
|
288
|
+
this.memoizedURL = Object.create(null);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return this.memoizedURL;
|
|
292
|
+
},
|
|
293
|
+
|
|
250
294
|
/**
|
|
251
295
|
* Check if the request is fresh, aka
|
|
252
296
|
* Last-Modified and/or the ETag
|
|
@@ -265,7 +309,7 @@ module.exports = {
|
|
|
265
309
|
|
|
266
310
|
// 2xx or 304 as per rfc2616 14.26
|
|
267
311
|
if ((s >= 200 && s < 300) || 304 == s) {
|
|
268
|
-
return fresh(this.header, this.
|
|
312
|
+
return fresh(this.header, this.response.header);
|
|
269
313
|
}
|
|
270
314
|
|
|
271
315
|
return false;
|
|
@@ -460,8 +504,8 @@ module.exports = {
|
|
|
460
504
|
* @api public
|
|
461
505
|
*/
|
|
462
506
|
|
|
463
|
-
accepts() {
|
|
464
|
-
return this.accept.types
|
|
507
|
+
accepts(...args) {
|
|
508
|
+
return this.accept.types(...args);
|
|
465
509
|
},
|
|
466
510
|
|
|
467
511
|
/**
|
|
@@ -477,8 +521,8 @@ module.exports = {
|
|
|
477
521
|
* @api public
|
|
478
522
|
*/
|
|
479
523
|
|
|
480
|
-
acceptsEncodings() {
|
|
481
|
-
return this.accept.encodings
|
|
524
|
+
acceptsEncodings(...args) {
|
|
525
|
+
return this.accept.encodings(...args);
|
|
482
526
|
},
|
|
483
527
|
|
|
484
528
|
/**
|
|
@@ -494,8 +538,8 @@ module.exports = {
|
|
|
494
538
|
* @api public
|
|
495
539
|
*/
|
|
496
540
|
|
|
497
|
-
acceptsCharsets() {
|
|
498
|
-
return this.accept.charsets
|
|
541
|
+
acceptsCharsets(...args) {
|
|
542
|
+
return this.accept.charsets(...args);
|
|
499
543
|
},
|
|
500
544
|
|
|
501
545
|
/**
|
|
@@ -511,8 +555,8 @@ module.exports = {
|
|
|
511
555
|
* @api public
|
|
512
556
|
*/
|
|
513
557
|
|
|
514
|
-
acceptsLanguages() {
|
|
515
|
-
return this.accept.languages
|
|
558
|
+
acceptsLanguages(...args) {
|
|
559
|
+
return this.accept.languages(...args);
|
|
516
560
|
},
|
|
517
561
|
|
|
518
562
|
/**
|
package/lib/response.js
CHANGED
|
@@ -80,12 +80,13 @@ module.exports = {
|
|
|
80
80
|
*/
|
|
81
81
|
|
|
82
82
|
set status(code) {
|
|
83
|
+
if (this.headerSent) return;
|
|
84
|
+
|
|
83
85
|
assert('number' == typeof code, 'status code must be a number');
|
|
84
86
|
assert(statuses[code], `invalid status code: ${code}`);
|
|
85
|
-
assert(!this.res.headersSent, 'headers have already been sent');
|
|
86
87
|
this._explicitStatus = true;
|
|
87
88
|
this.res.statusCode = code;
|
|
88
|
-
this.res.statusMessage = statuses[code];
|
|
89
|
+
if (this.req.httpVersionMajor < 2) this.res.statusMessage = statuses[code];
|
|
89
90
|
if (this.body && statuses.empty[code]) this.body = null;
|
|
90
91
|
},
|
|
91
92
|
|
|
@@ -133,8 +134,6 @@ module.exports = {
|
|
|
133
134
|
const original = this._body;
|
|
134
135
|
this._body = val;
|
|
135
136
|
|
|
136
|
-
if (this.res.headersSent) return;
|
|
137
|
-
|
|
138
137
|
// no content
|
|
139
138
|
if (null == val) {
|
|
140
139
|
if (!statuses.empty[this.status]) this.status = 204;
|
|
@@ -233,6 +232,8 @@ module.exports = {
|
|
|
233
232
|
*/
|
|
234
233
|
|
|
235
234
|
vary(field) {
|
|
235
|
+
if (this.headerSent) return;
|
|
236
|
+
|
|
236
237
|
vary(this.res, field);
|
|
237
238
|
},
|
|
238
239
|
|
|
@@ -305,7 +306,7 @@ module.exports = {
|
|
|
305
306
|
*/
|
|
306
307
|
|
|
307
308
|
set type(type) {
|
|
308
|
-
type = getType(type)
|
|
309
|
+
type = getType(type);
|
|
309
310
|
if (type) {
|
|
310
311
|
this.set('Content-Type', type);
|
|
311
312
|
} else {
|
|
@@ -434,6 +435,8 @@ module.exports = {
|
|
|
434
435
|
*/
|
|
435
436
|
|
|
436
437
|
set(field, val) {
|
|
438
|
+
if (this.headerSent) return;
|
|
439
|
+
|
|
437
440
|
if (2 == arguments.length) {
|
|
438
441
|
if (Array.isArray(val)) val = val.map(String);
|
|
439
442
|
else val = String(val);
|
|
@@ -481,6 +484,8 @@ module.exports = {
|
|
|
481
484
|
*/
|
|
482
485
|
|
|
483
486
|
remove(field) {
|
|
487
|
+
if (this.headerSent) return;
|
|
488
|
+
|
|
484
489
|
this.res.removeHeader(field);
|
|
485
490
|
},
|
|
486
491
|
|
package/package.json
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koa",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.1",
|
|
4
4
|
"description": "Koa web app framework",
|
|
5
5
|
"main": "lib/application.js",
|
|
6
6
|
"scripts": {
|
|
7
|
-
"test": "
|
|
7
|
+
"test": "jest",
|
|
8
|
+
"test-cov": "jest --coverage --runInBand --forceExit",
|
|
9
|
+
"lint": "eslint benchmarks lib test",
|
|
10
|
+
"bench": "make -C benchmarks",
|
|
11
|
+
"authors": "git log --format='%aN <%aE>' | sort -u > AUTHORS"
|
|
8
12
|
},
|
|
9
13
|
"repository": "koajs/koa",
|
|
10
14
|
"keywords": [
|
|
@@ -28,11 +32,11 @@
|
|
|
28
32
|
"destroy": "^1.0.3",
|
|
29
33
|
"error-inject": "~1.0.0",
|
|
30
34
|
"escape-html": "~1.0.1",
|
|
31
|
-
"fresh": "^0.5.
|
|
35
|
+
"fresh": "^0.5.2",
|
|
32
36
|
"http-assert": "^1.1.0",
|
|
33
37
|
"http-errors": "^1.2.8",
|
|
34
38
|
"is-generator-function": "^1.0.3",
|
|
35
|
-
"koa-compose": "^
|
|
39
|
+
"koa-compose": "^4.0.0",
|
|
36
40
|
"koa-convert": "^1.2.0",
|
|
37
41
|
"koa-is-json": "^1.0.0",
|
|
38
42
|
"mime-types": "^2.0.7",
|
|
@@ -44,23 +48,29 @@
|
|
|
44
48
|
"vary": "^1.0.0"
|
|
45
49
|
},
|
|
46
50
|
"devDependencies": {
|
|
47
|
-
"babel-eslint": "^7.1.1",
|
|
48
51
|
"eslint": "^3.17.1",
|
|
52
|
+
"eslint-config-koa": "^2.0.0",
|
|
49
53
|
"eslint-config-standard": "^7.0.1",
|
|
50
|
-
"eslint-plugin-babel": "^4.1.1",
|
|
51
54
|
"eslint-plugin-promise": "^3.5.0",
|
|
52
55
|
"eslint-plugin-standard": "^2.1.1",
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"should": "^6.0.3",
|
|
56
|
-
"should-http": "0.0.3",
|
|
57
|
-
"supertest": "^3.0.0",
|
|
58
|
-
"test-console": "^0.7.1"
|
|
56
|
+
"jest": "^20.0.0",
|
|
57
|
+
"supertest": "^3.0.0"
|
|
59
58
|
},
|
|
60
59
|
"engines": {
|
|
61
|
-
"node": "
|
|
60
|
+
"node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4"
|
|
62
61
|
},
|
|
63
62
|
"files": [
|
|
64
63
|
"lib"
|
|
65
|
-
]
|
|
64
|
+
],
|
|
65
|
+
"jest": {
|
|
66
|
+
"testMatch": [
|
|
67
|
+
"**/test/!(helpers)/*.js"
|
|
68
|
+
],
|
|
69
|
+
"coverageReporters": [
|
|
70
|
+
"text-summary",
|
|
71
|
+
"lcov"
|
|
72
|
+
],
|
|
73
|
+
"bail": true,
|
|
74
|
+
"testEnvironment": "node"
|
|
75
|
+
}
|
|
66
76
|
}
|