diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 39003ee984..e882bdf919 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -49,6 +49,27 @@ jobs:
cache: 'npm'
- run: npm ci
- run: npm test
+ benchmark:
+ name: Benchmark
+ runs-on: ubuntu-latest
+ needs: build
+ strategy:
+ matrix:
+ node-version:
+ - 20.x
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ - name: Use Node.js ${{ matrix.node-version }}
+ uses: actions/setup-node@v3
+ with:
+ node-version: ${{ matrix.node-version }}
+ cache: 'npm'
+ - run: npm ci
+ - run: npm run benchmark
+
release:
name: Release
if: github.ref == 'refs/heads/main' && success()
diff --git a/benchmarks/Makefile b/benchmarks/Makefile
deleted file mode 100644
index ed1ddfc4f3..0000000000
--- a/benchmarks/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-
-all:
- @./run 1 middleware 50
- @./run 5 middleware 50
- @./run 10 middleware 50
- @./run 15 middleware 50
- @./run 20 middleware 50
- @./run 30 middleware 50
- @./run 50 middleware 50
- @./run 100 middleware 50
- @./run 10 middleware 100
- @./run 10 middleware 250
- @./run 10 middleware 500
- @./run 10 middleware 1000
- @echo
-
-.PHONY: all
diff --git a/benchmarks/README.md b/benchmarks/README.md
new file mode 100644
index 0000000000..f3dec7e076
--- /dev/null
+++ b/benchmarks/README.md
@@ -0,0 +1,90 @@
+# Benchmark history
+
+
+# 2024-01-06
+
+Redesigned Express to not create a prototype chain on the req/res objects. The following are the benchmark results between the old version (Express 5 - with prototype chaining) with the new design.
+
+**System Specs**
+
+- Mac mini
+- Apple M1
+- Memory: 16 GB
+- OS Version: 14.2.1 macOS Sonoma
+
+## Old design (with prototype chain)
+
+ autocannon -c 100 -d 5 -p 10 http://localhost:3333/?foo[bar]=baz
+
+ Running 5s test @ http://localhost:3333/?foo[bar]=baz
+ 100 connections with 10 pipelining factor
+
+
+ ┌─────────┬───────┬───────┬───────┬───────┬──────────┬──────────┬─────────┐
+ │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
+ ├─────────┼───────┼───────┼───────┼───────┼──────────┼──────────┼─────────┤
+ │ Latency │ 16 ms │ 29 ms │ 66 ms │ 70 ms │ 38.63 ms │ 61.67 ms │ 1562 ms │
+ └─────────┴───────┴───────┴───────┴───────┴──────────┴──────────┴─────────┘
+ ┌───────────┬─────────┬─────────┬─────────┬─────────┬──────────┬──────────┬─────────┐
+ │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
+ ├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼──────────┼─────────┤
+ │ Req/Sec │ 22,351 │ 22,351 │ 26,143 │ 26,543 │ 25,460.8 │ 1,568.25 │ 22,336 │
+ ├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼──────────┼─────────┤
+ │ Bytes/Sec │ 5.32 MB │ 5.32 MB │ 6.22 MB │ 6.32 MB │ 6.06 MB │ 375 kB │ 5.32 MB │
+ └───────────┴─────────┴─────────┴─────────┴─────────┴──────────┴──────────┴─────────┘
+
+ Req/Bytes counts sampled once per second.
+ # of samples: 5
+
+ 128k requests in 5.02s, 30.3 MB read
+
+## New design
+
+ autocannon -c 100 -d 5 -p 10 http://localhost:3333/?foo[bar]=baz
+
+ Running 5s test @ http://localhost:3333/?foo[bar]=baz
+ 100 connections with 10 pipelining factor
+
+
+ ┌─────────┬──────┬───────┬───────┬───────┬──────────┬──────────┬────────┐
+ │ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
+ ├─────────┼──────┼───────┼───────┼───────┼──────────┼──────────┼────────┤
+ │ Latency │ 9 ms │ 16 ms │ 22 ms │ 26 ms │ 16.32 ms │ 11.46 ms │ 471 ms │
+ └─────────┴──────┴───────┴───────┴───────┴──────────┴──────────┴────────┘
+ ┌───────────┬─────────┬─────────┬─────────┬─────────┬──────────┬──────────┬─────────┐
+ │ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
+ ├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼──────────┼─────────┤
+ │ Req/Sec │ 54,943 │ 54,943 │ 60,575 │ 60,703 │ 59,363.2 │ 2,230.44 │ 54,922 │
+ ├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼──────────┼─────────┤
+ │ Bytes/Sec │ 13.1 MB │ 13.1 MB │ 14.4 MB │ 14.4 MB │ 14.1 MB │ 531 kB │ 13.1 MB │
+ └───────────┴─────────┴─────────┴─────────┴─────────┴──────────┴──────────┴─────────┘
+
+ Req/Bytes counts sampled once per second.
+ # of samples: 5
+
+ 298k requests in 5.02s, 70.6 MB read
+
+# Results
+
+## Version 3 - 1/6/2024, 6:51:42 PM
+
+| Stat | 2.5% | 50% | 97.5% | 99% | Avg | Stdev | Max |
+|:-----|:----:|:---:|:-----:|:---:|:---:|:-----:|:---:|
+| Latency | 7 ms | 18 ms | 21 ms | 26 ms | 15.23 ms | 17.1 ms | 600 ms |
+
+| Stat | 1% | 2.5% | 50% | 97.5% | Avg | Stdev | Min |
+|:-----|:--:|:----:|:---:|:-----:|:---:|:-----:|:---:|
+| Req/Sec | 56,255 | 56,255 | 64,511 | 66,495 | 63,388.8 | 3,687.84 | 56,241 |
+| Bytes/Sec | 13.4 MB | 13.4 MB | 15.4 MB | 15.8 MB | 15.1 MB | 877 kB | 13.4 MB |
+
+## Version 3 - 1/6/2024, 7:19:01 PM
+
+| Stat | 2.5% | 50% | 97.5% | 99% | Avg | Stdev | Max |
+|:-----|:----:|:---:|:-----:|:---:|:---:|:-----:|:---:|
+| Latency | 8 ms | 18 ms | 20 ms | 22 ms | 15.4 ms | 18.39 ms | 667 ms |
+
+| Stat | 1% | 2.5% | 50% | 97.5% | Avg | Stdev | Min |
+|:-----|:--:|:----:|:---:|:-----:|:---:|:-----:|:---:|
+| Req/Sec | 52,095 | 52,095 | 65,439 | 65,855 | 62,758.4 | 5,344.96 | 52,088 |
+| Bytes/Sec | 12.4 MB | 12.4 MB | 15.6 MB | 15.7 MB | 14.9 MB | 1.27 MB | 12.4 MB |
+
diff --git a/benchmarks/index.mjs b/benchmarks/index.mjs
new file mode 100644
index 0000000000..b07c54555b
--- /dev/null
+++ b/benchmarks/index.mjs
@@ -0,0 +1,126 @@
+
+import express from '../index.js'
+import autocannon from 'autocannon'
+import prettyBytes from 'pretty-bytes'
+import fs from 'node:fs'
+
+const toMs = (ns) => {
+ return `${Math.floor(ns * 100) / 100} ms`;
+}
+const format = (ns) => {
+ return ns.toLocaleString({minimumFractionDigits: 2, maximumFractionDigits: 2})
+}
+
+const toTable = (data) => {
+ const { latency, requests, throughput } = data
+ const latencyTable = `
+| Stat | 2.5% | 50% | 97.5% | 99% | Avg | Stdev | Max |
+|:-----|:----:|:---:|:-----:|:---:|:---:|:-----:|:---:|
+| Latency | ${toMs(latency.p2_5)} | ${toMs(latency.p50)} | ${toMs(latency.p97_5)} | ${toMs(latency.p99)} | ${toMs(latency.average)} | ${toMs(latency.stddev)} | ${toMs(latency.max)} |
+`
+ const rateTable = `
+| Stat | 1% | 2.5% | 50% | 97.5% | Avg | Stdev | Min |
+|:-----|:--:|:----:|:---:|:-----:|:---:|:-----:|:---:|
+| Req/Sec | ${format(requests.p1)} | ${format(requests.p2_5)} | ${format(requests.p50)} | ${format(requests.p97_5)} | ${format(requests.average)} | ${format(requests.stddev)} | ${format(requests.min)} |
+| Bytes/Sec | ${prettyBytes(throughput.p1)} | ${prettyBytes(throughput.p2_5)} | ${prettyBytes(throughput.p50)} | ${prettyBytes(throughput.p97_5)} | ${prettyBytes(throughput.average)} | ${prettyBytes(throughput.stddev)} | ${prettyBytes(throughput.min)} |
+`
+ return latencyTable + rateTable
+}
+const toHtmlTable = (data) => {
+ const { latency, requests, throughput } = data
+ const latencyTable = `
+
+
+
+ Stat |
+ 2.5% |
+ 50% |
+ 97.5% |
+ 99% |
+ Avg |
+ Stdev |
+ Max |
+
+
+
+
+ Latency |
+ ${toMs(latency.p2_5)} ms |
+ ${toMs(latency.p50)} ms |
+ ${toMs(latency.p97_5)} ms |
+ ${toMs(latency.p99)} ms |
+ ${toMs(latency.average)} ms |
+ ${toMs(latency.stddev)} ms |
+ ${toMs(latency.max)} ms |
+
+
+
+`
+ const rateTable = `
+
+
+
+ Stat |
+ 1% |
+ 2.5% |
+ 50% |
+ 97.5% |
+ Avg |
+ Stdev |
+ Min |
+
+
+
+
+ Req/Sec |
+ ${requests.p1.toLocaleString()} |
+ ${requests.p2_5.toLocaleString()} |
+ ${requests.p50.toLocaleString()} |
+ ${requests.p97_5.toLocaleString()} |
+ ${requests.average.toLocaleString()} |
+ ${requests.stddev.toLocaleString()} |
+ ${requests.min.toLocaleString()} |
+
+
+ Bytes/Sec |
+ ${prettyBytes(throughput.p1)} |
+ ${prettyBytes(throughput.p2_5)} |
+ ${prettyBytes(throughput.p50)} |
+ ${prettyBytes(throughput.p97_5)} |
+ ${prettyBytes(throughput.average)} |
+ ${prettyBytes(throughput.stddev)} |
+ ${prettyBytes(throughput.min)} |
+
+
+
+`
+ return latencyTable + rateTable
+}
+const app = express()
+let n = parseInt(process.env.MW || '1', 10)
+console.log(' %s middleware', n)
+while (n--) {
+ app.use(function(req, res, next){
+ next()
+ })
+}
+app.use((req, res) => {
+ res.send('Hello World')
+})
+const server = app.listen()
+const { port } = server.address()
+autocannon({
+ url: `http://localhost:${port}/?foo[bar]=baz`,
+ pipelining: 10,
+ connections: 100,
+ duration: 5,
+ title: 'Version 3',
+ workers: 4
+}, (err, result) => {
+ console.log(toTable(result))
+ fs.appendFile(`benchmarks/README.md`, `## ${result.title} - ${new Date().toLocaleString()}
+${toTable(result)}\n`, 'utf8', () => {
+ server.close()
+ })
+})
+
diff --git a/benchmarks/middleware.js b/benchmarks/middleware.js
deleted file mode 100644
index fed97ba8ce..0000000000
--- a/benchmarks/middleware.js
+++ /dev/null
@@ -1,20 +0,0 @@
-
-var express = require('..');
-var app = express();
-
-// number of middleware
-
-var n = parseInt(process.env.MW || '1', 10);
-console.log(' %s middleware', n);
-
-while (n--) {
- app.use(function(req, res, next){
- next();
- });
-}
-
-app.use(function(req, res){
- res.send('Hello World')
-});
-
-app.listen(3333);
diff --git a/benchmarks/run b/benchmarks/run
deleted file mode 100755
index ec8f55d564..0000000000
--- a/benchmarks/run
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/env bash
-
-echo
-MW=$1 node $2 &
-pid=$!
-
-echo " $3 connections"
-
-sleep 2
-
-wrk 'http://localhost:3333/?foo[bar]=baz' \
- -d 3 \
- -c $3 \
- -t 8 \
- | grep 'Requests/sec\|Latency' \
- | awk '{ print " " $2 }'
-
-kill $pid
diff --git a/package-lock.json b/package-lock.json
index 98a9455b5d..c31259f753 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -41,6 +41,7 @@
},
"devDependencies": {
"after": "^0.8.2",
+ "autocannon": "^7.14.0",
"connect-redis": "^7.1.0",
"cookie-parser": "^1.4.6",
"cookie-session": "^2.0.0",
@@ -69,6 +70,22 @@
"node": ">=0.10.0"
}
},
+ "node_modules/@assemblyscript/loader": {
+ "version": "0.19.23",
+ "resolved": "https://registry.npmjs.org/@assemblyscript/loader/-/loader-0.19.23.tgz",
+ "integrity": "sha512-ulkCYfFbYj01ie1MDOyxv2F6SpRN1TOj7fQxbP07D6HmeR+gr2JLSmINKjga2emB+b1L2KGrFKBTc+e00p54nw==",
+ "dev": true
+ },
+ "node_modules/@colors/colors": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
+ "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==",
+ "dev": true,
+ "optional": true,
+ "engines": {
+ "node": ">=0.1.90"
+ }
+ },
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -314,12 +331,66 @@
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
"dev": true
},
+ "node_modules/autocannon": {
+ "version": "7.14.0",
+ "resolved": "https://registry.npmjs.org/autocannon/-/autocannon-7.14.0.tgz",
+ "integrity": "sha512-lusP43BAwrTwQhihLjKwy7LceyX01eKSvFJUsBktASGqcR1g1ySYSPxCoCGDX08uLEs9oaqEKBBUFMenK3B3lQ==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "char-spinner": "^1.0.1",
+ "cli-table3": "^0.6.0",
+ "color-support": "^1.1.1",
+ "cross-argv": "^2.0.0",
+ "form-data": "^4.0.0",
+ "has-async-hooks": "^1.0.0",
+ "hdr-histogram-js": "^3.0.0",
+ "hdr-histogram-percentiles-obj": "^3.0.0",
+ "http-parser-js": "^0.5.2",
+ "hyperid": "^3.0.0",
+ "lodash.chunk": "^4.2.0",
+ "lodash.clonedeep": "^4.5.0",
+ "lodash.flatten": "^4.4.0",
+ "manage-path": "^2.0.0",
+ "on-net-listen": "^1.1.1",
+ "pretty-bytes": "^5.4.1",
+ "progress": "^2.0.3",
+ "reinterval": "^1.1.0",
+ "retimer": "^3.0.0",
+ "semver": "^7.3.2",
+ "subarg": "^1.0.0",
+ "timestring": "^6.0.0"
+ },
+ "bin": {
+ "autocannon": "autocannon.js"
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true
},
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ]
+ },
"node_modules/basic-auth": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
@@ -452,6 +523,27 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
+ "node_modules/char-spinner": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/char-spinner/-/char-spinner-1.0.1.tgz",
+ "integrity": "sha512-acv43vqJ0+N0rD+Uw3pDHSxP30FHrywu2NO6/wBaHChJIizpDeBUd6NjqhNhy9LGaEAhZAXn46QzmlAvIWd16g==",
+ "dev": true
+ },
+ "node_modules/cli-table3": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.3.tgz",
+ "integrity": "sha512-w5Jac5SykAeZJKntOxJCrm63Eg5/4dhMWIcuTbo9rpE+brgaSZo0RuNJZeOyMgsUdhDeojvgyQLmjI+K50ZGyg==",
+ "dev": true,
+ "dependencies": {
+ "string-width": "^4.2.0"
+ },
+ "engines": {
+ "node": "10.* || >= 12.*"
+ },
+ "optionalDependencies": {
+ "@colors/colors": "1.5.0"
+ }
+ },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -470,6 +562,15 @@
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
"dev": true
},
+ "node_modules/color-support": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
+ "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
+ "dev": true,
+ "bin": {
+ "color-support": "bin.js"
+ }
+ },
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -621,6 +722,12 @@
"node": ">= 0.8"
}
},
+ "node_modules/cross-argv": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/cross-argv/-/cross-argv-2.0.0.tgz",
+ "integrity": "sha512-YIaY9TR5Nxeb8SMdtrU8asWVM4jqJDNDYlKV21LxtYcfNJhp1kEsgSa6qXwXgzN0WQWGODps0+TlGp2xQSHwOg==",
+ "dev": true
+ },
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -743,6 +850,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true
+ },
"node_modules/encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
@@ -1291,6 +1404,12 @@
"uglify-js": "^3.1.4"
}
},
+ "node_modules/has-async-hooks": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/has-async-hooks/-/has-async-hooks-1.0.0.tgz",
+ "integrity": "sha512-YF0VPGjkxr7AyyQQNykX8zK4PvtEDsUJAPqwu06UFz1lb6EvI53sPh5H1kWxg8NXI5LsfRCZ8uX9NkYDZBb/mw==",
+ "dev": true
+ },
"node_modules/has-flag": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -1358,6 +1477,26 @@
"npm": "1.2.8000 || >= 1.4.16"
}
},
+ "node_modules/hdr-histogram-js": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hdr-histogram-js/-/hdr-histogram-js-3.0.0.tgz",
+ "integrity": "sha512-/EpvQI2/Z98mNFYEnlqJ8Ogful8OpArLG/6Tf2bPnkutBVLIeMVNHjk1ZDfshF2BUweipzbk+dB1hgSB7SIakw==",
+ "dev": true,
+ "dependencies": {
+ "@assemblyscript/loader": "^0.19.21",
+ "base64-js": "^1.2.0",
+ "pako": "^1.0.3"
+ },
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/hdr-histogram-percentiles-obj": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hdr-histogram-percentiles-obj/-/hdr-histogram-percentiles-obj-3.0.0.tgz",
+ "integrity": "sha512-7kIufnBqdsBGcSZLPJwqHT3yhk1QTsSlFsVD3kx5ixH/AlgBs9yM1q6DPhXZ8f8gtdqgh7N7/5btRLpQsS2gHw==",
+ "dev": true
+ },
"node_modules/hexoid": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz",
@@ -1398,6 +1537,22 @@
"node": ">= 0.6"
}
},
+ "node_modules/http-parser-js": {
+ "version": "0.5.8",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz",
+ "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==",
+ "dev": true
+ },
+ "node_modules/hyperid": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/hyperid/-/hyperid-3.1.1.tgz",
+ "integrity": "sha512-RveV33kIksycSf7HLkq1sHB5wW0OwuX8ot8MYnY++gaaPXGFfKpBncHrAWxdpuEeRlazUMGWefwP1w6o6GaumA==",
+ "dev": true,
+ "dependencies": {
+ "uuid": "^8.3.2",
+ "uuid-parse": "^1.1.0"
+ }
+ },
"node_modules/iconv-lite": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.5.2.tgz",
@@ -1475,6 +1630,15 @@
"node": ">=0.10.0"
}
},
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/is-glob": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -1587,12 +1751,36 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lodash.chunk": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.chunk/-/lodash.chunk-4.2.0.tgz",
+ "integrity": "sha512-ZzydJKfUHJwHa+hF5X66zLFCBrWn5GeF28OHEr4WVWtNDXlQ/IjWKPBiikqKo2ne0+v6JgCgJ0GzJp8k8bHC7w==",
+ "dev": true
+ },
+ "node_modules/lodash.clonedeep": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
+ "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==",
+ "dev": true
+ },
+ "node_modules/lodash.flatten": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
+ "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==",
+ "dev": true
+ },
"node_modules/lodash.merge": {
"version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true
},
+ "node_modules/manage-path": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/manage-path/-/manage-path-2.0.0.tgz",
+ "integrity": "sha512-NJhyB+PJYTpxhxZJ3lecIGgh4kwIY2RAh44XvAz9UlqthlQwtPBf62uBVR8XaD8CRuSjQ6TnZH2lNJkbLPZM2A==",
+ "dev": true
+ },
"node_modules/marked": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/marked/-/marked-11.1.0.tgz",
@@ -1812,6 +2000,15 @@
"node": ">= 0.8"
}
},
+ "node_modules/on-net-listen": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/on-net-listen/-/on-net-listen-1.1.2.tgz",
+ "integrity": "sha512-y1HRYy8s/RlcBvDUwKXSmkODMdx4KSuIvloCnQYJ2LdBBC1asY4HtfhXwe3UWknLakATZDnbzht2Ijw3M1EqFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=9.4.0 || ^8.9.4"
+ }
+ },
"node_modules/once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -1868,6 +2065,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/pako": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+ "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+ "dev": true
+ },
"node_modules/parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -1937,6 +2140,27 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/pretty-bytes": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
+ "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/proxy-addr": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
@@ -2038,6 +2262,12 @@
"node": ">= 0.8"
}
},
+ "node_modules/reinterval": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reinterval/-/reinterval-1.1.0.tgz",
+ "integrity": "sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==",
+ "dev": true
+ },
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -2047,6 +2277,12 @@
"node": ">=4"
}
},
+ "node_modules/retimer": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/retimer/-/retimer-3.0.0.tgz",
+ "integrity": "sha512-WKE0j11Pa0ZJI5YIk0nflGI7SQsfl2ljihVy7ogh7DeQSeYAUi0ubZ/yEueGtDfUPk6GH5LRw1hBdLq4IwUBWA==",
+ "dev": true
+ },
"node_modules/reusify": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -2300,6 +2536,20 @@
"node": ">= 0.8"
}
},
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/strip-ansi": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
@@ -2324,6 +2574,15 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/subarg": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz",
+ "integrity": "sha512-RIrIdRY0X1xojthNcVtgT9sjpOGagEUKpZdgBUi054OEPFo282yg+zE+t1Rj3+RqKq2xStL7uUHhY+AjbC4BXg==",
+ "dev": true,
+ "dependencies": {
+ "minimist": "^1.1.0"
+ }
+ },
"node_modules/superagent": {
"version": "8.1.2",
"resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz",
@@ -2376,6 +2635,15 @@
"integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
"dev": true
},
+ "node_modules/timestring": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/timestring/-/timestring-6.0.0.tgz",
+ "integrity": "sha512-wMctrWD2HZZLuIlchlkE2dfXJh7J2KDI9Dwl+2abPYg0mswQHfOAyQW3jJg1pY5VfttSINZuKcXoB3FGypVklA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/toidentifier": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
@@ -2479,6 +2747,21 @@
"node": ">= 0.4.0"
}
},
+ "node_modules/uuid": {
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
+ "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
+ "dev": true,
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/uuid-parse": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/uuid-parse/-/uuid-parse-1.1.0.tgz",
+ "integrity": "sha512-OdmXxA8rDsQ7YpNVbKSJkNzTw2I+S5WsbMDnCtIWSQaosNAcWtFuI/YK1TjzUI6nbkgiqEyh8gWngfcv8Asd9A==",
+ "dev": true
+ },
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
diff --git a/package.json b/package.json
index 1ba198419f..724cd7893e 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "@hubot-friends/express",
"description": "Fast, unopinionated, minimalist web framework",
- "version": "5.0.0",
+ "version": "0.0.0-development",
"author": "TJ Holowaychuk ",
"contributors": [
"Aaron Heckmann ",
@@ -63,6 +63,7 @@
},
"devDependencies": {
"after": "^0.8.2",
+ "autocannon": "^7.14.0",
"connect-redis": "^7.1.0",
"cookie-parser": "^1.4.6",
"cookie-session": "^2.0.0",
@@ -90,7 +91,8 @@
],
"scripts": {
"pretest": "eslint .",
- "test": "NODE_ENV=test NO_DEPRECATION='body-parser,express' node --test"
+ "test": "NODE_ENV=test NO_DEPRECATION='body-parser,express' node --test",
+ "benchmark": "node benchmarks/index.mjs"
},
"release": {
"branches": [