diff --git a/.eslintignore b/.eslintignore
index 628e08b..adadeb8 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -2,4 +2,5 @@ coverage
node_modules
__tests__
*.test.js
-dist
\ No newline at end of file
+dist
+benchmarks
\ No newline at end of file
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
new file mode 100644
index 0000000..7214836
--- /dev/null
+++ b/.github/workflows/benchmark.yml
@@ -0,0 +1,79 @@
+name: 'Benchmarks'
+
+# Perf numbers are noisy on shared runners, so benchmarks never gate PRs. They run on each
+# published release (and on demand) to refresh the Benchmarks section of the README.
+on:
+ release:
+ types: [published]
+ workflow_dispatch:
+ inputs:
+ version:
+ description: 'Version label to record (defaults to package.json version)'
+ required: false
+ type: string
+ commit:
+ description: 'Commit the refreshed README back to the default branch'
+ required: false
+ default: true
+ type: boolean
+
+permissions:
+ contents: write
+
+jobs:
+ benchmark:
+ name: 'Run benchmarks (Node 20)'
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout default branch
+ uses: actions/checkout@v4
+ with:
+ ref: ${{ github.event.repository.default_branch }}
+
+ - name: Set up Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+ cache: 'npm'
+
+ - name: Resolve version label
+ id: ver
+ run: |
+ RAW="${{ github.event.release.tag_name || inputs.version }}"
+ echo "value=${RAW#v}" >> "$GITHUB_OUTPUT"
+
+ - name: Install benchmark dependencies
+ working-directory: benchmarks
+ run: npm ci
+
+ - name: Run benchmarks and refresh README
+ working-directory: benchmarks
+ env:
+ LAMBDA_API_VERSION: ${{ steps.ver.outputs.value }}
+ run: node run.js --md results/RESULTS.md --json results/raw.json --update-readme
+
+ # Keep the committed README Prettier-clean so the push doesn't break main's CI.
+ - name: Format README
+ run: npx --yes prettier@2 --write README.md
+
+ - name: Upload raw results
+ uses: actions/upload-artifact@v4
+ with:
+ name: benchmark-results
+ path: benchmarks/results/
+
+ - name: Add results to job summary
+ run: cat benchmarks/results/RESULTS.md >> "$GITHUB_STEP_SUMMARY"
+
+ - name: Commit refreshed README
+ if: ${{ github.event_name == 'release' || inputs.commit }}
+ run: |
+ if git diff --quiet -- README.md; then
+ echo 'README.md unchanged — nothing to commit'
+ exit 0
+ fi
+ git config user.name 'github-actions[bot]'
+ git config user.email 'github-actions[bot]@users.noreply.github.com'
+ git add README.md
+ git commit -m 'docs: update benchmark results [skip ci]'
+ git push origin HEAD:${{ github.event.repository.default_branch }}
diff --git a/.prettierignore b/.prettierignore
index 628e08b..adadeb8 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -2,4 +2,5 @@ coverage
node_modules
__tests__
*.test.js
-dist
\ No newline at end of file
+dist
+benchmarks
\ No newline at end of file
diff --git a/README.md b/README.md
index 5fe5b88..7e1bd30 100644
--- a/README.md
+++ b/README.md
@@ -1523,3 +1523,73 @@ Contributions, ideas and bug reports are welcome and greatly appreciated. Please
## Are you using Lambda API?
If you're using Lambda API and finding it useful, hit me up on [Twitter](https://twitter.com/jeremy_daly) or email me at contact[at]jeremydaly.com. I'd love to hear your stories, ideas, and even your complaints!
+
+
+
+## Benchmarks
+
+In-process micro-benchmarks of lambda-api against other AWS Lambda web frameworks. The numbers measure **framework overhead only** (event → route → middleware → response, in a single Node VM) — not end-to-end Lambda timings. Absolute values vary by machine, so compare the **relative** ranking rather than the raw ops/sec. See [`benchmarks/`](./benchmarks) for the methodology and how to reproduce.
+
+_Generated 2026-06-26 21:24:41 UTC · lambda-api v0.0.0-development · Node 20.19.5 · Apple M4 Max (16 cores) · darwin/arm64_
+
+#### API Gateway REST (v1) — throughput (ops/sec, higher is better)
+
+| Framework | get-json | path-param | post-json | routing-50 | not-found |
+| ------------------------------ | --------- | ---------- | --------- | ---------- | --------- |
+| baseline | 5,654,874 | 5,513,012 | 2,560,533 | 4,493,436 | 4,620,989 |
+| middy `6.4.5` | 1,056,927 | 811,301 | 62,540 | 433,185 | 155,744 |
+| lambda-api `0.0.0-development` | 217,700 | 202,224 | 194,819 | 199,565 | 91,296 |
+| hono `4.12.27` | 58,370 | 57,559 | 38,469 | 60,441 | 61,732 |
+| fastify `5.8.5` | 51,240 | 52,873 | 13,598 | 56,465 | 48,915 |
+| serverless-express `4.22.2` | 26,732 | 28,082 | 15,997 | 26,940 | 28,643 |
+
+API Gateway REST (v1) — latency (avg / p99, µs, lower is better)
+
+| Framework | get-json | path-param | post-json | routing-50 | not-found |
+| ------------------------------ | ----------- | ----------- | ----------- | ----------- | ----------- |
+| baseline | 0.18 / 0.23 | 0.18 / 0.25 | 0.39 / 0.46 | 0.22 / 0.28 | 0.22 / 0.27 |
+| middy `6.4.5` | 0.95 / 2.29 | 1.23 / 1.41 | 16.0 / 16.3 | 2.31 / 2.45 | 6.42 / 6.56 |
+| lambda-api `0.0.0-development` | 4.59 / 4.74 | 4.95 / 5.14 | 5.13 / 5.28 | 5.01 / 5.63 | 11.0 / 11.0 |
+| hono `4.12.27` | 17.1 / 69.7 | 17.4 / 18.7 | 26.0 / 30.2 | 16.5 / 17.3 | 16.2 / 18.1 |
+| fastify `5.8.5` | 19.5 / 95.6 | 18.9 / 20.4 | 73.5 / 152 | 17.7 / 23.4 | 20.4 / 20.4 |
+| serverless-express `4.22.2` | 37.4 / 118 | 35.6 / 36.8 | 62.5 / 195 | 37.1 / 99.0 | 34.9 / 36.7 |
+
+
+
+#### API Gateway HTTP (v2) — throughput (ops/sec, higher is better)
+
+| Framework | get-json | path-param | post-json | routing-50 | not-found |
+| ------------------------------ | --------- | ---------- | --------- | ---------- | --------- |
+| baseline | 4,830,598 | 4,424,847 | 2,237,039 | 3,946,604 | 3,994,042 |
+| middy `6.4.5` | 1,032,952 | 791,846 | 55,802 | 372,581 | 148,189 |
+| lambda-api `0.0.0-development` | 201,969 | 187,395 | 183,887 | 195,975 | 88,721 |
+| hono `4.12.27` | 70,859 | 62,001 | 40,860 | 65,895 | 67,978 |
+| fastify `5.8.5` | 45,114 | 38,232 | 12,973 | 45,687 | 49,536 |
+| serverless-express `4.22.2` | 26,837 | 27,753 | 17,533 | 25,565 | 29,008 |
+
+API Gateway HTTP (v2) — latency (avg / p99, µs, lower is better)
+
+| Framework | get-json | path-param | post-json | routing-50 | not-found |
+| ------------------------------ | ----------- | ----------- | ----------- | ----------- | ----------- |
+| baseline | 0.21 / 0.47 | 0.23 / 0.44 | 0.45 / 0.71 | 0.25 / 0.48 | 0.25 / 0.51 |
+| middy `6.4.5` | 0.97 / 1.17 | 1.26 / 1.38 | 17.9 / 21.8 | 2.68 / 3.70 | 6.75 / 7.37 |
+| lambda-api `0.0.0-development` | 4.95 / 5.32 | 5.34 / 5.57 | 5.44 / 5.87 | 5.10 / 5.30 | 11.3 / 11.3 |
+| hono `4.12.27` | 14.1 / 14.2 | 16.1 / 19.9 | 24.5 / 26.9 | 15.2 / 16.4 | 14.7 / 15.2 |
+| fastify `5.8.5` | 22.2 / 20.0 | 26.2 / 30.9 | 77.1 / 248 | 21.9 / 30.3 | 20.2 / 25.6 |
+| serverless-express `4.22.2` | 37.3 / 46.0 | 36.0 / 38.1 | 57.0 / 144 | 39.1 / 40.9 | 34.5 / 35.5 |
+
+
+
+#### History
+
+Throughput for the `get-json` scenario on V2 events (ops/sec), one row per release:
+
+
+
+| version | date | node | baseline | lambda-api | fastify | hono | middy | express |
+| ----------------- | ---------- | ------- | --------- | ---------- | ------- | ------ | --------- | ------- |
+| 0.0.0-development | 2026-06-26 | 20.19.5 | 4,830,598 | 201,969 | 45,114 | 70,859 | 1,032,952 | 26,837 |
+
+
+
+
diff --git a/benchmarks/.gitignore b/benchmarks/.gitignore
new file mode 100644
index 0000000..219bdb2
--- /dev/null
+++ b/benchmarks/.gitignore
@@ -0,0 +1,3 @@
+node_modules/
+results/*.json
+results/*.md
diff --git a/benchmarks/README.md b/benchmarks/README.md
new file mode 100644
index 0000000..7a7132c
--- /dev/null
+++ b/benchmarks/README.md
@@ -0,0 +1,133 @@
+# lambda-api benchmarks
+
+A repeatable comparison of **lambda-api** against other popular frameworks running on AWS
+Lambda.
+
+This is an isolated package — its (heavy) comparison-framework dependencies live here and
+never touch the zero-dependency root library. It is excluded from the published npm tarball
+by the root `package.json` `files` whitelist.
+
+```bash
+cd benchmarks
+npm install
+npm run bench # print results to stdout
+npm run bench:md # also write results/RESULTS.md
+npm run bench:release # write md + json and refresh the README Benchmarks section
+```
+
+## What this measures
+
+Each framework's compiled `aws-lambda` handler is invoked **in-process** — in the same Node
+VM — with identical synthetic API Gateway events. We measure the **framework overhead** of a
+request: parse the event → match a route → run middleware → serialize the response. That is
+the work the maintainer clocked at ~0.68 ms per request, and the thing this suite resolves.
+
+For every `(framework × scenario × event format)` cell, the handler is first run once and its
+response is checked by a **correctness gate** (`lib/validate.js`) — status code, and body
+fields where applicable. Only cells that pass are timed, so we never publish a number for a
+framework that is silently 404-ing, throwing, or returning the wrong shape.
+
+Measurement uses [mitata](https://github.com/evanwashere/mitata), which calibrates clock
+overhead, warms up the JIT, and auto-tunes iteration counts for sub-microsecond accuracy.
+
+## Why in-process, and not LocalStack or a real Lambda deploy
+
+The signal we care about is sub-millisecond. LocalStack and real Lambda wrap that signal in:
+
+- Docker / Firecracker container startup and the Node runtime bootstrap (cold start),
+- the Lambda Runtime API request loop,
+- network latency to the function URL / API Gateway.
+
+That is **tens of milliseconds of noise** — 10–100× the thing we are trying to compare. Worse,
+LocalStack would be benchmarking the _emulator_, not the framework, and shared CI runners make
+absolute numbers non-reproducible. So the in-process harness is the right primary tool: it
+isolates framework overhead, is deterministic, needs no Docker, and runs in seconds.
+
+End-to-end Lambda timings (cold/warm `Duration`, `Init Duration`) are a **different, valid**
+question — see the [real-Lambda appendix](#appendix-end-to-end-real-lambda-numbers) below — but
+they answer "how fast is my whole deployment", not "how much overhead does the framework add".
+
+## Frameworks compared
+
+| key | package | adapter |
+| -------------------- | --------------------------------------- | ---------------------- |
+| `baseline` | — (hand-written handler) | none — the lower bound |
+| `lambda-api` | this repo (loaded from the working tree)| native `api.run()` |
+| `serverless-express` | `express` + `@vendia/serverless-express`| `serverlessExpress()` |
+| `fastify` | `fastify` + `@fastify/aws-lambda` | `awsLambdaFastify()` |
+| `hono` | `hono` | `hono/aws-lambda` |
+| `middy` | `@middy/core` + `@middy/http-router` | `httpRouterHandler()` |
+
+`baseline` is a raw handler with zero routing abstraction; every other framework's overhead is
+read as the gap above it.
+
+## Scenarios
+
+Each framework registers the **same** canonical routes, configured as minimally and equally as
+possible (JSON responses, no extra middleware). All scenarios run against both **API Gateway
+REST (v1)** and **HTTP API (v2)** events.
+
+| id | request | checks |
+| ------------ | ------------------------------------ | ----------------- |
+| `get-json` | `GET /` | 200, `{hello}` |
+| `path-param` | `GET /users/42` | 200, `{id:'42'}` |
+| `post-json` | `POST /users` with a JSON body | 200, echoes body |
+| `routing-50` | `GET /r49/x` (50 routes registered) | 200 — routing cost|
+| `not-found` | `GET /does-not-exist` | 404 |
+
+## Fairness notes
+
+- Frameworks are configured minimally and equivalently — the goal is to compare core overhead,
+ not feature sets. Defaults still differ (e.g. lambda-api computes an `ETag` and handles
+ serialization itself; Express needs an explicit `express.json()` and a 404 handler; Middy is a
+ middleware engine wired with only `http-router` + a per-route JSON body parser + an error
+ handler, and is otherwise a very thin layer), and those differences are part of what the numbers
+ reflect. Read the `frameworks/*.js` to see exactly how each is set up.
+- Absolute ops/sec depend on the machine. **Compare the relative ranking**, not raw numbers.
+- All frameworks run in one process by default. Pass `--framework ` to run a single one in
+ its own process — used for published numbers — to avoid cross-framework JIT interference.
+
+## CLI
+
+```
+node run.js [options]
+
+ --framework run a single framework (baseline | lambda-api | serverless-express
+ | fastify | hono | middy); also gives clean per-process JIT isolation
+ --md write the markdown report to a file (relative to benchmarks/)
+ --json dump raw per-cell stats as JSON
+ --update-readme refresh the Benchmarks section in ../README.md (full run only)
+```
+
+The version label recorded in the README history can be overridden with the
+`LAMBDA_API_VERSION` environment variable (the release workflow sets this from the release tag).
+
+## How the README stays up to date
+
+`.github/workflows/benchmark.yml` runs on every published release (and via manual dispatch). It
+runs the suite on Node 20, calls `--update-readme`, Prettier-formats the README, commits it back
+to the default branch, and uploads `results/` as an artifact. The Benchmarks section lives
+between `` / `` markers and is regenerated in
+place, with one history row appended per release. Move that marker block anywhere in the README
+once; future runs respect its position.
+
+## Adding a framework
+
+1. Create `frameworks/.js` exporting `{ name, version, build }`, where `build()` returns
+ (or resolves to) an async `(event, context) => apiGatewayResponse` handler with the canonical
+ routes registered. Use `await import()` for ESM-only packages (see `frameworks/hono.js`).
+2. Add `` to `ALL_FRAMEWORKS` in `run.js`.
+3. Add its dependency to `package.json` and re-run `npm install`.
+
+## Appendix: end-to-end (real Lambda) numbers
+
+To measure cold/warm start and total billed duration — which include infrastructure, not just
+framework overhead — deploy each `frameworks/*.js` handler behind API Gateway with AWS SAM or the
+Serverless Framework, then:
+
+- drive warm throughput with a load tool (e.g. `autocannon` / `artillery`) against the API URL, or
+- loop `aws lambda invoke` for controlled single invocations,
+- and read `Duration` / `Init Duration` from the CloudWatch `REPORT` log lines.
+
+These are deliberately **not** part of the in-process suite: they answer a different question and
+are not reproducible on shared CI. Label any such results clearly as end-to-end, infra-inclusive.
diff --git a/benchmarks/frameworks/baseline.js b/benchmarks/frameworks/baseline.js
new file mode 100644
index 0000000..52aca77
--- /dev/null
+++ b/benchmarks/frameworks/baseline.js
@@ -0,0 +1,64 @@
+'use strict';
+
+/**
+ * Baseline: a hand-written Lambda handler with zero framework abstraction.
+ *
+ * Establishes the theoretical lower bound for request handling — event parse, a manual
+ * route match, JSON serialize. Every framework's overhead is meaningfully read as the gap
+ * above this floor.
+ */
+
+const { ROUTE_COUNT } = require('../lib/scenarios');
+
+function pathOf(event) {
+ return event.rawPath || event.path;
+}
+
+function methodOf(event) {
+ return event.httpMethod || (event.requestContext && event.requestContext.http && event.requestContext.http.method);
+}
+
+// Emit the response shape lambda-api / the adapters produce for each event format.
+function reply(event, statusCode, payload) {
+ const body = JSON.stringify(payload);
+ if (event.version === '2.0') {
+ return { statusCode, headers: { 'content-type': 'application/json' }, body, isBase64Encoded: false };
+ }
+ return {
+ statusCode,
+ multiValueHeaders: { 'content-type': ['application/json'] },
+ body,
+ isBase64Encoded: false
+ };
+}
+
+const USER_RE = /^\/users\/([^/]+)$/;
+const ROUTE_RE = /^\/r(\d+)\/([^/]+)$/;
+
+function build() {
+ return async (event) => {
+ const path = pathOf(event);
+ const method = methodOf(event);
+
+ if (method === 'GET' && path === '/') return reply(event, 200, { hello: 'world' });
+
+ if (method === 'POST' && path === '/users') {
+ const parsed = event.body ? JSON.parse(event.body) : {};
+ return reply(event, 200, { created: parsed });
+ }
+
+ let match;
+ if (method === 'GET' && (match = USER_RE.exec(path))) {
+ return reply(event, 200, { id: match[1] });
+ }
+
+ if (method === 'GET' && (match = ROUTE_RE.exec(path))) {
+ const i = Number(match[1]);
+ if (i >= 0 && i < ROUTE_COUNT) return reply(event, 200, { i });
+ }
+
+ return reply(event, 404, { error: 'Not Found' });
+ };
+}
+
+module.exports = { name: 'baseline', version: '-', build };
diff --git a/benchmarks/frameworks/fastify.js b/benchmarks/frameworks/fastify.js
new file mode 100644
index 0000000..579a772
--- /dev/null
+++ b/benchmarks/frameworks/fastify.js
@@ -0,0 +1,31 @@
+'use strict';
+
+/**
+ * Fastify (via @fastify/aws-lambda) adapter for the benchmark suite.
+ */
+
+const Fastify = require('fastify');
+const awsLambdaFastify = require('@fastify/aws-lambda');
+const pkg = require('fastify/package.json');
+const { ROUTE_COUNT } = require('../lib/scenarios');
+
+async function build() {
+ const app = Fastify();
+
+ app.get('/', async () => ({ hello: 'world' }));
+ app.get('/users/:id', async (req) => ({ id: req.params.id }));
+ app.post('/users', async (req) => ({ created: req.body }));
+
+ for (let i = 0; i < ROUTE_COUNT; i++) {
+ const index = i;
+ app.get(`/r${i}/:p`, async () => ({ i: index }));
+ }
+
+ const proxy = awsLambdaFastify(app);
+ // Build the route tree before timing so the first measured call isn't paying init cost.
+ await app.ready();
+
+ return (event, context) => proxy(event, context);
+}
+
+module.exports = { name: 'fastify', version: pkg.version, build };
diff --git a/benchmarks/frameworks/hono.js b/benchmarks/frameworks/hono.js
new file mode 100644
index 0000000..c972d11
--- /dev/null
+++ b/benchmarks/frameworks/hono.js
@@ -0,0 +1,43 @@
+'use strict';
+
+/**
+ * Hono (via hono/aws-lambda) adapter for the benchmark suite.
+ *
+ * Hono ships as ESM-only, so it is loaded through a dynamic import() inside an async
+ * build() — keeping the rest of the suite plain CommonJS. The runner awaits build().
+ */
+
+const fs = require('fs');
+const path = require('path');
+const { ROUTE_COUNT } = require('../lib/scenarios');
+
+// hono's `exports` map blocks `require('hono/package.json')`, so read it from disk directly.
+function honoVersion() {
+ try {
+ const pkgPath = path.join(__dirname, '..', 'node_modules', 'hono', 'package.json');
+ return JSON.parse(fs.readFileSync(pkgPath, 'utf8')).version;
+ } catch (err) {
+ return 'unknown';
+ }
+}
+
+async function build() {
+ const { Hono } = await import('hono');
+ const { handle } = await import('hono/aws-lambda');
+
+ const app = new Hono();
+
+ app.get('/', (c) => c.json({ hello: 'world' }));
+ app.get('/users/:id', (c) => c.json({ id: c.req.param('id') }));
+ app.post('/users', async (c) => c.json({ created: await c.req.json() }));
+
+ for (let i = 0; i < ROUTE_COUNT; i++) {
+ const index = i;
+ app.get(`/r${i}/:p`, (c) => c.json({ i: index }));
+ }
+
+ const handler = handle(app);
+ return (event, context) => handler(event, context);
+}
+
+module.exports = { name: 'hono', version: honoVersion(), build };
diff --git a/benchmarks/frameworks/lambda-api.js b/benchmarks/frameworks/lambda-api.js
new file mode 100644
index 0000000..96ed48e
--- /dev/null
+++ b/benchmarks/frameworks/lambda-api.js
@@ -0,0 +1,29 @@
+'use strict';
+
+/**
+ * lambda-api adapter for the benchmark suite.
+ *
+ * Loaded directly from the repository working tree (../../), so `npm run bench` always
+ * measures the local source — exactly what a maintainer iterating on performance wants.
+ */
+
+const createAPI = require('../../');
+const pkg = require('../../package.json');
+const { ROUTE_COUNT } = require('../lib/scenarios');
+
+function build() {
+ const api = createAPI();
+
+ api.get('/', (req, res) => res.json({ hello: 'world' }));
+ api.get('/users/:id', (req, res) => res.json({ id: req.params.id }));
+ api.post('/users', (req, res) => res.json({ created: req.body }));
+
+ for (let i = 0; i < ROUTE_COUNT; i++) {
+ const index = i;
+ api.get(`/r${i}/:p`, (req, res) => res.json({ i: index }));
+ }
+
+ return (event, context) => api.run(event, context);
+}
+
+module.exports = { name: 'lambda-api', version: pkg.version, build };
diff --git a/benchmarks/frameworks/middy.js b/benchmarks/frameworks/middy.js
new file mode 100644
index 0000000..67be243
--- /dev/null
+++ b/benchmarks/frameworks/middy.js
@@ -0,0 +1,59 @@
+'use strict';
+
+/**
+ * Middy (@middy/core) adapter for the benchmark suite.
+ *
+ * Middy is a middleware engine, not a router, so a fair comparison adds the official
+ * http-router (routing + path params), http-json-body-parser (the POST body), and
+ * http-error-handler (the 404). These packages are ESM-only, so they are loaded through a
+ * dynamic import() inside an async build() — the runner awaits build().
+ */
+
+const fs = require('fs');
+const path = require('path');
+const { ROUTE_COUNT } = require('../lib/scenarios');
+
+function middyVersion() {
+ try {
+ const pkgPath = path.join(__dirname, '..', 'node_modules', '@middy', 'core', 'package.json');
+ return JSON.parse(fs.readFileSync(pkgPath, 'utf8')).version;
+ } catch (err) {
+ return 'unknown';
+ }
+}
+
+async function build() {
+ const middy = (await import('@middy/core')).default;
+ const httpRouterHandler = (await import('@middy/http-router')).default;
+ const httpJsonBodyParser = (await import('@middy/http-json-body-parser')).default;
+ const httpErrorHandler = (await import('@middy/http-error-handler')).default;
+
+ const json = (statusCode, payload) => ({ statusCode, body: JSON.stringify(payload) });
+
+ const routes = [
+ { method: 'GET', path: '/', handler: async () => json(200, { hello: 'world' }) },
+ {
+ method: 'GET',
+ path: '/users/{id}',
+ handler: async (event) => json(200, { id: event.pathParameters.id })
+ },
+ {
+ method: 'POST',
+ path: '/users',
+ handler: middy(async (event) => json(200, { created: event.body })).use(httpJsonBodyParser())
+ }
+ ];
+
+ for (let i = 0; i < ROUTE_COUNT; i++) {
+ const index = i;
+ routes.push({ method: 'GET', path: `/r${i}/{p}`, handler: async () => json(200, { i: index }) });
+ }
+
+ // logger:false — http-router throws a 404 for unmatched routes; without this the
+ // not-found scenario would spam stderr on every timed iteration.
+ const handler = middy(httpRouterHandler(routes)).use(httpErrorHandler({ logger: false }));
+
+ return (event, context) => handler(event, context);
+}
+
+module.exports = { name: 'middy', version: middyVersion(), build };
diff --git a/benchmarks/frameworks/serverless-express.js b/benchmarks/frameworks/serverless-express.js
new file mode 100644
index 0000000..8d5779c
--- /dev/null
+++ b/benchmarks/frameworks/serverless-express.js
@@ -0,0 +1,39 @@
+'use strict';
+
+/**
+ * Express (via @vendia/serverless-express) adapter for the benchmark suite.
+ *
+ * Represents the common "port my Express app to Lambda" path. Configured minimally —
+ * only the JSON body parser middleware — to keep the comparison fair.
+ */
+
+const express = require('express');
+const serverlessExpress = require('@vendia/serverless-express');
+const pkg = require('express/package.json');
+const { ROUTE_COUNT } = require('../lib/scenarios');
+
+function build() {
+ const app = express();
+ app.use(express.json());
+
+ app.get('/', (req, res) => res.json({ hello: 'world' }));
+ app.get('/users/:id', (req, res) => res.json({ id: req.params.id }));
+ app.post('/users', (req, res) => res.json({ created: req.body }));
+
+ for (let i = 0; i < ROUTE_COUNT; i++) {
+ const index = i;
+ app.get(`/r${i}/:p`, (req, res) => res.json({ i: index }));
+ }
+
+ // Explicit 404 — every Express-on-Lambda app needs one, and it keeps unmatched routes off
+ // Express's finalhandler path, which `on-finished` can't attach to serverless-express's
+ // mock socket (it would otherwise 500). Mirrors the built-in 404 of the other frameworks.
+ app.use((req, res) => res.status(404).json({ error: 'Not Found' }));
+
+ const handler = serverlessExpress({ app });
+
+ // @vendia/serverless-express v4 returns a promise when invoked without a callback.
+ return (event, context) => handler(event, context);
+}
+
+module.exports = { name: 'serverless-express', version: pkg.version, build };
diff --git a/benchmarks/lib/events.js b/benchmarks/lib/events.js
new file mode 100644
index 0000000..906065a
--- /dev/null
+++ b/benchmarks/lib/events.js
@@ -0,0 +1,104 @@
+'use strict';
+
+/**
+ * Synthetic API Gateway event builders for the benchmark suite.
+ *
+ * Seeded from the real shapes in __tests__/sample-event-apigateway-v{1,2}.json but
+ * trimmed to the fields that lambda-api and the comparison adapters actually read, so
+ * every framework performs equivalent work. The method / path / body are overridden per
+ * scenario; everything else is realistic boilerplate.
+ */
+
+const V1_HEADERS = {
+ 'content-type': 'application/json',
+ accept: 'application/json',
+ host: 'wt6mne2s9k.execute-api.us-west-2.amazonaws.com',
+ 'user-agent': 'lambda-api-benchmarks',
+ 'x-forwarded-for': '192.168.100.1',
+ 'x-forwarded-port': '443',
+ 'x-forwarded-proto': 'https'
+};
+
+const V2_HEADERS = {
+ 'content-type': 'application/json',
+ accept: 'application/json',
+ host: 'id.execute-api.us-east-1.amazonaws.com',
+ 'user-agent': 'lambda-api-benchmarks',
+ 'x-forwarded-for': '192.168.100.1',
+ 'x-forwarded-port': '443',
+ 'x-forwarded-proto': 'https'
+};
+
+function serializeBody(body) {
+ if (body === null || body === undefined) return null;
+ return typeof body === 'string' ? body : JSON.stringify(body);
+}
+
+/**
+ * Build an API Gateway REST (v1 / proxy) event.
+ * @param {{ method?: string, path?: string, body?: any }} opts
+ */
+function apiGatewayV1({ method = 'GET', path = '/', body = null } = {}) {
+ const multiValueHeaders = {};
+ for (const key of Object.keys(V1_HEADERS)) multiValueHeaders[key] = [V1_HEADERS[key]];
+
+ return {
+ resource: path,
+ path,
+ httpMethod: method,
+ headers: { ...V1_HEADERS },
+ multiValueHeaders,
+ queryStringParameters: null,
+ multiValueQueryStringParameters: null,
+ pathParameters: null,
+ stageVariables: null,
+ requestContext: {
+ accountId: '123456789012',
+ stage: 'test',
+ httpMethod: method,
+ path,
+ resourcePath: path,
+ identity: { sourceIp: '192.168.100.12' },
+ requestId: 'bench-request-id',
+ apiId: 'wt6mne2s9k'
+ },
+ body: serializeBody(body),
+ isBase64Encoded: false
+ };
+}
+
+/**
+ * Build an API Gateway HTTP (v2) event.
+ * @param {{ method?: string, path?: string, body?: any }} opts
+ */
+function apiGatewayV2({ method = 'GET', path = '/', body = null } = {}) {
+ return {
+ version: '2.0',
+ routeKey: '$default',
+ rawPath: path,
+ rawQueryString: '',
+ headers: { ...V2_HEADERS },
+ requestContext: {
+ accountId: '123456789012',
+ apiId: 'api-id',
+ domainName: 'id.execute-api.us-east-1.amazonaws.com',
+ domainPrefix: 'id',
+ http: {
+ method,
+ path,
+ protocol: 'HTTP/1.1',
+ sourceIp: '192.168.100.12',
+ userAgent: 'lambda-api-benchmarks'
+ },
+ requestId: 'bench-request-id',
+ routeKey: '$default',
+ stage: '$default',
+ time: '12/Mar/2020:19:03:58 +0000',
+ timeEpoch: 1583348638390
+ },
+ body: serializeBody(body),
+ isBase64Encoded: false
+ };
+}
+
+module.exports = { apiGatewayV1, apiGatewayV2 };
diff --git a/benchmarks/lib/scenarios.js b/benchmarks/lib/scenarios.js
new file mode 100644
index 0000000..742e3af
--- /dev/null
+++ b/benchmarks/lib/scenarios.js
@@ -0,0 +1,57 @@
+'use strict';
+
+/**
+ * Shared benchmark scenarios. Every framework registers the same canonical routes
+ * (see frameworks/*.js) and is exercised with the same set of requests, against both
+ * API Gateway v1 and v2 events.
+ *
+ * `expect` is the correctness gate (lib/validate.js): a handler must produce this exact
+ * status (and matching body fields, when given) before it is timed — so we never publish
+ * numbers for a framework that is silently 404-ing, throwing, or returning the wrong shape.
+ *
+ * The 50 `r{i}/:p` routes (registered by each framework) make `routing-50` a meaningful
+ * test of routing cost as the route table grows.
+ */
+
+const ROUTE_COUNT = 50;
+
+const scenarios = [
+ {
+ id: 'get-json',
+ description: 'simplest GET returning a small JSON object',
+ method: 'GET',
+ path: '/',
+ expect: { status: 200, body: { hello: 'world' } }
+ },
+ {
+ id: 'path-param',
+ description: 'GET with a single path parameter',
+ method: 'GET',
+ path: '/users/42',
+ expect: { status: 200, body: { id: '42' } }
+ },
+ {
+ id: 'post-json',
+ description: 'POST that parses a JSON body and echoes it',
+ method: 'POST',
+ path: '/users',
+ body: { name: 'ada' },
+ expect: { status: 200, body: { created: { name: 'ada' } } }
+ },
+ {
+ id: 'routing-50',
+ description: 'routing cost with 50 registered routes (hits the last one)',
+ method: 'GET',
+ path: '/r' + (ROUTE_COUNT - 1) + '/x',
+ expect: { status: 200, body: { i: ROUTE_COUNT - 1 } }
+ },
+ {
+ id: 'not-found',
+ description: 'unmatched route returns 404',
+ method: 'GET',
+ path: '/does-not-exist',
+ expect: { status: 404 }
+ }
+];
+
+module.exports = { scenarios, ROUTE_COUNT };
diff --git a/benchmarks/lib/table.js b/benchmarks/lib/table.js
new file mode 100644
index 0000000..7155e3b
--- /dev/null
+++ b/benchmarks/lib/table.js
@@ -0,0 +1,113 @@
+'use strict';
+
+/**
+ * Renders benchmark results into GitHub-flavored markdown.
+ *
+ * Layout: one throughput table per event format (rows = framework, columns = scenario),
+ * each followed by a collapsible latency table (avg / p99). Rows are sorted by the
+ * `get-json` throughput, descending.
+ */
+
+const { scenarios } = require('./scenarios');
+
+const FORMAT_LABELS = {
+ v1: 'API Gateway REST (v1)',
+ v2: 'API Gateway HTTP (v2)'
+};
+
+function opsPerSec(stats) {
+ return stats && stats.avg ? 1e9 / stats.avg : 0;
+}
+
+function fmtOps(n) {
+ if (!n) return 'n/a';
+ return Math.round(n).toLocaleString('en-US');
+}
+
+function fmtUs(ns) {
+ if (!ns) return 'n/a';
+ const us = ns / 1000;
+ if (us >= 100) return us.toFixed(0);
+ if (us >= 10) return us.toFixed(1);
+ return us.toFixed(2);
+}
+
+function cell(results, framework, format, scenarioId) {
+ return results.find(
+ (r) => r.framework === framework && r.format === format && r.scenario === scenarioId
+ );
+}
+
+// Unique framework metadata ({ name, version }) in the order first seen.
+function frameworkMeta(results) {
+ const seen = new Map();
+ for (const r of results) {
+ if (!seen.has(r.framework)) seen.set(r.framework, { name: r.framework, version: r.version });
+ }
+ return [...seen.values()];
+}
+
+function label(meta) {
+ return meta.version && meta.version !== '-' ? `${meta.name} \`${meta.version}\`` : meta.name;
+}
+
+function toMarkdown(header, rows) {
+ const lines = [];
+ lines.push(`| ${header.join(' | ')} |`);
+ lines.push(`| ${header.map(() => '---').join(' | ')} |`);
+ for (const row of rows) lines.push(`| ${row.join(' | ')} |`);
+ return lines.join('\n');
+}
+
+function buildTable(results, frameworks, format, render) {
+ const ordered = [...frameworks].sort((a, b) => {
+ const ca = cell(results, a.name, format, 'get-json');
+ const cb = cell(results, b.name, format, 'get-json');
+ return opsPerSec(cb && cb.stats) - opsPerSec(ca && ca.stats);
+ });
+
+ const header = ['Framework', ...scenarios.map((s) => s.id)];
+ const rows = ordered.map((meta) => {
+ const cells = scenarios.map((s) => {
+ const c = cell(results, meta.name, format, s.id);
+ return c && c.ok && c.stats ? render(c.stats) : 'n/a';
+ });
+ return [label(meta), ...cells];
+ });
+ return toMarkdown(header, rows);
+}
+
+function envHeader(env) {
+ return (
+ `_Generated ${env.date} · lambda-api v${env.lambdaApiVersion} · ` +
+ `Node ${env.node} · ${env.cpu} · ${env.platform}/${env.arch}_`
+ );
+}
+
+/**
+ * @param {Array} results - the runner's result rows
+ * @param {object} env - environment metadata from the runner
+ * @param {string[]} formats - event format ids to render (default ['v1','v2'])
+ * @returns {string} markdown
+ */
+function renderTables(results, env, formats = ['v1', 'v2']) {
+ const frameworks = frameworkMeta(results);
+ const out = [envHeader(env), ''];
+
+ for (const format of formats) {
+ out.push(`#### ${FORMAT_LABELS[format] || format} — throughput (ops/sec, higher is better)`);
+ out.push('');
+ out.push(buildTable(results, frameworks, format, (s) => fmtOps(opsPerSec(s))));
+ out.push('');
+ out.push(`${FORMAT_LABELS[format] || format} — latency (avg / p99, µs, lower is better)
`);
+ out.push('');
+ out.push(buildTable(results, frameworks, format, (s) => `${fmtUs(s.avg)} / ${fmtUs(s.p99)}`));
+ out.push('');
+ out.push(' ');
+ out.push('');
+ }
+
+ return out.join('\n').trim() + '\n';
+}
+
+module.exports = { renderTables, opsPerSec, fmtOps, fmtUs, FORMAT_LABELS };
diff --git a/benchmarks/lib/update-readme.js b/benchmarks/lib/update-readme.js
new file mode 100644
index 0000000..4d90c00
--- /dev/null
+++ b/benchmarks/lib/update-readme.js
@@ -0,0 +1,154 @@
+'use strict';
+
+/**
+ * Idempotently writes the Benchmarks section into the root README.md.
+ *
+ * The whole section lives between BENCHMARKS:START / BENCHMARKS:END markers and is
+ * regenerated in place on every run (so the README — already large — never accumulates
+ * duplicate sections). Inside it, a compact History table keeps one row per lambda-api
+ * version: a new version appends a row; re-running for the same version replaces its row.
+ * If the markers are absent, the section is appended once at the end of the file.
+ */
+
+const fs = require('fs');
+const { renderTables, opsPerSec, fmtOps } = require('./table');
+
+const START = '';
+const END = '';
+const H_START = '';
+const H_END = '';
+
+// Representative cell used for the compact history table.
+const HISTORY_FORMAT = 'v2';
+const HISTORY_SCENARIO = 'get-json';
+const HISTORY_FRAMEWORKS = [
+ { name: 'baseline', header: 'baseline' },
+ { name: 'lambda-api', header: 'lambda-api' },
+ { name: 'fastify', header: 'fastify' },
+ { name: 'hono', header: 'hono' },
+ { name: 'middy', header: 'middy' },
+ { name: 'serverless-express', header: 'express' }
+];
+
+function rowCells(line) {
+ return line
+ .split('|')
+ .slice(1, -1)
+ .map((c) => c.trim());
+}
+
+function stripVersion(v) {
+ return String(v).replace(/`/g, '').trim();
+}
+
+function extractHistoryRows(readme) {
+ const s = readme.indexOf(H_START);
+ const e = readme.indexOf(H_END);
+ if (s === -1 || e === -1 || e < s) return [];
+ const block = readme.slice(s + H_START.length, e);
+ const lines = block
+ .split('\n')
+ .map((l) => l.trim())
+ .filter((l) => l.startsWith('|'));
+ // [0] = header, [1] = separator, rest = data rows
+ return lines
+ .slice(2)
+ .filter((l) => !/^\|\s*-/.test(l))
+ .map(rowCells);
+}
+
+function opsFor(results, framework) {
+ const c = results.find(
+ (r) => r.framework === framework && r.format === HISTORY_FORMAT && r.scenario === HISTORY_SCENARIO
+ );
+ return c && c.ok && c.stats ? fmtOps(opsPerSec(c.stats)) : 'n/a';
+}
+
+function buildHistoryRow(results, env) {
+ return [
+ env.lambdaApiVersion,
+ env.dateShort,
+ env.node,
+ ...HISTORY_FRAMEWORKS.map((f) => opsFor(results, f.name))
+ ];
+}
+
+function renderHistoryTable(prevRows, current) {
+ const header = ['version', 'date', 'node', ...HISTORY_FRAMEWORKS.map((f) => f.header)];
+ const rows = [];
+ let replaced = false;
+ for (const r of prevRows) {
+ if (stripVersion(r[0]) === stripVersion(current[0])) {
+ rows.push(current);
+ replaced = true;
+ } else {
+ rows.push(r);
+ }
+ }
+ if (!replaced) rows.push(current);
+
+ const lines = [
+ H_START,
+ `| ${header.join(' | ')} |`,
+ `| ${header.map(() => '---').join(' | ')} |`,
+ ...rows.map((r) => `| ${r.join(' | ')} |`),
+ H_END
+ ];
+ return lines.join('\n');
+}
+
+function buildSection(results, env, historyTable) {
+ return [
+ START,
+ '## Benchmarks',
+ '',
+ 'In-process micro-benchmarks of lambda-api against other AWS Lambda web frameworks. ' +
+ 'The numbers measure **framework overhead only** (event → route → middleware → response, ' +
+ 'in a single Node VM) — not end-to-end Lambda timings. Absolute values vary by machine, so ' +
+ 'compare the **relative** ranking rather than the raw ops/sec. See ' +
+ '[`benchmarks/`](./benchmarks) for the methodology and how to reproduce.',
+ '',
+ renderTables(results, env).trim(),
+ '',
+ '#### History',
+ '',
+ `Throughput for the \`${HISTORY_SCENARIO}\` scenario on ${HISTORY_FORMAT.toUpperCase()} events (ops/sec), one row per release:`,
+ '',
+ historyTable,
+ '',
+ END
+ ].join('\n');
+}
+
+/**
+ * @param {{ readmePath: string, results: Array, env: object }} args
+ * @returns {{ changed: boolean, action: 'replaced'|'appended' }}
+ */
+function updateReadme({ readmePath, results, env }) {
+ const existing = fs.existsSync(readmePath) ? fs.readFileSync(readmePath, 'utf8') : '';
+
+ const prevRows = extractHistoryRows(existing);
+ const current = buildHistoryRow(results, env);
+ const historyTable = renderHistoryTable(prevRows, current);
+ const section = buildSection(results, env, historyTable);
+
+ const s = existing.indexOf(START);
+ const e = existing.indexOf(END);
+
+ let next;
+ let action;
+ if (s !== -1 && e !== -1 && e > s) {
+ next = existing.slice(0, s) + section + existing.slice(e + END.length);
+ action = 'replaced';
+ } else {
+ const prefix = existing.length ? existing.replace(/\s*$/, '') + '\n\n' : '';
+ next = prefix + section + '\n';
+ action = 'appended';
+ }
+
+ const changed = next !== existing;
+ if (changed) fs.writeFileSync(readmePath, next);
+ return { changed, action };
+}
+
+module.exports = { updateReadme, START, END };
diff --git a/benchmarks/lib/validate.js b/benchmarks/lib/validate.js
new file mode 100644
index 0000000..a68eeee
--- /dev/null
+++ b/benchmarks/lib/validate.js
@@ -0,0 +1,46 @@
+'use strict';
+
+/**
+ * Correctness gate. Runs once per (framework, scenario, event format) BEFORE timing.
+ *
+ * Guarantees we never benchmark a handler that is silently returning the wrong status,
+ * throwing, or emitting the wrong body — a classic way framework comparisons become
+ * dishonest. Throws a descriptive Error on any mismatch; the runner catches it, marks
+ * that cell as failed, and excludes it from the results (rather than crashing the suite).
+ */
+
+/**
+ * @param {object} response - the Lambda proxy response returned by the handler
+ * @param {{ status: number, body?: object }} expect - expected status and (optional) body fields
+ * @param {string} ctx - "framework/format/scenario" label for error messages
+ */
+function validate(response, expect, ctx) {
+ if (!response || typeof response !== 'object') {
+ throw new Error(`${ctx}: handler returned ${typeof response}, expected a response object`);
+ }
+
+ if (response.statusCode !== expect.status) {
+ throw new Error(`${ctx}: expected statusCode ${expect.status}, got ${response.statusCode}`);
+ }
+
+ if (expect.body) {
+ let parsed;
+ try {
+ parsed = JSON.parse(response.body);
+ } catch (err) {
+ throw new Error(`${ctx}: response body is not valid JSON (${err.message}): ${response.body}`);
+ }
+
+ for (const key of Object.keys(expect.body)) {
+ const got = JSON.stringify(parsed[key]);
+ const want = JSON.stringify(expect.body[key]);
+ if (got !== want) {
+ throw new Error(`${ctx}: body.${key} expected ${want}, got ${got}`);
+ }
+ }
+ }
+
+ return true;
+}
+
+module.exports = validate;
diff --git a/benchmarks/package-lock.json b/benchmarks/package-lock.json
new file mode 100644
index 0000000..1106ca7
--- /dev/null
+++ b/benchmarks/package-lock.json
@@ -0,0 +1,1572 @@
+{
+ "name": "lambda-api-benchmarks",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "lambda-api-benchmarks",
+ "version": "0.0.0",
+ "dependencies": {
+ "@fastify/aws-lambda": "^6.4.0",
+ "@middy/core": "^6.4.3",
+ "@middy/http-error-handler": "^6.4.3",
+ "@middy/http-json-body-parser": "^6.4.3",
+ "@middy/http-router": "^6.4.3",
+ "@vendia/serverless-express": "^4.12.6",
+ "express": "^4.21.2",
+ "fastify": "^5.8.5",
+ "hono": "^4.12.27",
+ "mitata": "^1.0.34"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/@codegenie/serverless-express": {
+ "version": "4.17.1",
+ "resolved": "https://registry.npmjs.org/@codegenie/serverless-express/-/serverless-express-4.17.1.tgz",
+ "integrity": "sha512-B/4RRtVK2iAp5ho+qoUFxUeMaWCgSP+hNrdLJV3DWKZ1E9n9oLKdsJ6W9LQekZsD8rGD15hDadIKWKKw5i3f3A==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@fastify/ajv-compiler": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.5.tgz",
+ "integrity": "sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.12.0",
+ "ajv-formats": "^3.0.1",
+ "fast-uri": "^3.0.0"
+ }
+ },
+ "node_modules/@fastify/aws-lambda": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/@fastify/aws-lambda/-/aws-lambda-6.4.0.tgz",
+ "integrity": "sha512-7iCx4Bl+SoeeRP0ALv1Rg4AR63yBW8XxgQiQ4OrTJApUObMzMtN1VPFtnoAqGhtpruAeVUTxgGpsW1hasYxU8Q==",
+ "license": "MIT"
+ },
+ "node_modules/@fastify/error": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.2.0.tgz",
+ "integrity": "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/@fastify/fast-json-stringify-compiler": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.3.tgz",
+ "integrity": "sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "fast-json-stringify": "^6.0.0"
+ }
+ },
+ "node_modules/@fastify/forwarded": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.1.tgz",
+ "integrity": "sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/@fastify/merge-json-schemas": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz",
+ "integrity": "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.3"
+ }
+ },
+ "node_modules/@fastify/proxy-addr": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.1.0.tgz",
+ "integrity": "sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/forwarded": "^3.0.0",
+ "ipaddr.js": "^2.1.0"
+ }
+ },
+ "node_modules/@middy/core": {
+ "version": "6.4.5",
+ "resolved": "https://registry.npmjs.org/@middy/core/-/core-6.4.5.tgz",
+ "integrity": "sha512-qRGCslDHjMr08fywcfVbWR9qpx16vGD481i9GpX3r5efi8Arjp/44JTjfeJkJJxvIb/8/+E9MLvU86+3oe1oJQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/willfarrell"
+ }
+ },
+ "node_modules/@middy/http-error-handler": {
+ "version": "6.4.5",
+ "resolved": "https://registry.npmjs.org/@middy/http-error-handler/-/http-error-handler-6.4.5.tgz",
+ "integrity": "sha512-0El5exXIl5QW47kQQTx1lVk0dZC961hGK6TVd+jtQAv9tpN6ibZQaRI7CaL8b+bmAgsdsxZQmEqoLOBhhW5gQQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@middy/util": "6.4.5"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/willfarrell"
+ }
+ },
+ "node_modules/@middy/http-json-body-parser": {
+ "version": "6.4.5",
+ "resolved": "https://registry.npmjs.org/@middy/http-json-body-parser/-/http-json-body-parser-6.4.5.tgz",
+ "integrity": "sha512-laK78DjphVJTDNPYFy2v7B6Tj0Pby96+HBxJ3AE9De1WcfKkeFbII+K0WR2KJND8+61fzHPWnPCGoRWB7AD3RQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@middy/util": "6.4.5"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/willfarrell"
+ }
+ },
+ "node_modules/@middy/http-router": {
+ "version": "6.4.5",
+ "resolved": "https://registry.npmjs.org/@middy/http-router/-/http-router-6.4.5.tgz",
+ "integrity": "sha512-hYaZgNu53Po0VAfMlnvrHXoYLXRVUTrEJ/0FlY7RS3EqBp+hpTibNT2N7icc8M/FewqAFAWBvI4CLINrzS3xgA==",
+ "license": "MIT",
+ "dependencies": {
+ "@middy/util": "6.4.5"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/willfarrell"
+ }
+ },
+ "node_modules/@middy/util": {
+ "version": "6.4.5",
+ "resolved": "https://registry.npmjs.org/@middy/util/-/util-6.4.5.tgz",
+ "integrity": "sha512-170ml5Q8QIHjric+ynwmDoa1sbcaMkUJumpUNxubqzStN7rEebVHnzXLQBwvSAhp7Fhcja7q2JLo0yjHpyPO4Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/willfarrell"
+ }
+ },
+ "node_modules/@pinojs/redact": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz",
+ "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==",
+ "license": "MIT"
+ },
+ "node_modules/@vendia/serverless-express": {
+ "version": "4.12.6",
+ "resolved": "https://registry.npmjs.org/@vendia/serverless-express/-/serverless-express-4.12.6.tgz",
+ "integrity": "sha512-ePsIPk3VQwgm5nh/JGBtTKQs5ZOF7REjHxC+PKk/CHvhlKQkJuUU365uPOlxuLJhC+BAefDznDRReWxpnKjmYg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@codegenie/serverless-express": "^4.12.5"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/abstract-logging": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
+ "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==",
+ "license": "MIT"
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.20.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz",
+ "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
+ "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+ "license": "MIT"
+ },
+ "node_modules/atomic-sleep": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
+ "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/avvio": {
+ "version": "9.2.0",
+ "resolved": "https://registry.npmjs.org/avvio/-/avvio-9.2.0.tgz",
+ "integrity": "sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/error": "^4.0.0",
+ "fastq": "^1.17.1"
+ }
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.5",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
+ "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "content-type": "~1.0.5",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "~1.2.0",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "on-finished": "~2.4.1",
+ "qs": "~6.15.1",
+ "raw-body": "~2.5.3",
+ "type-is": "~1.6.18",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
+ "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.22.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz",
+ "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "~1.20.5",
+ "content-disposition": "~0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "~0.7.1",
+ "cookie-signature": "~1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.3.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.0",
+ "merge-descriptors": "1.0.3",
+ "methods": "~1.1.2",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "~0.1.12",
+ "proxy-addr": "~2.0.7",
+ "qs": "~6.15.1",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "~0.19.0",
+ "serve-static": "~1.16.2",
+ "setprototypeof": "1.2.0",
+ "statuses": "~2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/fast-decode-uri-component": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz",
+ "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==",
+ "license": "MIT"
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stringify": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-6.4.0.tgz",
+ "integrity": "sha512-ibRCQ0GZKJIQ+P3Et1h0LhPgp3PMTYk0MH8O+kW3lNYsvmaQww5Nn3f1jf73Q0jR1Yz3a1CDP4/NZD3vOajWJQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/merge-json-schemas": "^0.2.0",
+ "ajv": "^8.12.0",
+ "ajv-formats": "^3.0.1",
+ "fast-uri": "^3.0.0",
+ "json-schema-ref-resolver": "^3.0.0",
+ "rfdc": "^1.2.0"
+ }
+ },
+ "node_modules/fast-querystring": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz",
+ "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-decode-uri-component": "^1.0.1"
+ }
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz",
+ "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/fastify": {
+ "version": "5.8.5",
+ "resolved": "https://registry.npmjs.org/fastify/-/fastify-5.8.5.tgz",
+ "integrity": "sha512-Yqptv59pQzPgQUSIm87hMqHJmdkb1+GPxdE6vW6FRyVE9G86mt7rOghitiU4JHRaTyDUk9pfeKmDeu70lAwM4Q==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@fastify/ajv-compiler": "^4.0.5",
+ "@fastify/error": "^4.0.0",
+ "@fastify/fast-json-stringify-compiler": "^5.0.0",
+ "@fastify/proxy-addr": "^5.0.0",
+ "abstract-logging": "^2.0.1",
+ "avvio": "^9.0.0",
+ "fast-json-stringify": "^6.0.0",
+ "find-my-way": "^9.0.0",
+ "light-my-request": "^6.0.0",
+ "pino": "^9.14.0 || ^10.1.0",
+ "process-warning": "^5.0.0",
+ "rfdc": "^1.3.1",
+ "secure-json-parse": "^4.0.0",
+ "semver": "^7.6.0",
+ "toad-cache": "^3.7.0"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+ "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
+ "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "~2.0.2",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/find-my-way": {
+ "version": "9.6.0",
+ "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.6.0.tgz",
+ "integrity": "sha512-Zf4Xve4RymLl7NgaavNebZ01joJ8MfVerOG43wy7SHLO+r+K0C6d/SE0BiR7AV5V1VOCFlOP7ecdo+I4qmiHrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-querystring": "^1.0.0",
+ "safe-regex2": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hono": {
+ "version": "4.12.27",
+ "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.27.tgz",
+ "integrity": "sha512-1yrb/+w6HWQJrUCLkJ2IF5jNIPvvFkblV5RNOYl6bV+OA6p9GLcMpHFFGTosSvHvcAUibuUukRqhlYI4z32C7Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.9.0"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/ipaddr.js": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.4.0.tgz",
+ "integrity": "sha512-9VGk3HGanVE6JoZXHiCpnGy5X0jYDnN4EA4lntFPj+1vIWlFhIylq2CrrCOJH9EAhc5CYhq18F2Av2tgoAPsYQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/json-schema-ref-resolver": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-3.0.0.tgz",
+ "integrity": "sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.3"
+ }
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "license": "MIT"
+ },
+ "node_modules/light-my-request": {
+ "version": "6.6.0",
+ "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-6.6.0.tgz",
+ "integrity": "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "cookie": "^1.0.1",
+ "process-warning": "^4.0.0",
+ "set-cookie-parser": "^2.6.0"
+ }
+ },
+ "node_modules/light-my-request/node_modules/cookie": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+ "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/light-my-request/node_modules/process-warning": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz",
+ "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mitata": {
+ "version": "1.0.34",
+ "resolved": "https://registry.npmjs.org/mitata/-/mitata-1.0.34.tgz",
+ "integrity": "sha512-Mc3zrtNBKIMeHSCQ0XqRLo1vbdIx1wvFV9c8NJAiyho6AjNfMY8bVhbS12bwciUdd1t4rj8099CH3N3NFahaUA==",
+ "license": "MIT"
+ },
+ "node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-exit-leak-free": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz",
+ "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
+ "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
+ "license": "MIT"
+ },
+ "node_modules/pino": {
+ "version": "10.3.1",
+ "resolved": "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz",
+ "integrity": "sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg==",
+ "license": "MIT",
+ "dependencies": {
+ "@pinojs/redact": "^0.4.0",
+ "atomic-sleep": "^1.0.0",
+ "on-exit-leak-free": "^2.1.0",
+ "pino-abstract-transport": "^3.0.0",
+ "pino-std-serializers": "^7.0.0",
+ "process-warning": "^5.0.0",
+ "quick-format-unescaped": "^4.0.3",
+ "real-require": "^0.2.0",
+ "safe-stable-stringify": "^2.3.1",
+ "sonic-boom": "^4.0.1",
+ "thread-stream": "^4.0.0"
+ },
+ "bin": {
+ "pino": "bin.js"
+ }
+ },
+ "node_modules/pino-abstract-transport": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz",
+ "integrity": "sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg==",
+ "license": "MIT",
+ "dependencies": {
+ "split2": "^4.0.0"
+ }
+ },
+ "node_modules/pino-std-serializers": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz",
+ "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==",
+ "license": "MIT"
+ },
+ "node_modules/process-warning": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
+ "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/proxy-addr/node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.15.3",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz",
+ "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "es-define-property": "^1.0.1",
+ "side-channel": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/quick-format-unescaped": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
+ "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
+ "license": "MIT"
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
+ "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/real-require": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
+ "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12.13.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ret": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz",
+ "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "license": "MIT"
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safe-regex2": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.1.1.tgz",
+ "integrity": "sha512-mOSBvHGDZMuIEZMdOz/aCEYDCv0E7nfcNsIhUF+/P+xC7Hyf3FkvymqgPbg9D1EdSGu+uKbJgy09K/RKKc7kJA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "ret": "~0.5.0"
+ },
+ "bin": {
+ "safe-regex2": "bin/safe-regex2.js"
+ }
+ },
+ "node_modules/safe-stable-stringify": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz",
+ "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/secure-json-parse": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz",
+ "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/send": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
+ "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.1",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "~2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "~2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/serve-static": {
+ "version": "1.16.3",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
+ "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "~0.19.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/set-cookie-parser": {
+ "version": "2.7.2",
+ "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
+ "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
+ "license": "MIT"
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "license": "ISC"
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+ "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4",
+ "side-channel-list": "^1.0.1",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/sonic-boom": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz",
+ "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==",
+ "license": "MIT",
+ "dependencies": {
+ "atomic-sleep": "^1.0.0"
+ }
+ },
+ "node_modules/split2": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+ "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+ "license": "ISC",
+ "engines": {
+ "node": ">= 10.x"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/thread-stream": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.2.0.tgz",
+ "integrity": "sha512-e2zZ96wSChazBsbENf/Pcm/4swHt2cEKQ92rhUjkL9GCKiTDJIaTBenjE/m9DXi0QBmTMDkFDdOomUy20A1tDQ==",
+ "license": "MIT",
+ "dependencies": {
+ "real-require": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/thread-stream/node_modules/real-require": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/real-require/-/real-require-1.0.0.tgz",
+ "integrity": "sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==",
+ "license": "MIT"
+ },
+ "node_modules/toad-cache": {
+ "version": "3.7.1",
+ "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.1.tgz",
+ "integrity": "sha512-5DXWzE4Vz7xNHsv+xQ+MGfJYyC78Aok3tEr0MNwHoRf7vZnga1mQXZ4/Nsodld4VR6Wd+VhfmqnNrsRJyYPfrQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ }
+ }
+}
diff --git a/benchmarks/package.json b/benchmarks/package.json
new file mode 100644
index 0000000..7b399ff
--- /dev/null
+++ b/benchmarks/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "lambda-api-benchmarks",
+ "version": "0.0.0",
+ "private": true,
+ "description": "In-process micro-benchmarks comparing lambda-api against other AWS Lambda web frameworks",
+ "scripts": {
+ "bench": "node run.js",
+ "bench:md": "node run.js --md results/RESULTS.md",
+ "bench:json": "node run.js --json results/raw.json",
+ "bench:release": "node run.js --md results/RESULTS.md --json results/raw.json --update-readme"
+ },
+ "engines": {
+ "node": ">=20"
+ },
+ "dependencies": {
+ "@fastify/aws-lambda": "^6.4.0",
+ "@middy/core": "^6.4.3",
+ "@middy/http-error-handler": "^6.4.3",
+ "@middy/http-json-body-parser": "^6.4.3",
+ "@middy/http-router": "^6.4.3",
+ "@vendia/serverless-express": "^4.12.6",
+ "express": "^4.21.2",
+ "fastify": "^5.8.5",
+ "hono": "^4.12.27",
+ "mitata": "^1.0.34"
+ }
+}
diff --git a/benchmarks/results/.gitkeep b/benchmarks/results/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/benchmarks/run.js b/benchmarks/run.js
new file mode 100644
index 0000000..12fd0ca
--- /dev/null
+++ b/benchmarks/run.js
@@ -0,0 +1,175 @@
+'use strict';
+
+/**
+ * lambda-api benchmark runner.
+ *
+ * Invokes each framework's compiled aws-lambda handler IN-PROCESS with identical synthetic
+ * API Gateway events and measures framework overhead with mitata. This deliberately avoids
+ * LocalStack / a real Lambda deploy: those wrap the sub-millisecond work we care about in
+ * Docker, the runtime bootstrap and network latency — tens of milliseconds of noise that
+ * would swamp the signal and measure the emulator, not the framework. See README.md.
+ *
+ * Usage:
+ * node run.js run every framework, print tables to stdout
+ * node run.js --framework lambda-api run a single framework (clean JIT isolation)
+ * node run.js --md results/RESULTS.md write the markdown report to a file
+ * node run.js --json results/raw.json dump raw per-cell stats (machine-readable)
+ * node run.js --update-readme refresh the Benchmarks section in ../README.md
+ */
+
+const os = require('os');
+const fs = require('fs');
+const path = require('path');
+
+const { apiGatewayV1, apiGatewayV2 } = require('./lib/events');
+const { scenarios } = require('./lib/scenarios');
+const validate = require('./lib/validate');
+const { renderTables } = require('./lib/table');
+const { updateReadme } = require('./lib/update-readme');
+
+const ALL_FRAMEWORKS = ['baseline', 'lambda-api', 'serverless-express', 'fastify', 'hono', 'middy'];
+
+const FORMATS = [
+ { id: 'v1', build: apiGatewayV1 },
+ { id: 'v2', build: apiGatewayV2 }
+];
+
+// Minimal but realistic Lambda context. Some adapters read getRemainingTimeInMillis().
+const CONTEXT = {
+ awsRequestId: 'benchmark-request',
+ functionName: 'lambda-api-benchmark',
+ functionVersion: '$LATEST',
+ memoryLimitInMB: '1024',
+ callbackWaitsForEmptyEventLoop: false,
+ getRemainingTimeInMillis: () => 30000
+};
+
+function parseArgs(argv) {
+ const opts = { md: null, json: null, updateReadme: false, framework: null };
+ for (let i = 0; i < argv.length; i++) {
+ const a = argv[i];
+ if (a === '--md') opts.md = argv[++i];
+ else if (a === '--json') opts.json = argv[++i];
+ else if (a === '--update-readme') opts.updateReadme = true;
+ else if (a === '--framework') opts.framework = argv[++i];
+ else if (a === '--help' || a === '-h') opts.help = true;
+ else throw new Error(`Unknown argument: ${a}`);
+ }
+ return opts;
+}
+
+function collectEnv() {
+ const cpus = os.cpus();
+ const now = new Date();
+ return {
+ lambdaApiVersion: process.env.LAMBDA_API_VERSION || require('../package.json').version,
+ node: process.version.replace(/^v/, ''),
+ platform: process.platform,
+ arch: process.arch,
+ cpu: cpus && cpus.length ? `${cpus[0].model.trim()} (${cpus.length} cores)` : 'unknown',
+ date: now.toISOString().replace('T', ' ').slice(0, 19) + ' UTC',
+ dateShort: now.toISOString().slice(0, 10)
+ };
+}
+
+function eventFor(format, scenario) {
+ return format.build({ method: scenario.method, path: scenario.path, body: scenario.body || null });
+}
+
+async function main() {
+ const opts = parseArgs(process.argv.slice(2));
+ if (opts.help) {
+ console.log(fs.readFileSync(__filename, 'utf8').split('\n').slice(6, 24).join('\n'));
+ return;
+ }
+
+ const names = opts.framework ? [opts.framework] : ALL_FRAMEWORKS;
+ const env = collectEnv();
+
+ // mitata is ESM-only; load it from CommonJS via dynamic import.
+ const { measure } = await import('mitata');
+
+ console.error(`\nlambda-api benchmarks — Node ${env.node} on ${env.cpu}`);
+ console.error(`frameworks: ${names.join(', ')}\n`);
+
+ const results = [];
+
+ for (const name of names) {
+ const fw = require(`./frameworks/${name}`);
+ const handler = await fw.build();
+ console.error(`▸ ${name}${fw.version !== '-' ? ' ' + fw.version : ''}`);
+
+ for (const format of FORMATS) {
+ for (const scenario of scenarios) {
+ const event = eventFor(format, scenario);
+ const ctx = `${name}/${format.id}/${scenario.id}`;
+
+ // Correctness gate first — never time a handler that fails the contract.
+ let ok = true;
+ try {
+ const res = await handler(event, CONTEXT);
+ validate(res, scenario.expect, ctx);
+ } catch (err) {
+ ok = false;
+ console.error(` ! skipped ${format.id}/${scenario.id}: ${err.message}`);
+ }
+
+ let stats = null;
+ if (ok) {
+ // mitata detects the returned Promise and awaits it; handles warmup + batching.
+ stats = await measure(() => handler(event, CONTEXT));
+ const ops = Math.round(1e9 / stats.avg).toLocaleString('en-US');
+ console.error(` ✓ ${format.id}/${scenario.id}: ${ops} ops/sec`);
+ }
+
+ results.push({
+ framework: name,
+ version: fw.version,
+ format: format.id,
+ scenario: scenario.id,
+ ok,
+ stats: stats && {
+ avg: stats.avg,
+ min: stats.min,
+ max: stats.max,
+ p50: stats.p50,
+ p75: stats.p75,
+ p99: stats.p99
+ }
+ });
+ }
+ }
+ }
+
+ const markdown = renderTables(results, env);
+ console.log('\n' + markdown);
+
+ if (opts.md) {
+ const dest = path.resolve(__dirname, opts.md);
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
+ fs.writeFileSync(dest, `# lambda-api benchmark results\n\n${markdown}`);
+ console.error(`\nwrote ${opts.md}`);
+ }
+
+ if (opts.json) {
+ const dest = path.resolve(__dirname, opts.json);
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
+ fs.writeFileSync(dest, JSON.stringify({ env, results }, null, 2));
+ console.error(`wrote ${opts.json}`);
+ }
+
+ if (opts.updateReadme) {
+ if (opts.framework) {
+ console.error('refusing --update-readme with --framework (partial results)');
+ } else {
+ const readmePath = path.resolve(__dirname, '..', 'README.md');
+ const { changed, action } = updateReadme({ readmePath, results, env });
+ console.error(`README.md ${changed ? action : 'unchanged'}`);
+ }
+ }
+}
+
+main().catch((err) => {
+ console.error(err);
+ process.exit(1);
+});