koa 3.0.0 → 3.0.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/Readme.md +7 -8
- package/lib/application.js +37 -38
- package/lib/response.js +25 -7
- package/package.json +13 -13
package/Readme.md
CHANGED
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
|
|
20
20
|
Koa requires __node v18.0.0__ or higher for ES2015 and async function support.
|
|
21
21
|
|
|
22
|
-
```
|
|
23
|
-
|
|
22
|
+
```sh
|
|
23
|
+
npm install koa
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
## Hello Koa
|
|
@@ -81,10 +81,9 @@ app.use((ctx, next) => {
|
|
|
81
81
|
|
|
82
82
|
The middleware signature changed between v1.x and v2.x. The older signature is deprecated.
|
|
83
83
|
|
|
84
|
-
**Old signature middleware support
|
|
84
|
+
**Old signature middleware support has been removed in v3**
|
|
85
85
|
|
|
86
|
-
Please see the [Migration Guide](docs/migration.md) for
|
|
87
|
-
using v1.x middleware with v2.x.
|
|
86
|
+
Please see the [Migration Guide from v2.x to v3.x](docs/migration-v2-to-v3.md) for information on upgrading from v2.x to v3.x, and the [Migration Guide from v1.x to v2.x](docs/migration-v1-to-v2.md) for information on upgrading from v1.x to v2.x.
|
|
88
87
|
|
|
89
88
|
## Context, Request and Response
|
|
90
89
|
|
|
@@ -96,7 +95,7 @@ as the parameter name for the context object.
|
|
|
96
95
|
app.use(async (ctx, next) => { await next(); });
|
|
97
96
|
```
|
|
98
97
|
|
|
99
|
-
Koa provides a `Request` object as the `request` property of the `Context`.
|
|
98
|
+
Koa provides a `Request` object as the `request` property of the `Context`.
|
|
100
99
|
Koa's `Request` object provides helpful methods for working with
|
|
101
100
|
http requests which delegate to an [IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage)
|
|
102
101
|
from the node `http` module.
|
|
@@ -112,10 +111,10 @@ app.use(async (ctx, next) => {
|
|
|
112
111
|
});
|
|
113
112
|
```
|
|
114
113
|
|
|
115
|
-
Koa provides a `Response` object as the `response` property of the `Context`.
|
|
114
|
+
Koa provides a `Response` object as the `response` property of the `Context`.
|
|
116
115
|
Koa's `Response` object provides helpful methods for working with
|
|
117
116
|
http responses which delegate to a [ServerResponse](https://nodejs.org/api/http.html#http_class_http_serverresponse)
|
|
118
|
-
.
|
|
117
|
+
.
|
|
119
118
|
|
|
120
119
|
Koa's pattern of delegating to Node's request and response objects rather than extending them
|
|
121
120
|
provides a cleaner interface and reduces conflicts between different middleware and with Node
|
package/lib/application.js
CHANGED
|
@@ -3,22 +3,23 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Module dependencies.
|
|
5
5
|
*/
|
|
6
|
+
const util = require('node:util')
|
|
7
|
+
const debug = util.debuglog('koa:application')
|
|
8
|
+
const Emitter = require('node:events')
|
|
9
|
+
const Stream = require('node:stream')
|
|
10
|
+
const http = require('node:http')
|
|
11
|
+
const { AsyncLocalStorage } = require('node:async_hooks')
|
|
6
12
|
|
|
7
|
-
const debug = require('debug')('koa:application')
|
|
8
|
-
const assert = require('assert')
|
|
9
13
|
const onFinished = require('on-finished')
|
|
10
|
-
const response = require('./response')
|
|
11
14
|
const compose = require('koa-compose')
|
|
12
|
-
const context = require('./context')
|
|
13
|
-
const request = require('./request')
|
|
14
15
|
const statuses = require('statuses')
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
const
|
|
16
|
+
const { HttpError } = require('http-errors')
|
|
17
|
+
|
|
18
|
+
const request = require('./request')
|
|
19
|
+
const response = require('./response')
|
|
20
|
+
const context = require('./context')
|
|
19
21
|
const isStream = require('./is-stream.js')
|
|
20
22
|
const only = require('./only.js')
|
|
21
|
-
const { HttpError } = require('http-errors')
|
|
22
23
|
|
|
23
24
|
/** @typedef {typeof import ('./context') & {
|
|
24
25
|
* app: Application
|
|
@@ -47,18 +48,18 @@ module.exports = class Application extends Emitter {
|
|
|
47
48
|
*/
|
|
48
49
|
|
|
49
50
|
/**
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
51
|
+
*
|
|
52
|
+
* @param {object} [options] Application options
|
|
53
|
+
* @param {string} [options.env='development'] Environment
|
|
54
|
+
* @param {string[]} [options.keys] Signed cookie keys
|
|
55
|
+
* @param {boolean} [options.proxy] Trust proxy headers
|
|
56
|
+
* @param {number} [options.subdomainOffset] Subdomain offset
|
|
57
|
+
* @param {string} [options.proxyIpHeader] Proxy IP header, defaults to X-Forwarded-For
|
|
58
|
+
* @param {number} [options.maxIpsCount] Max IPs read from proxy IP header, default to 0 (means infinity)
|
|
59
|
+
* @param {function} [options.compose] Function to handle middleware composition
|
|
60
|
+
* @param {boolean} [options.asyncLocalStorage] Enable AsyncLocalStorage, default to false
|
|
61
|
+
*
|
|
62
|
+
*/
|
|
62
63
|
|
|
63
64
|
constructor (options) {
|
|
64
65
|
super()
|
|
@@ -80,9 +81,11 @@ module.exports = class Application extends Emitter {
|
|
|
80
81
|
this[util.inspect.custom] = this.inspect
|
|
81
82
|
}
|
|
82
83
|
if (options.asyncLocalStorage) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
84
|
+
if (options.asyncLocalStorage instanceof AsyncLocalStorage) {
|
|
85
|
+
this.ctxStorage = options.asyncLocalStorage
|
|
86
|
+
} else {
|
|
87
|
+
this.ctxStorage = new AsyncLocalStorage()
|
|
88
|
+
}
|
|
86
89
|
}
|
|
87
90
|
}
|
|
88
91
|
|
|
@@ -111,11 +114,7 @@ module.exports = class Application extends Emitter {
|
|
|
111
114
|
*/
|
|
112
115
|
|
|
113
116
|
toJSON () {
|
|
114
|
-
return only(this, [
|
|
115
|
-
'subdomainOffset',
|
|
116
|
-
'proxy',
|
|
117
|
-
'env'
|
|
118
|
-
])
|
|
117
|
+
return only(this, ['subdomainOffset', 'proxy', 'env'])
|
|
119
118
|
}
|
|
120
119
|
|
|
121
120
|
/**
|
|
@@ -140,7 +139,7 @@ module.exports = class Application extends Emitter {
|
|
|
140
139
|
*/
|
|
141
140
|
|
|
142
141
|
use (fn) {
|
|
143
|
-
if (typeof fn !== 'function') throw new TypeError('middleware must be a function!')
|
|
142
|
+
if (typeof fn !== 'function') { throw new TypeError('middleware must be a function!') }
|
|
144
143
|
debug('use %s', fn._name || fn.name || '-')
|
|
145
144
|
this.middleware.push(fn)
|
|
146
145
|
return this
|
|
@@ -188,7 +187,7 @@ module.exports = class Application extends Emitter {
|
|
|
188
187
|
handleRequest (ctx, fnMiddleware) {
|
|
189
188
|
const res = ctx.res
|
|
190
189
|
res.statusCode = 404
|
|
191
|
-
const onerror = err => ctx.onerror(err)
|
|
190
|
+
const onerror = (err) => ctx.onerror(err)
|
|
192
191
|
const handleResponse = () => respond(ctx)
|
|
193
192
|
onFinished(res, onerror)
|
|
194
193
|
return fnMiddleware(ctx).then(handleResponse).catch(onerror)
|
|
@@ -204,9 +203,9 @@ module.exports = class Application extends Emitter {
|
|
|
204
203
|
/** @type {Context} */
|
|
205
204
|
const context = Object.create(this.context)
|
|
206
205
|
/** @type {KoaRequest} */
|
|
207
|
-
const request = context.request = Object.create(this.request)
|
|
206
|
+
const request = (context.request = Object.create(this.request))
|
|
208
207
|
/** @type {KoaResponse} */
|
|
209
|
-
const response = context.response = Object.create(this.response)
|
|
208
|
+
const response = (context.response = Object.create(this.response))
|
|
210
209
|
context.app = request.app = response.app = this
|
|
211
210
|
context.req = request.req = response.req = req
|
|
212
211
|
context.res = request.res = response.res = res
|
|
@@ -232,7 +231,7 @@ module.exports = class Application extends Emitter {
|
|
|
232
231
|
const isNativeError =
|
|
233
232
|
Object.prototype.toString.call(err) === '[object Error]' ||
|
|
234
233
|
err instanceof Error
|
|
235
|
-
if (!isNativeError) throw new TypeError(util.format('non-error thrown: %j', err))
|
|
234
|
+
if (!isNativeError) { throw new TypeError(util.format('non-error thrown: %j', err)) }
|
|
236
235
|
|
|
237
236
|
if (err.status === 404 || err.expose) return
|
|
238
237
|
if (this.silent) return
|
|
@@ -305,9 +304,9 @@ function respond (ctx) {
|
|
|
305
304
|
|
|
306
305
|
if (Buffer.isBuffer(body)) return res.end(body)
|
|
307
306
|
if (typeof body === 'string') return res.end(body)
|
|
308
|
-
if (body instanceof Blob) return Stream.Readable.from(body.stream()).pipe(res)
|
|
309
|
-
if (body instanceof ReadableStream) return Stream.Readable.from(body).pipe(res)
|
|
310
|
-
if (body instanceof Response) return Stream.Readable.from(body?.body || '').pipe(res)
|
|
307
|
+
if (body instanceof Blob) { return Stream.Readable.from(body.stream()).pipe(res) }
|
|
308
|
+
if (body instanceof ReadableStream) { return Stream.Readable.from(body).pipe(res) }
|
|
309
|
+
if (body instanceof Response) { return Stream.Readable.from(body?.body || '').pipe(res) }
|
|
311
310
|
if (isStream(body)) return body.pipe(res)
|
|
312
311
|
|
|
313
312
|
// body: json
|
package/lib/response.js
CHANGED
|
@@ -4,20 +4,22 @@
|
|
|
4
4
|
* Module dependencies.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
const assert = require('node:assert')
|
|
8
|
+
const extname = require('node:path').extname
|
|
9
|
+
const util = require('node:util')
|
|
10
|
+
|
|
7
11
|
const contentDisposition = require('content-disposition')
|
|
8
|
-
const getType = require('cache-content-type')
|
|
9
12
|
const onFinish = require('on-finished')
|
|
10
13
|
const escape = require('escape-html')
|
|
11
14
|
const typeis = require('type-is').is
|
|
12
15
|
const statuses = require('statuses')
|
|
13
16
|
const destroy = require('destroy')
|
|
14
|
-
const
|
|
15
|
-
const extname = require('path').extname
|
|
17
|
+
const encodeUrl = require('encodeurl')
|
|
16
18
|
const vary = require('vary')
|
|
19
|
+
const getType = require('mime-types').contentType
|
|
20
|
+
|
|
17
21
|
const isStream = require('./is-stream.js')
|
|
18
22
|
const only = require('./only.js')
|
|
19
|
-
const util = require('util')
|
|
20
|
-
const encodeUrl = require('encodeurl')
|
|
21
23
|
|
|
22
24
|
/**
|
|
23
25
|
* Prototype.
|
|
@@ -318,8 +320,24 @@ module.exports = {
|
|
|
318
320
|
*/
|
|
319
321
|
|
|
320
322
|
back (alt) {
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
+
const referrer = this.ctx.get('Referrer')
|
|
324
|
+
if (referrer) {
|
|
325
|
+
// referrer is a relative path
|
|
326
|
+
if (referrer.startsWith('/')) {
|
|
327
|
+
this.redirect(referrer)
|
|
328
|
+
return
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// referrer is an absolute URL, check if it's the same origin
|
|
332
|
+
const url = new URL(referrer, this.ctx.href)
|
|
333
|
+
if (url.host === this.ctx.host) {
|
|
334
|
+
this.redirect(referrer)
|
|
335
|
+
return
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// no referrer, use alt or '/'
|
|
340
|
+
this.redirect(alt || '/')
|
|
323
341
|
},
|
|
324
342
|
|
|
325
343
|
/**
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "koa",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "Koa web app framework",
|
|
5
5
|
"main": "lib/application.js",
|
|
6
6
|
"exports": {
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"test": "node --test",
|
|
19
19
|
"test:coverage": "c8 --reporter=lcov --reporter=text-summary node --test",
|
|
20
20
|
"lint": "standard",
|
|
21
|
+
"lint:fix": "standard --fix",
|
|
21
22
|
"lint:pretty": "standard | snazzy",
|
|
22
23
|
"authors": "git log --format='%aN <%aE>' | sort -u > AUTHORS",
|
|
23
24
|
"build": "gen-esm-wrapper . ./dist/koa.mjs",
|
|
@@ -35,32 +36,31 @@
|
|
|
35
36
|
],
|
|
36
37
|
"license": "MIT",
|
|
37
38
|
"dependencies": {
|
|
38
|
-
"accepts": "^1.3.
|
|
39
|
-
"
|
|
40
|
-
"content-
|
|
41
|
-
"content-type": "^1.0.4",
|
|
39
|
+
"accepts": "^1.3.8",
|
|
40
|
+
"content-disposition": "~0.5.4",
|
|
41
|
+
"content-type": "^1.0.5",
|
|
42
42
|
"cookies": "~0.9.1",
|
|
43
|
-
"debug": "^4.3.2",
|
|
44
43
|
"delegates": "^1.0.0",
|
|
45
|
-
"destroy": "^1.0
|
|
44
|
+
"destroy": "^1.2.0",
|
|
46
45
|
"encodeurl": "^2.0.0",
|
|
47
46
|
"escape-html": "^1.0.3",
|
|
48
47
|
"fresh": "~0.5.2",
|
|
49
|
-
"http-assert": "^1.
|
|
48
|
+
"http-assert": "^1.5.0",
|
|
50
49
|
"http-errors": "^2.0.0",
|
|
51
50
|
"koa-compose": "^4.1.0",
|
|
52
|
-
"
|
|
53
|
-
"
|
|
51
|
+
"mime-types": "^3.0.1",
|
|
52
|
+
"on-finished": "^2.4.1",
|
|
53
|
+
"parseurl": "^1.3.3",
|
|
54
54
|
"statuses": "^2.0.1",
|
|
55
55
|
"type-is": "^2.0.1",
|
|
56
56
|
"vary": "^1.1.2"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"c8": "^10.1.3",
|
|
60
|
-
"gen-esm-wrapper": "^1.
|
|
60
|
+
"gen-esm-wrapper": "^1.1.3",
|
|
61
61
|
"snazzy": "^9.0.0",
|
|
62
|
-
"standard": "^17.1.
|
|
63
|
-
"supertest": "^7.
|
|
62
|
+
"standard": "^17.1.2",
|
|
63
|
+
"supertest": "^7.1.1"
|
|
64
64
|
},
|
|
65
65
|
"engines": {
|
|
66
66
|
"node": ">= 18"
|