diff --git a/docs/guide.md b/docs/guide.md index 5c7104d..1e1a3b7 100644 --- a/docs/guide.md +++ b/docs/guide.md @@ -213,13 +213,13 @@ app.use(async function (ctx, next) { ## Debugging Koa - Koa along with many of the libraries it's built with support the __DEBUG__ environment variable from [debug](https://github.com/visionmedia/debug) which provides simple conditional logging. + Koa, along with many of the libraries it's built, can be debugged using Node.js's built-in util.debuglog, which provides simple conditional logging via the __NODE_DEBUG__ environment variable. For example - to see all Koa-specific debugging information just pass `DEBUG=koa*` and upon boot you'll see the list of middleware used, among other things. + to see all Koa-specific debugging information just pass `NODE_DEBUG=koa*` and upon boot you'll see the list of middleware used, among other things. ``` -$ DEBUG=koa* node --harmony examples/simple +$ NODE_DEBUG=koa* node --harmony examples/simple koa:application use responseTime +0ms koa:application use logger +4ms koa:application use contentLength +0ms @@ -234,7 +234,7 @@ $ DEBUG=koa* node --harmony examples/simple For example: ```js -const path = require('path'); +const path = require('node:path'); const serve = require('koa-static'); const publicFiles = serve(path.join(__dirname, 'public')); @@ -249,6 +249,8 @@ app.use(publicFiles); koa:application use static /public +0ms ``` +This lets you use Koa’s internal debug logs without any third-party dependencies by relying on node:util's built-in debuglog. + ## HTTP2 Example of setting up an HTTP2 server with Koa using the HTTP compatibility layer: diff --git a/lib/application.js b/lib/application.js index 8679e66..ff32453 100644 --- a/lib/application.js +++ b/lib/application.js @@ -3,22 +3,23 @@ /** * Module dependencies. */ +const util = require('node:util') +const debug = util.debuglog('koa:application') +const Emitter = require('node:events') +const Stream = require('node:stream') +const http = require('node:http') +const { AsyncLocalStorage } = require('node:async_hooks') -const debug = require('debug')('koa:application') const onFinished = require('on-finished') -const response = require('./response') const compose = require('koa-compose') -const context = require('./context') -const request = require('./request') const statuses = require('statuses') -const Emitter = require('events') -const util = require('util') -const Stream = require('stream') -const http = require('http') +const { HttpError } = require('http-errors') + +const request = require('./request') +const response = require('./response') +const context = require('./context') const isStream = require('./is-stream.js') const only = require('./only.js') -const { HttpError } = require('http-errors') -const { AsyncLocalStorage } = require('async_hooks') /** @typedef {typeof import ('./context') & { * app: Application @@ -47,18 +48,18 @@ module.exports = class Application extends Emitter { */ /** - * - * @param {object} [options] Application options - * @param {string} [options.env='development'] Environment - * @param {string[]} [options.keys] Signed cookie keys - * @param {boolean} [options.proxy] Trust proxy headers - * @param {number} [options.subdomainOffset] Subdomain offset - * @param {string} [options.proxyIpHeader] Proxy IP header, defaults to X-Forwarded-For - * @param {number} [options.maxIpsCount] Max IPs read from proxy IP header, default to 0 (means infinity) - * @param {function} [options.compose] Function to handle middleware composition - * @param {boolean} [options.asyncLocalStorage] Enable AsyncLocalStorage, default to false - * - */ + * + * @param {object} [options] Application options + * @param {string} [options.env='development'] Environment + * @param {string[]} [options.keys] Signed cookie keys + * @param {boolean} [options.proxy] Trust proxy headers + * @param {number} [options.subdomainOffset] Subdomain offset + * @param {string} [options.proxyIpHeader] Proxy IP header, defaults to X-Forwarded-For + * @param {number} [options.maxIpsCount] Max IPs read from proxy IP header, default to 0 (means infinity) + * @param {function} [options.compose] Function to handle middleware composition + * @param {boolean} [options.asyncLocalStorage] Enable AsyncLocalStorage, default to false + * + */ constructor (options) { super() @@ -113,11 +114,7 @@ module.exports = class Application extends Emitter { */ toJSON () { - return only(this, [ - 'subdomainOffset', - 'proxy', - 'env' - ]) + return only(this, ['subdomainOffset', 'proxy', 'env']) } /** @@ -142,7 +139,7 @@ module.exports = class Application extends Emitter { */ use (fn) { - if (typeof fn !== 'function') throw new TypeError('middleware must be a function!') + if (typeof fn !== 'function') { throw new TypeError('middleware must be a function!') } debug('use %s', fn._name || fn.name || '-') this.middleware.push(fn) return this @@ -190,7 +187,7 @@ module.exports = class Application extends Emitter { handleRequest (ctx, fnMiddleware) { const res = ctx.res res.statusCode = 404 - const onerror = err => ctx.onerror(err) + const onerror = (err) => ctx.onerror(err) const handleResponse = () => respond(ctx) onFinished(res, onerror) return fnMiddleware(ctx).then(handleResponse).catch(onerror) @@ -206,9 +203,9 @@ module.exports = class Application extends Emitter { /** @type {Context} */ const context = Object.create(this.context) /** @type {KoaRequest} */ - const request = context.request = Object.create(this.request) + const request = (context.request = Object.create(this.request)) /** @type {KoaResponse} */ - const response = context.response = Object.create(this.response) + const response = (context.response = Object.create(this.response)) context.app = request.app = response.app = this context.req = request.req = response.req = req context.res = request.res = response.res = res @@ -234,7 +231,7 @@ module.exports = class Application extends Emitter { const isNativeError = Object.prototype.toString.call(err) === '[object Error]' || err instanceof Error - if (!isNativeError) throw new TypeError(util.format('non-error thrown: %j', err)) + if (!isNativeError) { throw new TypeError(util.format('non-error thrown: %j', err)) } if (err.status === 404 || err.expose) return if (this.silent) return @@ -307,9 +304,9 @@ function respond (ctx) { if (Buffer.isBuffer(body)) return res.end(body) if (typeof body === 'string') return res.end(body) - if (body instanceof Blob) return Stream.Readable.from(body.stream()).pipe(res) - if (body instanceof ReadableStream) return Stream.Readable.from(body).pipe(res) - if (body instanceof Response) return Stream.Readable.from(body?.body || '').pipe(res) + if (body instanceof Blob) { return Stream.Readable.from(body.stream()).pipe(res) } + if (body instanceof ReadableStream) { return Stream.Readable.from(body).pipe(res) } + if (body instanceof Response) { return Stream.Readable.from(body?.body || '').pipe(res) } if (isStream(body)) return body.pipe(res) // body: json diff --git a/package-lock.json b/package-lock.json index 984e9d3..7ab0ddc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,32 +9,31 @@ "version": "3.0.0", "license": "MIT", "dependencies": { - "accepts": "^1.3.5", + "accepts": "^1.3.8", "cache-content-type": "^1.0.0", - "content-disposition": "~0.5.2", - "content-type": "^1.0.4", + "content-disposition": "~0.5.4", + "content-type": "^1.0.5", "cookies": "~0.9.1", - "debug": "^4.3.2", "delegates": "^1.0.0", - "destroy": "^1.0.4", + "destroy": "^1.2.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "fresh": "~0.5.2", - "http-assert": "^1.3.0", + "http-assert": "^1.5.0", "http-errors": "^2.0.0", "koa-compose": "^4.1.0", - "on-finished": "^2.3.0", - "parseurl": "^1.3.2", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" }, "devDependencies": { "c8": "^10.1.3", - "gen-esm-wrapper": "^1.0.6", + "gen-esm-wrapper": "^1.1.3", "snazzy": "^9.0.0", - "standard": "^17.1.0", - "supertest": "^7.0.0" + "standard": "^17.1.2", + "supertest": "^7.1.1" }, "engines": { "node": ">= 18" @@ -1055,6 +1054,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -3376,6 +3376,7 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, "license": "MIT" }, "node_modules/natural-compare": { diff --git a/package.json b/package.json index 6158c8a..86cf072 100644 --- a/package.json +++ b/package.json @@ -35,32 +35,31 @@ ], "license": "MIT", "dependencies": { - "accepts": "^1.3.5", + "accepts": "^1.3.8", "cache-content-type": "^1.0.0", - "content-disposition": "~0.5.2", - "content-type": "^1.0.4", + "content-disposition": "~0.5.4", + "content-type": "^1.0.5", "cookies": "~0.9.1", - "debug": "^4.3.2", "delegates": "^1.0.0", - "destroy": "^1.0.4", + "destroy": "^1.2.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "fresh": "~0.5.2", - "http-assert": "^1.3.0", + "http-assert": "^1.5.0", "http-errors": "^2.0.0", "koa-compose": "^4.1.0", - "on-finished": "^2.3.0", - "parseurl": "^1.3.2", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" }, "devDependencies": { "c8": "^10.1.3", - "gen-esm-wrapper": "^1.0.6", + "gen-esm-wrapper": "^1.1.3", "snazzy": "^9.0.0", - "standard": "^17.1.0", - "supertest": "^7.0.0" + "standard": "^17.1.2", + "supertest": "^7.1.1" }, "engines": { "node": ">= 18"