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 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
- $ npm install koa
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 will be removed in v3**
84
+ **Old signature middleware support has been removed in v3**
85
85
 
86
- Please see the [Migration Guide](docs/migration.md) for more information on upgrading from v1.x and
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
@@ -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 Emitter = require('events')
16
- const util = require('util')
17
- const Stream = require('stream')
18
- const http = require('http')
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
- * @param {object} [options] Application options
52
- * @param {string} [options.env='development'] Environment
53
- * @param {string[]} [options.keys] Signed cookie keys
54
- * @param {boolean} [options.proxy] Trust proxy headers
55
- * @param {number} [options.subdomainOffset] Subdomain offset
56
- * @param {string} [options.proxyIpHeader] Proxy IP header, defaults to X-Forwarded-For
57
- * @param {number} [options.maxIpsCount] Max IPs read from proxy IP header, default to 0 (means infinity)
58
- * @param {function} [options.compose] Function to handle middleware composition
59
- * @param {boolean} [options.asyncLocalStorage] Enable AsyncLocalStorage, default to false
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
- const { AsyncLocalStorage } = require('async_hooks')
84
- assert(AsyncLocalStorage, 'Requires node 12.17.0 or higher to enable asyncLocalStorage')
85
- this.ctxStorage = new AsyncLocalStorage()
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 assert = require('assert')
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 url = this.ctx.get('Referrer') || alt || '/'
322
- this.redirect(url)
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.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.5",
39
- "cache-content-type": "^1.0.0",
40
- "content-disposition": "~0.5.2",
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.4",
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.3.0",
48
+ "http-assert": "^1.5.0",
50
49
  "http-errors": "^2.0.0",
51
50
  "koa-compose": "^4.1.0",
52
- "on-finished": "^2.3.0",
53
- "parseurl": "^1.3.2",
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.0.6",
60
+ "gen-esm-wrapper": "^1.1.3",
61
61
  "snazzy": "^9.0.0",
62
- "standard": "^17.1.0",
63
- "supertest": "^7.0.0"
62
+ "standard": "^17.1.2",
63
+ "supertest": "^7.1.1"
64
64
  },
65
65
  "engines": {
66
66
  "node": ">= 18"