From 54a679aece1676c1f3ec33dd28d314b0ee6598bc Mon Sep 17 00:00:00 2001 From: will Farrell Date: Thu, 30 Apr 2026 05:06:02 -0600 Subject: [PATCH 1/8] chore: dep update Signed-off-by: will Farrell --- package-lock.json | 115 ++++++++++++++++------------------------------ package.json | 1 - 2 files changed, 40 insertions(+), 76 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1a9475e12..0c2892e3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2017,15 +2017,15 @@ "license": "MIT OR Apache-2.0" }, "node_modules/@commitlint/cli": { - "version": "20.5.2", - "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.5.2.tgz", - "integrity": "sha512-IXr5xd3IX8SEG936P8gcpozRplkDeDSwJlt8UvoY1winwIy2udTbQ/cOCgbaaxcjdDqVoS29VUcz/wkwnSozbA==", + "version": "20.5.3", + "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-20.5.3.tgz", + "integrity": "sha512-OJdL0EXWD5y9LPa0nr/geOwzaS8BsdaybKkcloB0JgsguGxNv2R+hC2FTPqrAcprg35zF33KOQerY0x8W1aesA==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/format": "^20.5.0", - "@commitlint/lint": "^20.5.0", - "@commitlint/load": "^20.5.2", + "@commitlint/lint": "^20.5.3", + "@commitlint/load": "^20.5.3", "@commitlint/read": "^20.5.0", "@commitlint/types": "^20.5.0", "tinyexec": "^1.0.0", @@ -2039,9 +2039,9 @@ } }, "node_modules/@commitlint/config-conventional": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.5.0.tgz", - "integrity": "sha512-t3Ni88rFw1XMa4nZHgOKJ8fIAT9M2j5TnKyTqJzsxea7FUetlNdYFus9dz+MhIRZmc16P0PPyEfh6X2d/qw8SA==", + "version": "20.5.3", + "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-20.5.3.tgz", + "integrity": "sha512-j34Qqeaa152chJgz2ysyk0BCpHenJn1lV0Rx0VXf8k3ccQcED+48EZrzMvo9jLmJUyBrrBwvu89I+2er4gW7QQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2067,18 +2067,14 @@ } }, "node_modules/@commitlint/ensure": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.5.0.tgz", - "integrity": "sha512-IpHqAUesBeW1EDDdjzJeaOxU9tnogLAyXLRBn03SHlj1SGENn2JGZqSWGkFvBJkJzfXAuCNtsoYzax+ZPS+puw==", + "version": "20.5.3", + "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-20.5.3.tgz", + "integrity": "sha512-4i4AgNvH62owG9MwSiWKrle7HGNpBHHdLnWFIp5fTsHUYe5kRuh15t08L/0pdbbrRk8JKXQxxN4hZQcn+szkrw==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/types": "^20.5.0", - "lodash.camelcase": "^4.3.0", - "lodash.kebabcase": "^4.1.1", - "lodash.snakecase": "^4.1.1", - "lodash.startcase": "^4.4.0", - "lodash.upperfirst": "^4.3.1" + "es-toolkit": "^1.46.0" }, "engines": { "node": ">=v18" @@ -2123,15 +2119,15 @@ } }, "node_modules/@commitlint/lint": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.5.0.tgz", - "integrity": "sha512-jiM3hNUdu04jFBf1VgPdjtIPvbuVfDTBAc6L98AWcoLjF5sYqkulBHBzlVWll4rMF1T5zeQFB6r//a+s+BBKlA==", + "version": "20.5.3", + "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-20.5.3.tgz", + "integrity": "sha512-M7JbWBNr2gXKaPc4i/KipsuW1gkDHpj35KPjWtKy3Z+2AQw5wu1gBi1LIO0uoaij67CqY4K8PxPZSGens4evCw==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/is-ignored": "^20.5.0", "@commitlint/parse": "^20.5.0", - "@commitlint/rules": "^20.5.0", + "@commitlint/rules": "^20.5.3", "@commitlint/types": "^20.5.0" }, "engines": { @@ -2139,20 +2135,20 @@ } }, "node_modules/@commitlint/load": { - "version": "20.5.2", - "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.5.2.tgz", - "integrity": "sha512-zmr0RGDz7vThxW1I8ohb9yBjnGuH9mqwJpn21hInjGla+IlLOkS9ey0+dD5HlkzFlY0lX2NYdA2lDW6/0rO7Gw==", + "version": "20.5.3", + "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-20.5.3.tgz", + "integrity": "sha512-1FDZWuKyu98Myb8i7Tp31jPU2rZpOwAdYRyJcy2KoGg7Xk2A+bgHN8smhMaaNSNkmE8fwt53BokywZq8Gv/5XQ==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/config-validator": "^20.5.0", "@commitlint/execute-rule": "^20.0.0", - "@commitlint/resolve-extends": "^20.5.2", + "@commitlint/resolve-extends": "^20.5.3", "@commitlint/types": "^20.5.0", "cosmiconfig": "^9.0.1", "cosmiconfig-typescript-loader": "^6.1.0", + "es-toolkit": "^1.46.0", "is-plain-obj": "^4.1.0", - "lodash.mergewith": "^4.6.2", "picocolors": "^1.1.1" }, "engines": { @@ -2202,17 +2198,17 @@ } }, "node_modules/@commitlint/resolve-extends": { - "version": "20.5.2", - "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.5.2.tgz", - "integrity": "sha512-8EhSCU9eNos/5cI1yg64GW79UH1c64O69AfStCsj4zqy6An/qIphVEXj4/+2M6056T8coz00f+UXFn4WUUP1HQ==", + "version": "20.5.3", + "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-20.5.3.tgz", + "integrity": "sha512-+ogW9v/u9JqpvAgTrLra/YTFo0KkjU6iNblF89pPsj4NebNc+DAWctsludwezI8YnsjBmfHpApSwcXprN/f/ew==", "dev": true, "license": "MIT", "dependencies": { "@commitlint/config-validator": "^20.5.0", "@commitlint/types": "^20.5.0", + "es-toolkit": "^1.46.0", "global-directory": "^5.0.0", "import-meta-resolve": "^4.0.0", - "lodash.mergewith": "^4.6.2", "resolve-from": "^5.0.0" }, "engines": { @@ -2220,13 +2216,13 @@ } }, "node_modules/@commitlint/rules": { - "version": "20.5.0", - "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.5.0.tgz", - "integrity": "sha512-5NdQXQEdnDPT5pK8O39ZA7HohzPRHEsDGU23cyVCNPQy4WegAbAwrQk3nIu7p2sl3dutPk8RZd91yKTrMTnRkQ==", + "version": "20.5.3", + "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-20.5.3.tgz", + "integrity": "sha512-MPlMnb9D3wbszYMp+1hPtuhtPJndRo6I6yfkZVA4+jR8w7Kqp0u2u/Y+gzbaItx5Lltq5rw7FSZQWJMoXUC4NQ==", "dev": true, "license": "MIT", "dependencies": { - "@commitlint/ensure": "^20.5.0", + "@commitlint/ensure": "^20.5.3", "@commitlint/message": "^20.4.3", "@commitlint/to-lines": "^20.0.0", "@commitlint/types": "^20.5.0" @@ -6570,6 +6566,17 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/es-toolkit": { + "version": "1.46.1", + "resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.46.1.tgz", + "integrity": "sha512-5eNtXOs3tbfxXOj04tjjseeWkRWaoCjdEI+96DgwzZoe6c9juL49pXlzAFTI72aWC9Y8p7168g6XIKjh7k6pyQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "docs", + "benchmarks" + ] + }, "node_modules/esbuild": { "version": "0.28.0", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", @@ -8005,48 +8012,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.kebabcase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.snakecase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.startcase": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz", - "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.upperfirst": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", - "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==", - "dev": true, - "license": "MIT" - }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", diff --git a/package.json b/package.json index 6d299ab72..f9a237a58 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,6 @@ "@sveltejs/kit": { "cookie": "^0.7.2" }, - "fast-xml-parser": ">=5.7.2", "mathjs": ">=15.2.0" }, "devEngines": { From 6e8617c50de9a0f84f57a92e97c060b23598b33a Mon Sep 17 00:00:00 2001 From: will Farrell Date: Thu, 30 Apr 2026 20:46:08 -0600 Subject: [PATCH 2/8] fix: add in endpoint Signed-off-by: will Farrell --- websites/middy.js.org/static/.well-known/mcp.json | 1 + 1 file changed, 1 insertion(+) diff --git a/websites/middy.js.org/static/.well-known/mcp.json b/websites/middy.js.org/static/.well-known/mcp.json index d922df7d0..016dce36b 100644 --- a/websites/middy.js.org/static/.well-known/mcp.json +++ b/websites/middy.js.org/static/.well-known/mcp.json @@ -2,6 +2,7 @@ "name": "middy", "description": "Middy - The stylish Node.js middleware engine for AWS Lambda", "documentation": "https://middy.js.org/", + "endpoint": "https://middy.js.org/.well-known/mcp.json", "resources": [ { "name": "llms.txt", From 09e5f69e975415672967314fb96c5cac94bf15c9 Mon Sep 17 00:00:00 2001 From: will Farrell Date: Fri, 1 May 2026 06:09:12 -0600 Subject: [PATCH 3/8] feat: add in support for dsql signer Signed-off-by: will Farrell --- package-lock.json | 75 +++ packages/dsql-signer/README.md | 50 ++ packages/dsql-signer/index.d.ts | 54 ++ packages/dsql-signer/index.fuzz.js | 22 + packages/dsql-signer/index.js | 128 +++++ packages/dsql-signer/index.perf.js | 62 +++ packages/dsql-signer/index.test.js | 508 ++++++++++++++++++ packages/dsql-signer/index.tst.ts | 78 +++ packages/dsql-signer/package.json | 82 +++ packages/rds-signer/index.js | 6 +- packages/util/index.js | 10 +- .../src/components/docs/AsideNav.svelte | 1 + .../docs/best-practices/bundling/+page.md | 2 + .../docs/middlewares/dsql-signer/+page.md | 82 +++ .../routes/docs/middlewares/intro/+page.md | 1 + 15 files changed, 1156 insertions(+), 5 deletions(-) create mode 100644 packages/dsql-signer/README.md create mode 100644 packages/dsql-signer/index.d.ts create mode 100644 packages/dsql-signer/index.fuzz.js create mode 100644 packages/dsql-signer/index.js create mode 100644 packages/dsql-signer/index.perf.js create mode 100644 packages/dsql-signer/index.test.js create mode 100644 packages/dsql-signer/index.tst.ts create mode 100644 packages/dsql-signer/package.json create mode 100644 websites/middy.js.org/src/routes/docs/middlewares/dsql-signer/+page.md diff --git a/package-lock.json b/package-lock.json index 0c2892e3b..01f17b0e4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1119,6 +1119,30 @@ "node": ">=20.0.0" } }, + "node_modules/@aws-sdk/dsql-signer": { + "version": "3.1040.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/dsql-signer/-/dsql-signer-3.1040.0.tgz", + "integrity": "sha512-8j19o9S88i7ZidWWZagJz/TLRMDX3sDauG+8oP3rgdR5R7QPfSW1s+gbRGc/WO27gD2UN0wsaJKnS9cVvTjv8g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/credential-provider-node": "^3.972.38", + "@aws-sdk/util-format-url": "^3.972.10", + "@smithy/config-resolver": "^4.4.17", + "@smithy/hash-node": "^4.2.14", + "@smithy/invalid-dependency": "^4.2.14", + "@smithy/node-config-provider": "^4.3.14", + "@smithy/protocol-http": "^5.3.14", + "@smithy/signature-v4": "^5.3.14", + "@smithy/types": "^4.14.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/@aws-sdk/dynamodb-codec": { "version": "3.973.7", "resolved": "https://registry.npmjs.org/@aws-sdk/dynamodb-codec/-/dynamodb-codec-3.973.7.tgz", @@ -3530,6 +3554,10 @@ "resolved": "packages/do-not-wait-for-empty-event-loop", "link": true }, + "node_modules/@middy/dsql-signer": { + "resolved": "packages/dsql-signer", + "link": true + }, "node_modules/@middy/dynamodb": { "resolved": "packages/dynamodb", "link": true @@ -10932,6 +10960,53 @@ "dev": true, "license": "MIT" }, + "packages/dsql-signer": { + "name": "@middy/dsql-signer", + "version": "7.3.4", + "license": "MIT", + "dependencies": { + "@middy/util": "7.3.4" + }, + "devDependencies": { + "@aws-sdk/dsql-signer": "^3.0.0", + "@middy/core": "7.3.4", + "@types/aws-lambda": "^8.0.0", + "@types/node": "^22.0.0", + "aws-xray-sdk": "^3.3.3" + }, + "engines": { + "node": ">=22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/willfarrell" + }, + "peerDependencies": { + "@aws-sdk/dsql-signer": "^3.0.0" + }, + "peerDependenciesMeta": { + "@aws-sdk/dsql-signer": { + "optional": true + } + } + }, + "packages/dsql-signer/node_modules/@types/node": { + "version": "22.19.17", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz", + "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "packages/dsql-signer/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, "packages/dynamodb": { "name": "@middy/dynamodb", "version": "7.3.4", diff --git a/packages/dsql-signer/README.md b/packages/dsql-signer/README.md new file mode 100644 index 000000000..5bde2e8a4 --- /dev/null +++ b/packages/dsql-signer/README.md @@ -0,0 +1,50 @@ +
+

Middy `dsql-signer` middleware

+ Middy logo +

Aurora DSQL Signer middleware for the middy framework, the stylish Node.js middleware engine for AWS Lambda

+

+ GitHub Actions unit test status + GitHub Actions dast test status + GitHub Actions perf test status + GitHub Actions sast test status + GitHub Actions lint test status +
+ npm version + npm install size + + npm weekly downloads + + npm provenance +
+ Open Source Security Foundation (OpenSSF) Scorecard + SLSA 3 + + Checked with Biome + Conventional Commits + + code coverage +
+

+

You can read the documentation at: https://middy.js.org/docs/middlewares/dsql-signer

+
+ +## Install + +```bash +npm install --save @middy/dsql-signer @aws-sdk/dsql-signer +``` + + +## Documentation and examples + +For documentation and examples, refer to the main [Middy monorepo on GitHub](https://github.com/middyjs/middy) or [Middy official website](https://middy.js.org/docs/middlewares/dsql-signer). + + +## Contributing + +Everyone is very welcome to contribute to this repository. Feel free to [raise issues](https://github.com/middyjs/middy/issues) or to [submit Pull Requests](https://github.com/middyjs/middy/pulls). + + +## License + +Licensed under [MIT License](https://github.com/middyjs/middy/blob/main/LICENSE). Copyright (c) 2017-2026 [will Farrell](https://github.com/willfarrell), [Luciano Mammino](https://github.com/lmammino), and [Middy contributors](https://github.com/middyjs/middy/graphs/contributors). diff --git a/packages/dsql-signer/index.d.ts b/packages/dsql-signer/index.d.ts new file mode 100644 index 000000000..32fb4e2d1 --- /dev/null +++ b/packages/dsql-signer/index.d.ts @@ -0,0 +1,54 @@ +// Copyright 2017 - 2026 will Farrell, Luciano Mammino, and Middy contributors. +// SPDX-License-Identifier: MIT +import type { DsqlSigner, DsqlSignerConfig } from "@aws-sdk/dsql-signer"; +import type middy from "@middy/core"; +import type { Options as MiddyOptions } from "@middy/util"; +import type { Context as LambdaContext } from "aws-lambda"; + +export type ParamType = string & { __returnType?: T }; +export declare function dsqlSignerParam(name: string): ParamType; + +export type DsqlSignerFetchConfig = DsqlSignerConfig & { username?: string }; + +export type DsqlSignerOptions = Omit< + MiddyOptions, + "fetchData" +> & { + fetchData?: { + [key: string]: DsqlSignerFetchConfig; + }; +}; + +export type Context = + TOptions extends { setToContext: true } + ? TOptions extends { fetchData: infer TFetchData } + ? LambdaContext & { + [Key in keyof TFetchData]: string; + } + : never + : LambdaContext; + +export type Internal = + TOptions extends DsqlSignerOptions + ? TOptions extends { fetchData: infer TFetchData } + ? { + [Key in keyof TFetchData]: string; + } + : {} + : {}; + +declare function dsqlSigner( + options?: TOptions, +): middy.MiddlewareObj< + unknown, + unknown, + Error, + Context, + Internal +>; + +export declare function dsqlSignerValidateOptions( + options?: Record, +): void; + +export default dsqlSigner; diff --git a/packages/dsql-signer/index.fuzz.js b/packages/dsql-signer/index.fuzz.js new file mode 100644 index 000000000..9a028581f --- /dev/null +++ b/packages/dsql-signer/index.fuzz.js @@ -0,0 +1,22 @@ +import { test } from "node:test"; +import fc from "fast-check"; +import middy from "../core/index.js"; +import middleware from "./index.js"; + +const handler = middy((event) => event).use(middleware()); +const defaultContext = { + getRemainingTimeInMillis: () => 1000, +}; + +test("fuzz `event` w/ `object`", async () => { + await fc.assert( + fc.asyncProperty(fc.object(), async (event) => { + await handler(event, defaultContext); + }), + { + numRuns: 100_000, + + examples: [], + }, + ); +}); diff --git a/packages/dsql-signer/index.js b/packages/dsql-signer/index.js new file mode 100644 index 000000000..95244babf --- /dev/null +++ b/packages/dsql-signer/index.js @@ -0,0 +1,128 @@ +// Copyright 2017 - 2026 will Farrell, Luciano Mammino, and Middy contributors. +// SPDX-License-Identifier: MIT +import { DsqlSigner } from "@aws-sdk/dsql-signer"; +import { + canPrefetch, + getCache, + getInternal, + modifyCache, + processCache, + validateOptions, +} from "@middy/util"; + +const name = "dsql-signer"; +const pkg = `@middy/${name}`; + +const defaults = { + AwsClient: DsqlSigner, + awsClientOptions: {}, + fetchData: {}, + disablePrefetch: false, + cacheKey: pkg, + cacheKeyExpiry: {}, + cacheExpiry: -1, + setToContext: false, +}; + +const optionSchema = { + type: "object", + properties: { + AwsClient: { instanceof: "Function" }, + awsClientOptions: { type: "object" }, + fetchData: { + type: "object", + additionalProperties: { + type: "object", + properties: { + hostname: { + type: "string", + pattern: + "^[a-z0-9]+\\.dsql(-[a-z]+)?\\.[a-z]{2}(-[a-z]+){1,2}-\\d+\\.on\\.aws$", + }, + username: { type: "string" }, + }, + required: ["hostname"], + additionalProperties: true, + }, + }, + disablePrefetch: { type: "boolean" }, + cacheKey: { type: "string" }, + cacheKeyExpiry: { + type: "object", + additionalProperties: { type: "number", minimum: -1 }, + }, + cacheExpiry: { type: "number", minimum: -1 }, + setToContext: { type: "boolean" }, + }, + additionalProperties: false, +}; + +export const dsqlSignerValidateOptions = (options) => + validateOptions(pkg, optionSchema, options); + +const dsqlSignerMiddleware = (opts = {}) => { + const options = { ...defaults, ...opts }; + + const fetchDataKeys = Object.keys(options.fetchData); + const clients = {}; + const fetchRequest = (request, cachedValues = {}) => { + const values = {}; + for (const internalKey of fetchDataKeys) { + if (cachedValues[internalKey]) continue; + + const { username, ...signerConfig } = options.fetchData[internalKey]; + clients[internalKey] ??= new options.AwsClient({ + ...options.awsClientOptions, + ...signerConfig, + }); + const method = + username === "admin" + ? "getDbConnectAdminAuthToken" + : "getDbConnectAuthToken"; + values[internalKey] = clients[internalKey] + [method]() + .then((token) => { + // Catch Missing token, this usually means there is something wrong with the credentials + if (!token.includes("X-Amz-Security-Token=")) { + throw new Error("X-Amz-Security-Token Missing", { + cause: { package: pkg, method }, + }); + } + return token; + }) + .catch((e) => { + const value = getCache(options.cacheKey).value ?? {}; + value[internalKey] = undefined; + modifyCache(options.cacheKey, value); + throw e; + }); + } + + return values; + }; + + if (canPrefetch(options)) { + processCache(options, fetchRequest); + } + + const dsqlSignerMiddlewareBefore = async (request) => { + const { value } = processCache(options, fetchRequest, request); + + Object.assign(request.internal, value); + + if (options.setToContext) { + const data = await getInternal(fetchDataKeys, request); + Object.assign(request.context, data); + } + }; + + return { + before: dsqlSignerMiddlewareBefore, + }; +}; +export default dsqlSignerMiddleware; + +// used for TS type inference (see index.d.ts) +export function dsqlSignerParam(name) { + return name; +} diff --git a/packages/dsql-signer/index.perf.js b/packages/dsql-signer/index.perf.js new file mode 100644 index 000000000..4b9ee130d --- /dev/null +++ b/packages/dsql-signer/index.perf.js @@ -0,0 +1,62 @@ +import { Bench } from "tinybench"; +import middy from "../core/index.js"; +import middleware from "./index.js"; + +const bench = new Bench({ + time: 1_000, + warmupTime: 500, + warmupIterations: 1_000, +}); + +const defaultContext = { + getRemainingTimeInMillis: () => 30000, +}; + +class MockDsqlSigner { + getDbConnectAuthToken() { + return Promise.resolve( + "cluster.dsql.us-east-1.on.aws/?Action=DbConnect&X-Amz-Security-Token=mock", + ); + } + getDbConnectAdminAuthToken() { + return Promise.resolve( + "cluster.dsql.us-east-1.on.aws/?Action=DbConnectAdmin&X-Amz-Security-Token=mock", + ); + } +} + +const setupHandler = (options = {}) => { + const baseHandler = () => {}; + return middy(baseHandler).use( + middleware({ + ...options, + AwsClient: MockDsqlSigner, + fetchData: { + token: { + hostname: "cluster.dsql.us-east-1.on.aws", + region: "us-east-1", + }, + }, + }), + ); +}; + +const coldHandler = setupHandler({ cacheExpiry: 0 }); +const warmHandler = setupHandler(); + +const defaultEvent = {}; +await bench + .add("without cache", async () => { + try { + await coldHandler(defaultEvent, defaultContext); + } catch (_e) {} + }) + .add("with cache", async () => { + try { + await warmHandler(defaultEvent, defaultContext); + } catch (_e) {} + }) + + .run(); + +console.table(bench.table()); diff --git a/packages/dsql-signer/index.test.js b/packages/dsql-signer/index.test.js new file mode 100644 index 000000000..2ebe747ea --- /dev/null +++ b/packages/dsql-signer/index.test.js @@ -0,0 +1,508 @@ +import { deepStrictEqual, ok, strictEqual } from "node:assert/strict"; +import { test } from "node:test"; +import middy from "../core/index.js"; +import { clearCache, getInternal } from "../util/index.js"; +import dsqlSigner, { dsqlSignerValidateOptions } from "./index.js"; + +test.afterEach((t) => { + t.mock.reset(); + clearCache(); +}); + +const defaultEvent = {}; +const defaultContext = { + getRemainingTimeInMillis: () => 1000, +}; + +test("It should set token to internal storage (token)", async (t) => { + const getDbConnectAuthToken = t.mock.fn( + async () => + "cluster.dsql.us-east-1.on.aws/?Action=DbConnect&X-Amz-Security-Token=token", + ); + class AwsClient { + getDbConnectAuthToken = getDbConnectAuthToken; + } + const handler = middy(() => {}); + + const middleware = async (request) => { + const values = await getInternal(true, request); + strictEqual( + values.token, + "cluster.dsql.us-east-1.on.aws/?Action=DbConnect&X-Amz-Security-Token=token", + ); + }; + + handler + .use( + dsqlSigner({ + AwsClient, + cacheExpiry: 0, + fetchData: { + token: { + region: "us-east-1", + hostname: "cluster.dsql.us-east-1.on.aws", + }, + }, + disablePrefetch: true, + }), + ) + .before(middleware); + + await handler(defaultEvent, defaultContext); + strictEqual(getDbConnectAuthToken.mock.callCount(), 1); +}); + +test("It should call admin token method when username is 'admin'", async (t) => { + const getDbConnectAuthToken = t.mock.fn( + async () => "X-Amz-Security-Token=non-admin-token", + ); + const getDbConnectAdminAuthToken = t.mock.fn( + async () => "X-Amz-Security-Token=admin-token", + ); + class AwsClient { + getDbConnectAuthToken = getDbConnectAuthToken; + getDbConnectAdminAuthToken = getDbConnectAdminAuthToken; + } + const handler = middy(() => {}); + + const middleware = async (request) => { + const values = await getInternal(true, request); + strictEqual(values.token, "X-Amz-Security-Token=admin-token"); + }; + + handler + .use( + dsqlSigner({ + AwsClient, + cacheExpiry: 0, + fetchData: { + token: { + region: "us-east-1", + hostname: "cluster.dsql.us-east-1.on.aws", + username: "admin", + }, + }, + disablePrefetch: true, + }), + ) + .before(middleware); + + await handler(defaultEvent, defaultContext); + strictEqual(getDbConnectAdminAuthToken.mock.callCount(), 1); + strictEqual(getDbConnectAuthToken.mock.callCount(), 0); +}); + +test("It should call non-admin token method when username is a custom role", async (t) => { + const getDbConnectAuthToken = t.mock.fn( + async () => "X-Amz-Security-Token=role-token", + ); + const getDbConnectAdminAuthToken = t.mock.fn( + async () => "X-Amz-Security-Token=admin-token", + ); + class AwsClient { + getDbConnectAuthToken = getDbConnectAuthToken; + getDbConnectAdminAuthToken = getDbConnectAdminAuthToken; + } + const handler = middy(() => {}); + + const middleware = async (request) => { + const values = await getInternal(true, request); + strictEqual(values.token, "X-Amz-Security-Token=role-token"); + }; + + handler + .use( + dsqlSigner({ + AwsClient, + cacheExpiry: 0, + fetchData: { + token: { + region: "us-east-1", + hostname: "cluster.dsql.us-east-1.on.aws", + username: "app_reader", + }, + }, + disablePrefetch: true, + }), + ) + .before(middleware); + + await handler(defaultEvent, defaultContext); + strictEqual(getDbConnectAuthToken.mock.callCount(), 1); + strictEqual(getDbConnectAdminAuthToken.mock.callCount(), 0); +}); + +test("It should not pass `username` to the signer constructor", async (t) => { + let receivedConfig; + const getDbConnectAdminAuthToken = t.mock.fn( + async () => "X-Amz-Security-Token=admin-token", + ); + class AwsClient { + constructor(config) { + receivedConfig = config; + } + getDbConnectAdminAuthToken = getDbConnectAdminAuthToken; + } + const handler = middy(() => {}); + + handler.use( + dsqlSigner({ + AwsClient, + cacheExpiry: 0, + fetchData: { + token: { + hostname: "cluster.dsql.us-east-1.on.aws", + username: "admin", + }, + }, + disablePrefetch: true, + }), + ); + + await handler(defaultEvent, defaultContext); + deepStrictEqual(receivedConfig, { + hostname: "cluster.dsql.us-east-1.on.aws", + }); +}); + +test("It should set tokens to internal storage (multiple keys)", async (t) => { + const getDbConnectAuthToken = t.mock.fn( + async () => "X-Amz-Security-Token=token2", + async () => "X-Amz-Security-Token=token1", + { times: 1 }, + ); + class AwsClient { + getDbConnectAuthToken = getDbConnectAuthToken; + } + + const handler = middy(() => {}); + + const middleware = async (request) => { + const values = await getInternal(true, request); + strictEqual(values.token1, "X-Amz-Security-Token=token1"); + strictEqual(values.token2, "X-Amz-Security-Token=token2"); + }; + + handler + .use( + dsqlSigner({ + AwsClient, + cacheExpiry: 0, + fetchData: { + token1: { + region: "us-east-1", + hostname: "reader.dsql.us-east-1.on.aws", + }, + token2: { + region: "us-east-1", + hostname: "writer.dsql.us-east-1.on.aws", + }, + }, + disablePrefetch: true, + }), + ) + .before(middleware); + + await handler(defaultEvent, defaultContext); +}); + +test("It should set DSQL token to context", async (t) => { + const getDbConnectAuthToken = t.mock.fn( + async () => "X-Amz-Security-Token=token", + ); + class AwsClient { + getDbConnectAuthToken = getDbConnectAuthToken; + } + const handler = middy(() => {}); + + const middleware = async (request) => { + strictEqual(request.context.token, "X-Amz-Security-Token=token"); + }; + + handler + .use( + dsqlSigner({ + AwsClient, + cacheExpiry: 0, + fetchData: { + token: { + region: "us-east-1", + hostname: "cluster.dsql.us-east-1.on.aws", + }, + }, + setToContext: true, + disablePrefetch: true, + }), + ) + .before(middleware); + + await handler(defaultEvent, defaultContext); +}); + +test("It should not call aws-sdk again if parameter is cached", async (t) => { + const getDbConnectAuthToken = t.mock.fn( + async () => "X-Amz-Security-Token=token", + ); + class AwsClient { + getDbConnectAuthToken = getDbConnectAuthToken; + } + const handler = middy(() => {}); + + const middleware = async (request) => { + const values = await getInternal(true, request); + strictEqual(values.token, "X-Amz-Security-Token=token"); + }; + + handler + .use( + dsqlSigner({ + AwsClient, + cacheExpiry: -1, + fetchData: { + token: { + region: "us-east-1", + hostname: "cluster.dsql.us-east-1.on.aws", + }, + }, + }), + ) + .before(middleware); + + await handler(defaultEvent, defaultContext); + await handler(defaultEvent, defaultContext); + + strictEqual(getDbConnectAuthToken.mock.callCount(), 1); +}); + +test("It should call aws-sdk if cache enabled but cached param has expired", async (t) => { + const getDbConnectAuthToken = t.mock.fn( + async () => "X-Amz-Security-Token=token", + ); + class AwsClient { + getDbConnectAuthToken = getDbConnectAuthToken; + } + + const handler = middy(() => {}); + + const middleware = async (request) => { + const values = await getInternal(true, request); + strictEqual(values.token, "X-Amz-Security-Token=token"); + }; + + handler + .use( + dsqlSigner({ + AwsClient, + cacheExpiry: 0, + fetchData: { + token: { + region: "us-east-1", + hostname: "cluster.dsql.us-east-1.on.aws", + }, + }, + disablePrefetch: true, + }), + ) + .before(middleware); + + await handler(defaultEvent, defaultContext); + await handler(defaultEvent, defaultContext); + + strictEqual(getDbConnectAuthToken.mock.callCount(), 2); +}); + +test("It should catch if an error is returned from fetch", async (t) => { + const getDbConnectAuthToken = t.mock.fn(async () => { + throw new Error("timeout"); + }); + class AwsClient { + getDbConnectAuthToken = getDbConnectAuthToken; + } + const handler = middy(() => {}).use( + dsqlSigner({ + AwsClient, + cacheExpiry: 0, + fetchData: { + token: { + region: "us-east-1", + hostname: "cluster.dsql.us-east-1.on.aws", + }, + }, + setToContext: true, + disablePrefetch: true, + }), + ); + + try { + await handler(defaultEvent, defaultContext); + } catch (e) { + strictEqual(getDbConnectAuthToken.mock.callCount(), 1); + strictEqual(e.message, "Failed to resolve internal values"); + deepStrictEqual(e.cause.data, [new Error("timeout")]); + } +}); + +test("It should catch if a token without X-Amz-Security-Token is returned from fetch", async (t) => { + const getDbConnectAuthToken = t.mock.fn(async () => "no-creds"); + class AwsClient { + getDbConnectAuthToken = getDbConnectAuthToken; + } + const handler = middy(() => {}).use( + dsqlSigner({ + AwsClient, + cacheExpiry: 0, + fetchData: { + token: { + region: "us-east-1", + hostname: "cluster.dsql.us-east-1.on.aws", + }, + }, + setToContext: true, + disablePrefetch: true, + }), + ); + + try { + await handler(defaultEvent, defaultContext); + } catch (e) { + strictEqual(getDbConnectAuthToken.mock.callCount(), 1); + strictEqual(e.message, "Failed to resolve internal values"); + deepStrictEqual(e.cause.data, [ + new Error("X-Amz-Security-Token Missing", { + cause: { + package: "@middy/dsql-signer", + method: "getDbConnectAuthToken", + }, + }), + ]); + } +}); + +test("It should skip fetching already cached values when fetching multiple keys", async (t) => { + let callCount = 0; + const getDbConnectAuthToken = t.mock.fn(async () => { + callCount++; + if (callCount === 1) return "X-Amz-Security-Token=token1"; + if (callCount === 2) throw new Error("timeout"); + if (callCount === 3) return "X-Amz-Security-Token=token2"; + }); + class AwsClient { + getDbConnectAuthToken = getDbConnectAuthToken; + } + + const middleware = async (request) => { + const values = await getInternal(true, request); + strictEqual(values.token1, "X-Amz-Security-Token=token1"); + strictEqual(values.token2, "X-Amz-Security-Token=token2"); + }; + + const handler = middy(() => {}); + + handler + .use( + dsqlSigner({ + AwsClient, + cacheExpiry: 1000, + fetchData: { + token1: { + region: "us-east-1", + hostname: "reader.dsql.us-east-1.on.aws", + }, + token2: { + region: "us-east-1", + hostname: "writer.dsql.us-east-1.on.aws", + }, + }, + }), + ) + .before(middleware); + + try { + await handler(defaultEvent, defaultContext); + } catch (_e) {} + + await handler(defaultEvent, defaultContext); + + strictEqual(getDbConnectAuthToken.mock.callCount(), 3); +}); + +test("It should export dsqlSignerParam helper for TypeScript type inference", async (t) => { + const { dsqlSignerParam } = await import("./index.js"); + const paramName = "test-param"; + const result = dsqlSignerParam(paramName); + strictEqual(result, paramName); +}); + +test("dsqlSignerValidateOptions accepts valid options and rejects typos", () => { + dsqlSignerValidateOptions({ cacheKey: "x", cacheExpiry: 0 }); + dsqlSignerValidateOptions({}); + try { + dsqlSignerValidateOptions({ cachExpiry: 60 }); + ok(false, "expected throw"); + } catch (e) { + ok(e instanceof TypeError); + strictEqual(e.cause.package, "@middy/dsql-signer"); + } +}); + +test("dsqlSignerValidateOptions rejects wrong type", () => { + try { + dsqlSignerValidateOptions({ fetchData: 42 }); + ok(false, "expected throw"); + } catch (e) { + ok(e.message.includes("fetchData")); + } +}); + +test("dsqlSignerValidateOptions accepts valid fetchData entry", () => { + dsqlSignerValidateOptions({ + fetchData: { + token: { + hostname: "cluster.dsql.us-east-1.on.aws", + username: "admin", + }, + }, + }); +}); + +test("dsqlSignerValidateOptions rejects fetchData entry missing hostname", () => { + try { + dsqlSignerValidateOptions({ + fetchData: { token: { username: "admin" } }, + }); + ok(false, "expected throw"); + } catch (e) { + ok(e instanceof TypeError); + strictEqual(e.cause.package, "@middy/dsql-signer"); + } +}); + +test("dsqlSignerValidateOptions rejects fetchData entry with non-string username", () => { + try { + dsqlSignerValidateOptions({ + fetchData: { + token: { + hostname: "cluster.dsql.us-east-1.on.aws", + username: true, + }, + }, + }); + ok(false, "expected throw"); + } catch (e) { + ok(e instanceof TypeError); + } +}); + +test("dsqlSignerValidateOptions rejects fetchData entry with non-DSQL hostname", () => { + try { + dsqlSignerValidateOptions({ + fetchData: { + token: { hostname: "db.example.com" }, + }, + }); + ok(false, "expected throw"); + } catch (e) { + ok(e instanceof TypeError); + strictEqual(e.cause.package, "@middy/dsql-signer"); + } +}); diff --git a/packages/dsql-signer/index.tst.ts b/packages/dsql-signer/index.tst.ts new file mode 100644 index 000000000..22ac632aa --- /dev/null +++ b/packages/dsql-signer/index.tst.ts @@ -0,0 +1,78 @@ +import { DsqlSigner } from "@aws-sdk/dsql-signer"; +import middy from "@middy/core"; +import { getInternal } from "@middy/util"; +import type { Context as LambdaContext } from "aws-lambda"; +import { expect, test } from "tstyche"; +import dsqlSigner from "./index.js"; + +test("use with default options", () => { + const middleware = dsqlSigner(); + expect(middleware).type.toBe>(); +}); + +const options = { + AwsClient: DsqlSigner, + awsClientOptions: { + credentials: { + secretAccessKey: "secret", + accessKeyId: "key", + }, + }, + awsClientAssumeRole: "some-role", + fetchData: { + foo: { + hostname: "cluster.dsql.ca-central-1.on.aws", + username: "admin", + }, + }, + disablePrefetch: true, + cacheKey: "some-key", + cacheExpiry: 60 * 60 * 5, + setToContext: true, +}; + +test("use with no options", () => { + expect(dsqlSigner()).type.toBe< + middy.MiddlewareObj + >(); +}); + +test("use with all options", () => { + expect(dsqlSigner(options)).type.toBe< + middy.MiddlewareObj + >(); +}); + +const handler = middy(async (event: {}, context: LambdaContext) => { + return await Promise.resolve({}); +}); + +test("use with setToContext: true", () => { + handler + .use( + dsqlSigner({ + ...options, + setToContext: true, + }), + ) + .before(async (request) => { + expect(request.context.foo).type.toBe(); + + const data = await getInternal("foo", request); + expect(data.foo).type.toBe(); + }); +}); + +test("use with setToContext: false", () => { + handler + .use( + dsqlSigner({ + ...options, + setToContext: false, + }), + ) + .before(async (request) => { + const data = await getInternal("foo", request); + expect(data.foo).type.toBe(); + }); +}); diff --git a/packages/dsql-signer/package.json b/packages/dsql-signer/package.json new file mode 100644 index 000000000..f50f08307 --- /dev/null +++ b/packages/dsql-signer/package.json @@ -0,0 +1,82 @@ +{ + "name": "@middy/dsql-signer", + "version": "7.3.4", + "description": "Aurora DSQL credentials middleware for the middy framework", + "type": "module", + "engines": { + "node": ">=22" + }, + "engineStrict": true, + "publishConfig": { + "access": "public" + }, + "module": "./index.js", + "sideEffects": false, + "exports": { + ".": { + "import": { + "types": "./index.d.ts", + "default": "./index.js" + } + } + }, + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts" + ], + "scripts": { + "test": "npm run test:unit && npm run test:fuzz", + "test:unit": "node --test", + "test:fuzz": "node --test index.fuzz.js", + "test:perf": "node --test index.perf.js" + }, + "license": "MIT", + "keywords": [ + "Lambda", + "Middleware", + "Serverless", + "Framework", + "AWS", + "AWS Lambda", + "Middy", + "DSQL", + "Aurora DSQL", + "Credentials" + ], + "author": { + "name": "Middy contributors", + "url": "https://github.com/middyjs/middy/graphs/contributors" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/middyjs/middy.git", + "directory": "packages/dsql-signer" + }, + "bugs": { + "url": "https://github.com/middyjs/middy/issues" + }, + "homepage": "https://middy.js.org", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/willfarrell" + }, + "peerDependencies": { + "@aws-sdk/dsql-signer": "^3.0.0" + }, + "peerDependenciesMeta": { + "@aws-sdk/dsql-signer": { + "optional": true + } + }, + "dependencies": { + "@middy/util": "7.3.4" + }, + "devDependencies": { + "@aws-sdk/dsql-signer": "^3.0.0", + "@middy/core": "7.3.4", + "@types/aws-lambda": "^8.0.0", + "@types/node": "^22.0.0", + "aws-xray-sdk": "^3.3.3" + } +} diff --git a/packages/rds-signer/index.js b/packages/rds-signer/index.js index cb1aa687c..6cd51f486 100644 --- a/packages/rds-signer/index.js +++ b/packages/rds-signer/index.js @@ -67,12 +67,14 @@ const rdsSignerMiddleware = (opts = {}) => { for (const internalKey of fetchDataKeys) { if (cachedValues[internalKey]) continue; + const signerConfig = options.fetchData[internalKey]; clients[internalKey] ??= new options.AwsClient({ ...options.awsClientOptions, - ...options.fetchData[internalKey], + ...signerConfig, }); + const method = "getAuthToken"; values[internalKey] = clients[internalKey] - .getAuthToken() + [method]() .then((token) => { // Catch Missing token, this usually means there is something wrong with the credentials if (!token.includes("X-Amz-Security-Token=")) { diff --git a/packages/util/index.js b/packages/util/index.js index 12281f73d..06d98f3a4 100644 --- a/packages/util/index.js +++ b/packages/util/index.js @@ -15,7 +15,8 @@ // exclusiveMaximum?, multipleOf?, minLength?, maxLength?, pattern? } // Numeric: `minimum`/`maximum` (inclusive), `exclusiveMinimum`/ // `exclusiveMaximum` (exclusive), `multipleOf` (number/integer). -// String: `minLength`/`maxLength` (string length), `pattern` (regex source). +// String: `minLength`/`maxLength` (string length), `pattern` (regex +// source string per JSON Schema, or a RegExp object). // { type: 'object' | 'object?', properties?: {...}, additionalProperties?: } // `properties` validates known keys with the flat-schema form. // `additionalProperties` validates every other key's value against the @@ -215,8 +216,11 @@ const checkRule = (rule, value, path, fail) => { fail(`Option '${path}' must be a multiple of ${multipleOf}`); } } - if (pattern !== undefined && !pattern.test(value)) { - fail(`Option '${path}' must match pattern ${pattern}`); + if (pattern !== undefined) { + const re = typeof pattern === "string" ? new RegExp(pattern) : pattern; + if (!re.test(value)) { + fail(`Option '${path}' must match pattern ${re}`); + } } if (minLength !== undefined && value.length < minLength) { fail(`Option '${path}' must have length >= ${minLength}`); diff --git a/websites/middy.js.org/src/components/docs/AsideNav.svelte b/websites/middy.js.org/src/components/docs/AsideNav.svelte index c7bdcff80..4be51527a 100644 --- a/websites/middy.js.org/src/components/docs/AsideNav.svelte +++ b/websites/middy.js.org/src/components/docs/AsideNav.svelte @@ -31,6 +31,7 @@ const nav = { "cloudwatch-metrics": "/docs/middlewares/cloudwatch-metrics", "do-not-wait-for-empty-event-loop": "/docs/middlewares/do-not-wait-for-empty-event-loop", + "dsql-signer": "/docs/middlewares/dsql-signer", dynamodb: "/docs/middlewares/dynamodb", "error-logger": "/docs/middlewares/error-logger", "event-normalizer": "/docs/middlewares/event-normalizer", diff --git a/websites/middy.js.org/src/routes/docs/best-practices/bundling/+page.md b/websites/middy.js.org/src/routes/docs/best-practices/bundling/+page.md index 29f953247..d3fc78767 100644 --- a/websites/middy.js.org/src/routes/docs/best-practices/bundling/+page.md +++ b/websites/middy.js.org/src/routes/docs/best-practices/bundling/+page.md @@ -83,6 +83,7 @@ export default (input) => ({ external: [ // AWS SDK '@aws-sdk/client-apigatewaymanagementapi', // @middy/ws-response + '@aws-sdk/dsql-signer', // @middy/dsql-signer '@aws-sdk/client-rds', // @middy/rds-signer '@aws-sdk/client-s3', // @middy/s3-object-response '@aws-sdk/client-secretsmanager', // @middy/sercrets-manager @@ -140,6 +141,7 @@ export default { 'zlib', // @middy/http-content-encoding // AWS SDK '@aws-sdk/client-apigatewaymanagementapi', // @middy/ws-response + '@aws-sdk/dsql-signer', // @middy/dsql-signer '@aws-sdk/client-rds', // @middy/rds-signer '@aws-sdk/client-s3', // @middy/s3-object-response '@aws-sdk/client-secretsmanager', // @middy/sercrets-manager diff --git a/websites/middy.js.org/src/routes/docs/middlewares/dsql-signer/+page.md b/websites/middy.js.org/src/routes/docs/middlewares/dsql-signer/+page.md new file mode 100644 index 000000000..daf92e231 --- /dev/null +++ b/websites/middy.js.org/src/routes/docs/middlewares/dsql-signer/+page.md @@ -0,0 +1,82 @@ +--- +title: dsql-signer +description: "Generate Aurora DSQL IAM authentication tokens for secure database connections in Lambda." +--- + +Fetches Aurora DSQL credentials to be used when connecting to a DSQL cluster with IAM roles. + +## Install + +To install this middleware you can use NPM: + +```bash npm2yarn +npm install --save @middy/dsql-signer +npm install --save-dev @aws-sdk/dsql-signer +``` + +## Options + +- `AwsClient` (object) (default `DsqlSigner`): Signer class constructor (i.e. that has been instrumented with AWS XRay). Must be from `@aws-sdk/dsql-signer`. +- `awsClientOptions` (object) (optional): Options to pass to Signer class constructor. +- `fetchData` (object) (required): Mapping of internal key name to API request parameters. + - `hostname` (string) (required): DSQL cluster endpoint, e.g. `.dsql..on.aws`. Validated against the DSQL hostname format. + - `username` (string) (optional): Database role. When set to `"admin"` the middleware calls `getDbConnectAdminAuthToken`; any other value (or omitted) calls `getDbConnectAuthToken`. +- `disablePrefetch` (boolean) (default `false`): On cold start requests will trigger early if they can. +- `cacheKey` (string) (default `dsql-signer`): Cache key for the fetched data responses. Must be unique across all middleware. +- `cacheExpiry` (number) (default `-1`): How long fetch data responses should be cached for. `-1`: cache forever, `0`: never cache, `n`: cache for n ms. Note: DSQL tokens have a default TTL of 900 s; cache for less than that to avoid using expired tokens on warm invocations. +- `setToContext` (boolean) (default `false`): Store role tokens to `request.context`. + +NOTES: + +- Lambda is required to have IAM permission for `dsql:DbConnect` (non-admin role) or `dsql:DbConnectAdmin` (admin role) on the cluster ARN. +- DSQL connections always use port `5432`, database `postgres`, and require SSL. +- Region is taken from the default credential provider chain (e.g. `AWS_REGION`); cross-region access is not a supported DSQL pattern. + +## Sample usage + +```javascript +import middy from '@middy/core' +import dsqlSigner from '@middy/dsql-signer' +import { getInternal } from '@middy/util' +import pg from 'pg' + +const lambdaHandler = async (event, context) => { + const { dsqlToken } = await getInternal(['dsqlToken'], context) + + const client = new pg.Client({ + host: 'cluster-id.dsql.us-east-1.on.aws', + port: 5432, + database: 'postgres', + user: 'admin', + password: dsqlToken, + ssl: true + }) + await client.connect() + + const { rows } = await client.query('SELECT 1') + await client.end() + + return { + statusCode: 200, + headers: {}, + body: JSON.stringify({ rows }) + } +} + +export const handler = middy() + .use( + dsqlSigner({ + fetchData: { + dsqlToken: { + hostname: 'cluster-id.dsql.us-east-1.on.aws', + username: 'admin' + } + } + }) + ) + .handler(lambdaHandler) +``` + +## Bundling + +To exclude `@aws-sdk` add `@aws-sdk/dsql-signer` to the exclude list. diff --git a/websites/middy.js.org/src/routes/docs/middlewares/intro/+page.md b/websites/middy.js.org/src/routes/docs/middlewares/intro/+page.md index 8b5adf4fb..3a7aee8ee 100644 --- a/websites/middy.js.org/src/routes/docs/middlewares/intro/+page.md +++ b/websites/middy.js.org/src/routes/docs/middlewares/intro/+page.md @@ -49,6 +49,7 @@ Each middleware should do a single task. We try to balance each to be as perform ## Fetch Data - [`appconfig`](/docs/middlewares/appconfig): Fetch JSON configurations from AppConfig. +- [`dsql-signer`](/docs/middlewares/dsql-signer): Fetches token for connecting to Aurora DSQL with IAM users. - [`dynamodb`](/docs/middlewares/dynamodb): Fetch configurations from DynamoDB. - [`rds-signer`](/docs/middlewares/rds-signer): Fetches token for connecting to RDS with IAM users. - [`s3`](/docs/middlewares/s3): Fetch JSON configurations from S3. From 5e6bd0d6d4155e878f0a0f4c12e151072f81bfbf Mon Sep 17 00:00:00 2001 From: will Farrell Date: Fri, 1 May 2026 06:30:18 -0600 Subject: [PATCH 4/8] fix: provide extra context for error Signed-off-by: will Farrell --- packages/rds-signer/index.js | 2 +- packages/rds-signer/index.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/rds-signer/index.js b/packages/rds-signer/index.js index 6cd51f486..a037e94d7 100644 --- a/packages/rds-signer/index.js +++ b/packages/rds-signer/index.js @@ -79,7 +79,7 @@ const rdsSignerMiddleware = (opts = {}) => { // Catch Missing token, this usually means there is something wrong with the credentials if (!token.includes("X-Amz-Security-Token=")) { throw new Error("X-Amz-Security-Token Missing", { - cause: { package: pkg }, + cause: { package: pkg, method }, }); } return token; diff --git a/packages/rds-signer/index.test.js b/packages/rds-signer/index.test.js index 8718f6e4a..abc344fd4 100644 --- a/packages/rds-signer/index.test.js +++ b/packages/rds-signer/index.test.js @@ -323,7 +323,7 @@ test("It should catch if an invalid response is returned from fetch", async (t) strictEqual(e.message, "Failed to resolve internal values"); deepStrictEqual(e.cause.data, [ new Error("X-Amz-Security-Token Missing", { - cause: { package: "@middy/rds-signer" }, + cause: { package: "@middy/rds-signer", method: "getAuthToken" }, }), ]); } From ae4621b94940c4987eeefca0aa251dbb1a44a763 Mon Sep 17 00:00:00 2001 From: will Farrell Date: Fri, 1 May 2026 08:59:05 -0600 Subject: [PATCH 5/8] fix: accepted regexp Signed-off-by: will Farrell --- packages/util/index.js | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/packages/util/index.js b/packages/util/index.js index 06d98f3a4..4775b1d14 100644 --- a/packages/util/index.js +++ b/packages/util/index.js @@ -16,7 +16,7 @@ // Numeric: `minimum`/`maximum` (inclusive), `exclusiveMinimum`/ // `exclusiveMaximum` (exclusive), `multipleOf` (number/integer). // String: `minLength`/`maxLength` (string length), `pattern` (regex -// source string per JSON Schema, or a RegExp object). +// source string per JSON Schema). // { type: 'object' | 'object?', properties?: {...}, additionalProperties?: } // `properties` validates known keys with the flat-schema form. // `additionalProperties` validates every other key's value against the @@ -216,11 +216,8 @@ const checkRule = (rule, value, path, fail) => { fail(`Option '${path}' must be a multiple of ${multipleOf}`); } } - if (pattern !== undefined) { - const re = typeof pattern === "string" ? new RegExp(pattern) : pattern; - if (!re.test(value)) { - fail(`Option '${path}' must match pattern ${re}`); - } + if (pattern !== undefined && !new RegExp(pattern).test(value)) { + fail(`Option '${path}' must match pattern ${pattern}`); } if (minLength !== undefined && value.length < minLength) { fail(`Option '${path}' must have length >= ${minLength}`); From 206f9a1e125838198f95a9f82b9b718a8466772e Mon Sep 17 00:00:00 2001 From: will Farrell Date: Fri, 1 May 2026 09:29:11 -0600 Subject: [PATCH 6/8] feat: add in env defaults Signed-off-by: will Farrell --- packages/dsql-signer/index.js | 10 +- packages/dsql-signer/index.test.js | 135 ++++++++++++------- packages/rds-signer/index.js | 14 +- packages/rds-signer/index.test.js | 209 ++++++++++++++++++----------- 4 files changed, 240 insertions(+), 128 deletions(-) diff --git a/packages/dsql-signer/index.js b/packages/dsql-signer/index.js index 95244babf..f4f90bf70 100644 --- a/packages/dsql-signer/index.js +++ b/packages/dsql-signer/index.js @@ -41,7 +41,7 @@ const optionSchema = { }, username: { type: "string" }, }, - required: ["hostname"], + required: [], additionalProperties: true, }, }, @@ -63,6 +63,14 @@ export const dsqlSignerValidateOptions = (options) => const dsqlSignerMiddleware = (opts = {}) => { const options = { ...defaults, ...opts }; + const defaultFetchData = { + hostname: process.env.PGHOST ?? process.env.DBHOST, + username: process.env.PGUSER ?? process.env.DBUSER, + }; + for (const key of Object.keys(options.fetchData)) { + options.fetchData[key] = { ...defaultFetchData, ...options.fetchData[key] }; + } + const fetchDataKeys = Object.keys(options.fetchData); const clients = {}; const fetchRequest = (request, cachedValues = {}) => { diff --git a/packages/dsql-signer/index.test.js b/packages/dsql-signer/index.test.js index 2ebe747ea..ab846f724 100644 --- a/packages/dsql-signer/index.test.js +++ b/packages/dsql-signer/index.test.js @@ -1,9 +1,17 @@ import { deepStrictEqual, ok, strictEqual } from "node:assert/strict"; -import { test } from "node:test"; +import { after, before, test } from "node:test"; import middy from "../core/index.js"; import { clearCache, getInternal } from "../util/index.js"; import dsqlSigner, { dsqlSignerValidateOptions } from "./index.js"; +before(() => { + process.env.PGHOST = "cluster.dsql.us-east-1.on.aws"; +}); + +after(() => { + delete process.env.PGHOST; +}); + test.afterEach((t) => { t.mock.reset(); clearCache(); @@ -38,10 +46,7 @@ test("It should set token to internal storage (token)", async (t) => { AwsClient, cacheExpiry: 0, fetchData: { - token: { - region: "us-east-1", - hostname: "cluster.dsql.us-east-1.on.aws", - }, + token: { region: "us-east-1" }, }, disablePrefetch: true, }), @@ -76,11 +81,7 @@ test("It should call admin token method when username is 'admin'", async (t) => AwsClient, cacheExpiry: 0, fetchData: { - token: { - region: "us-east-1", - hostname: "cluster.dsql.us-east-1.on.aws", - username: "admin", - }, + token: { region: "us-east-1", username: "admin" }, }, disablePrefetch: true, }), @@ -116,11 +117,7 @@ test("It should call non-admin token method when username is a custom role", asy AwsClient, cacheExpiry: 0, fetchData: { - token: { - region: "us-east-1", - hostname: "cluster.dsql.us-east-1.on.aws", - username: "app_reader", - }, + token: { region: "us-east-1", username: "app_reader" }, }, disablePrefetch: true, }), @@ -150,10 +147,7 @@ test("It should not pass `username` to the signer constructor", async (t) => { AwsClient, cacheExpiry: 0, fetchData: { - token: { - hostname: "cluster.dsql.us-east-1.on.aws", - username: "admin", - }, + token: { username: "admin" }, }, disablePrefetch: true, }), @@ -225,10 +219,7 @@ test("It should set DSQL token to context", async (t) => { AwsClient, cacheExpiry: 0, fetchData: { - token: { - region: "us-east-1", - hostname: "cluster.dsql.us-east-1.on.aws", - }, + token: { region: "us-east-1" }, }, setToContext: true, disablePrefetch: true, @@ -259,10 +250,7 @@ test("It should not call aws-sdk again if parameter is cached", async (t) => { AwsClient, cacheExpiry: -1, fetchData: { - token: { - region: "us-east-1", - hostname: "cluster.dsql.us-east-1.on.aws", - }, + token: { region: "us-east-1" }, }, }), ) @@ -295,10 +283,7 @@ test("It should call aws-sdk if cache enabled but cached param has expired", asy AwsClient, cacheExpiry: 0, fetchData: { - token: { - region: "us-east-1", - hostname: "cluster.dsql.us-east-1.on.aws", - }, + token: { region: "us-east-1" }, }, disablePrefetch: true, }), @@ -323,10 +308,7 @@ test("It should catch if an error is returned from fetch", async (t) => { AwsClient, cacheExpiry: 0, fetchData: { - token: { - region: "us-east-1", - hostname: "cluster.dsql.us-east-1.on.aws", - }, + token: { region: "us-east-1" }, }, setToContext: true, disablePrefetch: true, @@ -352,10 +334,7 @@ test("It should catch if a token without X-Amz-Security-Token is returned from f AwsClient, cacheExpiry: 0, fetchData: { - token: { - region: "us-east-1", - hostname: "cluster.dsql.us-east-1.on.aws", - }, + token: { region: "us-east-1" }, }, setToContext: true, disablePrefetch: true, @@ -433,6 +412,71 @@ test("It should export dsqlSignerParam helper for TypeScript type inference", as strictEqual(result, paramName); }); +test("It should use DBHOST/DBUSER env var defaults as fallback", async (t) => { + const savedPGHOST = process.env.PGHOST; + delete process.env.PGHOST; + process.env.DBHOST = "cluster.dsql.us-east-1.on.aws"; + process.env.DBUSER = "app_reader"; + t.after(() => { + delete process.env.DBHOST; + delete process.env.DBUSER; + process.env.PGHOST = savedPGHOST; + }); + + const getDbConnectAuthToken = t.mock.fn( + async () => "X-Amz-Security-Token=role-token", + ); + let receivedConfig; + class AwsClient { + constructor(config) { + receivedConfig = config; + } + getDbConnectAuthToken = getDbConnectAuthToken; + } + const handler = middy(() => {}).use( + dsqlSigner({ + AwsClient, + cacheExpiry: 0, + fetchData: { token: {} }, + disablePrefetch: true, + }), + ); + + await handler(defaultEvent, defaultContext); + strictEqual(receivedConfig.hostname, "cluster.dsql.us-east-1.on.aws"); + strictEqual(getDbConnectAuthToken.mock.callCount(), 1); +}); + +test("It should prefer explicit fetchData values over env var defaults", async (t) => { + const getDbConnectAuthToken = t.mock.fn( + async () => "X-Amz-Security-Token=role-token", + ); + let receivedConfig; + class AwsClient { + constructor(config) { + receivedConfig = config; + } + getDbConnectAuthToken = getDbConnectAuthToken; + } + const handler = middy(() => {}).use( + dsqlSigner({ + AwsClient, + cacheExpiry: 0, + fetchData: { + token: { + hostname: "explicit.dsql.us-east-1.on.aws", + username: "app_reader", + }, + }, + disablePrefetch: true, + }), + ); + + await handler(defaultEvent, defaultContext); + strictEqual(receivedConfig.hostname, "explicit.dsql.us-east-1.on.aws"); + strictEqual(getDbConnectAuthToken.mock.callCount(), 1); +}); + test("dsqlSignerValidateOptions accepts valid options and rejects typos", () => { dsqlSignerValidateOptions({ cacheKey: "x", cacheExpiry: 0 }); dsqlSignerValidateOptions({}); @@ -465,16 +509,9 @@ test("dsqlSignerValidateOptions accepts valid fetchData entry", () => { }); }); -test("dsqlSignerValidateOptions rejects fetchData entry missing hostname", () => { - try { - dsqlSignerValidateOptions({ - fetchData: { token: { username: "admin" } }, - }); - ok(false, "expected throw"); - } catch (e) { - ok(e instanceof TypeError); - strictEqual(e.cause.package, "@middy/dsql-signer"); - } +test("dsqlSignerValidateOptions accepts fetchData entry relying on env var defaults", () => { + dsqlSignerValidateOptions({ fetchData: { token: {} } }); + dsqlSignerValidateOptions({ fetchData: { token: { username: "admin" } } }); }); test("dsqlSignerValidateOptions rejects fetchData entry with non-string username", () => { diff --git a/packages/rds-signer/index.js b/packages/rds-signer/index.js index a037e94d7..cbb390b9e 100644 --- a/packages/rds-signer/index.js +++ b/packages/rds-signer/index.js @@ -38,7 +38,7 @@ const optionSchema = { port: { type: "integer", minimum: 1, maximum: 65535 }, username: { type: "string" }, }, - required: ["hostname", "port", "username"], + required: [], additionalProperties: true, }, }, @@ -60,6 +60,18 @@ export const rdsSignerValidateOptions = (options) => const rdsSignerMiddleware = (opts = {}) => { const options = { ...defaults, ...opts }; + const defaultFetchData = { + hostname: process.env.PGHOST ?? process.env.DBHOST, + port: Number.parseInt( + process.env.PGPORT ?? process.env.DBPORT ?? "5432", + 10, + ), + username: process.env.PGUSER ?? process.env.DBUSER, + }; + for (const key of Object.keys(options.fetchData)) { + options.fetchData[key] = { ...defaultFetchData, ...options.fetchData[key] }; + } + const fetchDataKeys = Object.keys(options.fetchData); const clients = {}; const fetchRequest = (request, cachedValues = {}) => { diff --git a/packages/rds-signer/index.test.js b/packages/rds-signer/index.test.js index abc344fd4..0dd74b396 100644 --- a/packages/rds-signer/index.test.js +++ b/packages/rds-signer/index.test.js @@ -1,9 +1,21 @@ import { deepStrictEqual, ok, strictEqual } from "node:assert/strict"; -import { test } from "node:test"; +import { after, before, test } from "node:test"; import middy from "../core/index.js"; import { clearCache, getInternal } from "../util/index.js"; import rdsSigner, { rdsSignerValidateOptions } from "./index.js"; +before(() => { + process.env.PGHOST = "hostname"; + process.env.PGPORT = "5432"; + process.env.PGUSER = "username"; +}); + +after(() => { + delete process.env.PGHOST; + delete process.env.PGPORT; + delete process.env.PGUSER; +}); + test.afterEach((t) => { t.mock.reset(); clearCache(); @@ -37,12 +49,7 @@ test("It should set token to internal storage (token)", async (t) => { AwsClient, cacheExpiry: 0, fetchData: { - token: { - region: "us-east-1", - hostname: "hostname", - username: "username", - port: 5432, - }, + token: { region: "us-east-1" }, }, disablePrefetch: true, }), @@ -82,18 +89,8 @@ test("It should set tokens to internal storage (token)", async (t) => { AwsClient, cacheExpiry: 0, fetchData: { - token1: { - region: "us-east-1", - hostname: "hostname-reader", - username: "username", - port: 5432, - }, - token2: { - region: "us-east-1", - hostname: "hostname-writer", - username: "username", - port: 5432, - }, + token1: { region: "us-east-1", hostname: "hostname-reader" }, + token2: { region: "us-east-1", hostname: "hostname-writer" }, }, disablePrefetch: true, }), @@ -126,12 +123,7 @@ test("It should set Signer token to internal storage without prefetch", async (t AwsClient, cacheExpiry: 0, fetchData: { - token: { - region: "us-east-1", - hostname: "hostname", - username: "username", - port: 5432, - }, + token: { region: "us-east-1" }, }, disablePrefetch: true, }), @@ -163,12 +155,7 @@ test("It should set Signer token to context", async (t) => { AwsClient, cacheExpiry: 0, fetchData: { - token: { - region: "us-east-1", - hostname: "hostname", - username: "username", - port: 5432, - }, + token: { region: "us-east-1" }, }, setToContext: true, disablePrefetch: true, @@ -202,12 +189,7 @@ test("It should not call aws-sdk again if parameter is cached", async (t) => { AwsClient, cacheExpiry: -1, fetchData: { - token: { - region: "us-east-1", - hostname: "hostname", - username: "username", - port: 5432, - }, + token: { region: "us-east-1" }, }, }), ) @@ -243,12 +225,7 @@ test("It should call aws-sdk if cache enabled but cached param has expired", asy AwsClient, cacheExpiry: 0, fetchData: { - token: { - region: "us-east-1", - hostname: "hostname", - username: "username", - port: 5432, - }, + token: { region: "us-east-1" }, }, disablePrefetch: true, }), @@ -273,12 +250,7 @@ test("It should catch if an error is returned from fetch", async (t) => { AwsClient, cacheExpiry: 0, fetchData: { - token: { - region: "us-east-1", - hostname: "hostname", - username: "username", - port: 5432, - }, + token: { region: "us-east-1" }, }, setToContext: true, disablePrefetch: true, @@ -304,12 +276,7 @@ test("It should catch if an invalid response is returned from fetch", async (t) AwsClient, cacheExpiry: 0, fetchData: { - token: { - region: "us-east-1", - hostname: "hostname", - username: "username", - port: 5432, - }, + token: { region: "us-east-1" }, }, setToContext: true, disablePrefetch: true, @@ -370,18 +337,8 @@ test("It should skip fetching already cached values when fetching multiple keys" AwsClient, cacheExpiry: 1000, fetchData: { - token1: { - region: "us-east-1", - hostname: "hostname-reader", - username: "username", - port: 5432, - }, - token2: { - region: "us-east-1", - hostname: "hostname-writer", - username: "username", - port: 5432, - }, + token1: { region: "us-east-1", hostname: "hostname-reader" }, + token2: { region: "us-east-1", hostname: "hostname-writer" }, }, }), ) @@ -408,6 +365,109 @@ test("It should export rdsSignerParam helper for TypeScript type inference", asy strictEqual(result, paramName); }); +test("It should use DBHOST/DBPORT/DBUSER env var defaults as fallback", async (t) => { + const savedPGHOST = process.env.PGHOST; + const savedPGPORT = process.env.PGPORT; + const savedPGUSER = process.env.PGUSER; + delete process.env.PGHOST; + delete process.env.PGPORT; + delete process.env.PGUSER; + process.env.DBHOST = "db.example.com"; + process.env.DBPORT = "5434"; + process.env.DBUSER = "dbuser"; + t.after(() => { + delete process.env.DBHOST; + delete process.env.DBPORT; + delete process.env.DBUSER; + process.env.PGHOST = savedPGHOST; + process.env.PGPORT = savedPGPORT; + process.env.PGUSER = savedPGUSER; + }); + + let receivedConfig; + class AwsClient { + constructor(config) { + receivedConfig = config; + } + getAuthToken = t.mock.fn( + async () => "https://rds.amazonaws.com?X-Amz-Security-Token=token", + ); + } + const handler = middy(() => {}).use( + rdsSigner({ + AwsClient, + cacheExpiry: 0, + fetchData: { token: {} }, + disablePrefetch: true, + }), + ); + + await handler(defaultEvent, defaultContext); + strictEqual(receivedConfig.hostname, "db.example.com"); + strictEqual(receivedConfig.port, 5434); + strictEqual(receivedConfig.username, "dbuser"); +}); + +test("It should use default port 5432 when no PGPORT/DBPORT env var is set", async (t) => { + const savedPGPORT = process.env.PGPORT; + delete process.env.PGPORT; + t.after(() => { + process.env.PGPORT = savedPGPORT; + }); + + let receivedConfig; + class AwsClient { + constructor(config) { + receivedConfig = config; + } + getAuthToken = t.mock.fn( + async () => "https://rds.amazonaws.com?X-Amz-Security-Token=token", + ); + } + const handler = middy(() => {}).use( + rdsSigner({ + AwsClient, + cacheExpiry: 0, + fetchData: { token: {} }, + disablePrefetch: true, + }), + ); + + await handler(defaultEvent, defaultContext); + strictEqual(receivedConfig.port, 5432); +}); + +test("It should prefer explicit fetchData values over env var defaults", async (t) => { + let receivedConfig; + class AwsClient { + constructor(config) { + receivedConfig = config; + } + getAuthToken = t.mock.fn( + async () => "https://rds.amazonaws.com?X-Amz-Security-Token=token", + ); + } + const handler = middy(() => {}).use( + rdsSigner({ + AwsClient, + cacheExpiry: 0, + fetchData: { + token: { + hostname: "explicit.example.com", + port: 9999, + username: "explicit", + }, + }, + disablePrefetch: true, + }), + ); + + await handler(defaultEvent, defaultContext); + strictEqual(receivedConfig.hostname, "explicit.example.com"); + strictEqual(receivedConfig.port, 9999); + strictEqual(receivedConfig.username, "explicit"); +}); + test("rdsSignerValidateOptions accepts valid options and rejects typos", () => { rdsSignerValidateOptions({ cacheKey: "x", cacheExpiry: 0 }); rdsSignerValidateOptions({}); @@ -442,16 +502,11 @@ test("rdsSignerValidateOptions accepts valid fetchData entry", () => { }); }); -test("rdsSignerValidateOptions rejects fetchData entry missing required fields", () => { - try { - rdsSignerValidateOptions({ - fetchData: { token: { hostname: "db.example.com" } }, - }); - ok(false, "expected throw"); - } catch (e) { - ok(e instanceof TypeError); - strictEqual(e.cause.package, "@middy/rds-signer"); - } +test("rdsSignerValidateOptions accepts fetchData entry relying on env var defaults", () => { + rdsSignerValidateOptions({ fetchData: { token: {} } }); + rdsSignerValidateOptions({ + fetchData: { token: { hostname: "db.example.com" } }, + }); }); test("rdsSignerValidateOptions rejects fetchData entry with non-integer port", () => { From 0b42dc10791325aa6f1ff4397e42a27c8ad71793 Mon Sep 17 00:00:00 2001 From: will Farrell Date: Fri, 1 May 2026 09:31:08 -0600 Subject: [PATCH 7/8] feat: add in http-mpp middleware Signed-off-by: will Farrell --- packages/http-mpp/README.md | 161 +++++++++++++ packages/http-mpp/index.d.ts | 29 +++ packages/http-mpp/index.fuzz.js | 74 ++++++ packages/http-mpp/index.js | 98 ++++++++ packages/http-mpp/index.test.js | 388 ++++++++++++++++++++++++++++++++ packages/http-mpp/index.tst.ts | 58 +++++ packages/http-mpp/mppx.d.ts | 13 ++ packages/http-mpp/mppx.js | 15 ++ packages/http-mpp/package.json | 89 ++++++++ 9 files changed, 925 insertions(+) create mode 100644 packages/http-mpp/README.md create mode 100644 packages/http-mpp/index.d.ts create mode 100644 packages/http-mpp/index.fuzz.js create mode 100644 packages/http-mpp/index.js create mode 100644 packages/http-mpp/index.test.js create mode 100644 packages/http-mpp/index.tst.ts create mode 100644 packages/http-mpp/mppx.d.ts create mode 100644 packages/http-mpp/mppx.js create mode 100644 packages/http-mpp/package.json diff --git a/packages/http-mpp/README.md b/packages/http-mpp/README.md new file mode 100644 index 000000000..a0d99a90b --- /dev/null +++ b/packages/http-mpp/README.md @@ -0,0 +1,161 @@ +
+

Middy `http-mpp` middleware

+ Middy logo +

Machine Payments Protocol (MPP) HTTP 402 payment gate middleware for the middy framework, the stylish Node.js middleware engine for AWS Lambda

+

+ GitHub Actions unit test status + GitHub Actions dast test status + GitHub Actions perf test status + GitHub Actions SAST test status + GitHub Actions lint test status +
+ npm version + npm install size + + npm weekly downloads + + npm provenance +
+ Open Source Security Foundation (OpenSSF) Scorecard + SLSA 3 + + Checked with Biome + Conventional Commits +
+

+

You can read the documentation at: https://middy.js.org/docs/middlewares/http-mpp

+
+ +## What is MPP? + +[Machine Payments Protocol (MPP)](https://mpp.dev) enables APIs to charge for access via HTTP 402. The flow: + +1. Client requests a payment-gated resource +2. Server returns `402 Payment Required` with a `WWW-Authenticate: MPP ...` challenge +3. Client pays via Tempo (EVM stablecoin), Stripe, or Lightning +4. Client retries with `Authorization: MPP ` +5. Server verifies the token and serves the resource + +## Install + +```bash +npm install --save @middy/http-mpp +``` + +To use the built-in `mppx` verify helper, also install the optional peer dependency: + +```bash +npm install --save mppx +``` + +## Options + +| Option | Type | Default | Description | +|---|---|---|---| +| `realm` | `string` | `"api"` | Value for `realm` in the `WWW-Authenticate` header | +| `methods` | `MethodOptions[]` | required | One or more payment method descriptors | +| `methods[].method` | `string` | required | Payment method name (e.g. `"tempo"`, `"lightning"`, `"stripe"`) | +| `methods[].recipient` | `string` | required | Recipient wallet/account address | +| `methods[].currency` | `string` | required | Token contract address or currency code | +| `methods[].amount` | `number` | required | Amount to charge (must be > 0) | +| `verify` | `function` | `undefined` | Token verification function. **Omitting this is insecure in production.** | + +## Basic usage with mppx + +```js +import middy from '@middy/core' +import httpMpp from '@middy/http-mpp' +import { httpMppVerify } from '@middy/http-mpp/mppx' +import { tempo } from 'mppx/server' + +const verify = httpMppVerify({ + methods: [ + tempo({ + recipient: process.env.MPP_RECIPIENT, + currency: process.env.MPP_CURRENCY, + decimals: 6, + }), + ], + realm: 'api', + secretKey: process.env.MPP_SECRET_KEY, +}) + +export const handler = middy(async (event) => { + return { statusCode: 200, body: JSON.stringify({ message: 'Premium content!' }) } +}) + .use(httpHeaderNormalizer()) + .use(httpMpp({ + methods: [ + { + method: 'tempo', + recipient: process.env.MPP_RECIPIENT, + currency: process.env.MPP_CURRENCY, + amount: 0.01, + }, + ], + verify, + })) + .use(httpErrorHandler()) +``` + +## Verify patterns + +**Charge model** - one payment, one token, on-chain verification (~600ms): + +```js +import { httpMppVerify } from '@middy/http-mpp/mppx' +import { tempo } from 'mppx/server' + +const verify = httpMppVerify({ + methods: [tempo({ recipient: '0x...', currency: '0x...', decimals: 6 })], + secretKey: process.env.MPP_SECRET_KEY, +}) +``` + +**Session model** - payment channel, cache-backed verification (~10ms): + +```js +import { httpMppVerify } from '@middy/http-mpp/mppx' +import { tempo } from 'mppx/server' + +const verify = httpMppVerify({ + methods: [tempo({ recipient: '0x...', currency: '0x...', decimals: 6, waitForConfirmation: false })], + secretKey: process.env.MPP_SECRET_KEY, +}) +``` + +**Custom verify with DynamoDB replay prevention**: + +```js +const verify = async (token) => { + const used = await dynamo.get({ TableName: 'usedTokens', Key: { token } }).promise() + if (used.Item) return false + const valid = await checkOnChain(token) + if (valid) { + await dynamo.put({ TableName: 'usedTokens', Item: { token, ttl: Math.floor(Date.now() / 1000) + 3600 } }).promise() + } + return valid +} +``` + +## Recommended middleware stack order + +```js +middy(handler) + .use(httpHeaderNormalizer()) // normalize header casing + .use(httpMpp({ ... })) // payment gate + .use(httpErrorHandler()) // format error responses +``` + +## Security note + +Omitting the `verify` option puts the middleware in header-presence-only mode: any request with `Authorization: MPP ` is allowed through. This is useful for local development and testing but **must not be used in production**. + +## Contributing + +Everyone is very welcome to contribute to this repository. Feel free to [raise issues](https://github.com/middyjs/middy/issues) or to [submit Pull Requests](https://github.com/middyjs/middy/pulls). + + +## License + +Licensed under [MIT License](https://github.com/middyjs/middy/blob/main/LICENSE). Copyright (c) 2017-2026 [will Farrell](https://github.com/willfarrell), [Luciano Mammino](https://github.com/lmammino), and [Middy contributors](https://github.com/middyjs/middy/graphs/contributors). diff --git a/packages/http-mpp/index.d.ts b/packages/http-mpp/index.d.ts new file mode 100644 index 000000000..e9e873ea0 --- /dev/null +++ b/packages/http-mpp/index.d.ts @@ -0,0 +1,29 @@ +// Copyright 2017 - 2026 will Farrell, Luciano Mammino, and Middy contributors. +// SPDX-License-Identifier: MIT +import type middy from "@middy/core"; + +export interface MethodOptions { + method: string; + recipient: string; + currency: string; + amount: number; +} + +export interface Options { + realm?: string; + methods?: MethodOptions[]; + verify?: ( + token: string, + request: middy.Request, + ) => boolean | Promise; +} + +declare function httpMpp( + options?: Options, +): middy.MiddlewareObj; + +export declare function httpMppValidateOptions( + options?: Record, +): void; + +export default httpMpp; diff --git a/packages/http-mpp/index.fuzz.js b/packages/http-mpp/index.fuzz.js new file mode 100644 index 000000000..f9980545e --- /dev/null +++ b/packages/http-mpp/index.fuzz.js @@ -0,0 +1,74 @@ +import { test } from "node:test"; +import fc from "fast-check"; +import middy from "../core/index.js"; +import middleware from "./index.js"; + +const defaultMethods = [ + { + method: "tempo", + recipient: "0x1234567890123456789012345678901234567890", + currency: "0xabcdef0123456789012345678901234567890123", + amount: 0.01, + }, +]; + +const handler = middy(() => ({ statusCode: 200 })).use( + middleware({ methods: defaultMethods }), +); + +const defaultContext = { + getRemainingTimeInMillis: () => 1000, +}; + +test("fuzz `event` w/ `object`", async () => { + await fc.assert( + fc.asyncProperty(fc.object(), async (event) => { + await handler(event, defaultContext); + }), + { + numRuns: 100_000, + examples: [], + }, + ); +}); + +test("fuzz `event` w/ `record`", async () => { + await fc.assert( + fc.asyncProperty( + fc.record({ + headers: fc.object(), + }), + async (event) => { + await handler(event, defaultContext); + }, + ), + { + numRuns: 100_000, + examples: [], + }, + ); +}); + +test("fuzz `Authorization` header value", async () => { + await fc.assert( + fc.asyncProperty( + fc.record({ + headers: fc.record({ + Authorization: fc.oneof( + fc.constant(undefined), + fc.constant("MPP "), + fc.string().map((s) => `MPP ${s}`), + fc.string(), + ), + }), + }), + async (event) => { + await handler(event, defaultContext); + }, + ), + { + numRuns: 100_000, + examples: [], + }, + ); +}); diff --git a/packages/http-mpp/index.js b/packages/http-mpp/index.js new file mode 100644 index 000000000..ae302ce58 --- /dev/null +++ b/packages/http-mpp/index.js @@ -0,0 +1,98 @@ +// Copyright 2017 - 2026 will Farrell, Luciano Mammino, and Middy contributors. +// SPDX-License-Identifier: MIT +import { normalizeHttpResponse, validateOptions } from "@middy/util"; + +const name = "http-mpp"; +const pkg = `@middy/${name}`; + +const methodSchema = { + type: "object", + properties: { + method: { type: "string" }, + recipient: { type: "string" }, + currency: { type: "string" }, + amount: { type: "number", exclusiveMinimum: 0 }, + }, + required: ["method", "recipient", "currency", "amount"], + additionalProperties: false, +}; + +const optionSchema = { + type: "object", + properties: { + realm: { type: "string" }, + methods: { type: "array", items: methodSchema }, + verify: { instanceof: "Function" }, + }, + additionalProperties: false, +}; + +export const httpMppValidateOptions = (options) => + validateOptions(pkg, optionSchema, options); + +const defaults = { + realm: "api", + methods: [], + verify: undefined, +}; + +const buildWwwAuthenticateValues = (realm, methods) => + methods.map( + ({ method, recipient, currency, amount }) => + `MPP realm="${realm}", method="${method}", params="recipient=${recipient},currency=${currency},amount=${amount}"`, + ); + +const httpMppMiddleware = (opts = {}) => { + const options = { ...defaults, ...opts }; + httpMppValidateOptions(options); + + if (!Array.isArray(options.methods) || options.methods.length === 0) { + throw new Error("options.methods must be a non-empty array", { + cause: { package: pkg }, + }); + } + + const wwwAuthValues = buildWwwAuthenticateValues( + options.realm, + options.methods, + ); + + const httpMppMiddlewareBefore = async (request) => { + const eventHeaders = request.event?.headers ?? {}; + const authHeader = eventHeaders.Authorization ?? eventHeaders.authorization; + + if (!authHeader?.startsWith("MPP ")) { + return buildChallengeResponse(request, wwwAuthValues); + } + + const token = authHeader.slice(4); + + if (!options.verify) return; + + let verified = false; + try { + verified = await options.verify(token, request); + } catch { + verified = false; + } + + if (!verified) { + return buildChallengeResponse(request, wwwAuthValues); + } + }; + + return { before: httpMppMiddlewareBefore }; +}; + +const buildChallengeResponse = (request, wwwAuthValues) => { + normalizeHttpResponse(request); + request.response.statusCode = 402; + request.response.headers["WWW-Authenticate"] = wwwAuthValues.join(", "); + if (wwwAuthValues.length > 1) { + request.response.multiValueHeaders ??= {}; + request.response.multiValueHeaders["WWW-Authenticate"] = wwwAuthValues; + } + return request.response; +}; + +export default httpMppMiddleware; diff --git a/packages/http-mpp/index.test.js b/packages/http-mpp/index.test.js new file mode 100644 index 000000000..5806e7eae --- /dev/null +++ b/packages/http-mpp/index.test.js @@ -0,0 +1,388 @@ +import { + deepStrictEqual, + doesNotThrow, + ok, + strictEqual, + throws, +} from "node:assert/strict"; +import { test } from "node:test"; +import middy from "../core/index.js"; +import httpMpp, { httpMppValidateOptions } from "./index.js"; + +const defaultContext = { + getRemainingTimeInMillis: () => 1000, +}; + +const defaultMethods = [ + { + method: "tempo", + recipient: "0x1234567890123456789012345678901234567890", + currency: "0xabcdef0123456789012345678901234567890123", + amount: 0.01, + }, +]; + +// *** challenge - missing/wrong Authorization *** + +test("Should return 402 with WWW-Authenticate when no Authorization header (v1.0 event)", async () => { + const handler = middy(() => ({ statusCode: 200 })); + handler.use(httpMpp({ methods: defaultMethods })); + + const event = { httpMethod: "GET", headers: {} }; + const response = await handler(event, defaultContext); + + deepStrictEqual(response, { + statusCode: 402, + headers: { + "WWW-Authenticate": + 'MPP realm="api", method="tempo", params="recipient=0x1234567890123456789012345678901234567890,currency=0xabcdef0123456789012345678901234567890123,amount=0.01"', + }, + }); +}); + +test("Should return 402 with WWW-Authenticate when no Authorization header (v2.0 event)", async () => { + const handler = middy(() => ({ statusCode: 200 })); + handler.use(httpMpp({ methods: defaultMethods })); + + const event = { + version: "2.0", + requestContext: { http: { method: "GET" } }, + headers: {}, + }; + const response = await handler(event, defaultContext); + + strictEqual(response.statusCode, 402); + ok(response.headers["WWW-Authenticate"].startsWith("MPP ")); +}); + +test("Should return 402 when Authorization header has wrong scheme", async () => { + const handler = middy(() => ({ statusCode: 200 })); + handler.use(httpMpp({ methods: defaultMethods })); + + const event = { headers: { Authorization: "Bearer sometoken" } }; + const response = await handler(event, defaultContext); + + strictEqual(response.statusCode, 402); + ok(response.headers["WWW-Authenticate"]); +}); + +test("Should return 402 when Authorization header is Basic scheme", async () => { + const handler = middy(() => ({ statusCode: 200 })); + handler.use(httpMpp({ methods: defaultMethods })); + + const event = { headers: { Authorization: "Basic dXNlcjpwYXNz" } }; + const response = await handler(event, defaultContext); + + strictEqual(response.statusCode, 402); +}); + +test("Should return 402 when event has no headers property", async () => { + const handler = middy(() => ({ statusCode: 200 })); + handler.use(httpMpp({ methods: defaultMethods })); + + const event = {}; + const response = await handler(event, defaultContext); + + strictEqual(response.statusCode, 402); +}); + +test("Should not call handler when Authorization is absent", async () => { + let handlerCalled = false; + const handler = middy(() => { + handlerCalled = true; + return { statusCode: 200 }; + }); + handler.use(httpMpp({ methods: defaultMethods })); + + const event = { headers: {} }; + await handler(event, defaultContext); + + strictEqual(handlerCalled, false); +}); + +// *** token present, no verify (dev mode) *** + +test("Should allow request when MPP token present and no verify option", async () => { + const handler = middy(() => ({ statusCode: 200 })); + handler.use(httpMpp({ methods: defaultMethods })); + + const event = { headers: { Authorization: "MPP sometoken" } }; + const response = await handler(event, defaultContext); + + strictEqual(response.statusCode, 200); +}); + +test("Should allow request with lowercase authorization header (APIGW v2)", async () => { + const handler = middy(() => ({ statusCode: 200 })); + handler.use(httpMpp({ methods: defaultMethods })); + + const event = { headers: { authorization: "MPP sometoken" } }; + const response = await handler(event, defaultContext); + + strictEqual(response.statusCode, 200); +}); + +// *** verify function *** + +test("Should allow request when verify returns true", async () => { + const handler = middy(() => ({ statusCode: 200 })); + handler.use( + httpMpp({ + methods: defaultMethods, + verify: async () => true, + }), + ); + + const event = { headers: { Authorization: "MPP validtoken" } }; + const response = await handler(event, defaultContext); + + strictEqual(response.statusCode, 200); +}); + +test("Should return 402 when verify returns false", async () => { + const handler = middy(() => ({ statusCode: 200 })); + handler.use( + httpMpp({ + methods: defaultMethods, + verify: async () => false, + }), + ); + + const event = { headers: { Authorization: "MPP badtoken" } }; + const response = await handler(event, defaultContext); + + strictEqual(response.statusCode, 402); + ok(response.headers["WWW-Authenticate"]); +}); + +test("Should return 402 when verify throws", async () => { + const handler = middy(() => ({ statusCode: 200 })); + handler.use( + httpMpp({ + methods: defaultMethods, + verify: async () => { + throw new Error("network error"); + }, + }), + ); + + const event = { headers: { Authorization: "MPP sometoken" } }; + const response = await handler(event, defaultContext); + + strictEqual(response.statusCode, 402); +}); + +test("Should pass token string to verify without MPP prefix", async () => { + let capturedToken = null; + const handler = middy(() => ({ statusCode: 200 })); + handler.use( + httpMpp({ + methods: defaultMethods, + verify: async (token) => { + capturedToken = token; + return true; + }, + }), + ); + + const event = { headers: { Authorization: "MPP mytoken123" } }; + await handler(event, defaultContext); + + strictEqual(capturedToken, "mytoken123"); +}); + +test("Should pass request object to verify as second argument", async () => { + let capturedRequest = null; + const handler = middy(() => ({ statusCode: 200 })); + handler.use( + httpMpp({ + methods: defaultMethods, + verify: async (token, request) => { + capturedRequest = request; + return true; + }, + }), + ); + + const event = { headers: { Authorization: "MPP sometoken" } }; + await handler(event, defaultContext); + + ok(capturedRequest); + ok(capturedRequest.event); +}); + +test("Should support async verify returning Promise", async () => { + const handler = middy(() => ({ statusCode: 200 })); + handler.use( + httpMpp({ + methods: defaultMethods, + verify: (token) => Promise.resolve(token === "validtoken"), + }), + ); + + const event = { headers: { Authorization: "MPP validtoken" } }; + const response = await handler(event, defaultContext); + + strictEqual(response.statusCode, 200); +}); + +// *** WWW-Authenticate header format *** + +test("Should format WWW-Authenticate header with correct realm, method, and params", async () => { + const handler = middy(() => ({ statusCode: 200 })); + handler.use( + httpMpp({ + methods: [ + { + method: "tempo", + recipient: "0xabc", + currency: "0xdef", + amount: 0.01, + }, + ], + }), + ); + + const event = { headers: {} }; + const response = await handler(event, defaultContext); + + strictEqual( + response.headers["WWW-Authenticate"], + 'MPP realm="api", method="tempo", params="recipient=0xabc,currency=0xdef,amount=0.01"', + ); +}); + +test("Should use custom realm in WWW-Authenticate", async () => { + const handler = middy(() => ({ statusCode: 200 })); + handler.use( + httpMpp({ + realm: "myapp", + methods: [ + { + method: "tempo", + recipient: "0xabc", + currency: "0xdef", + amount: 0.01, + }, + ], + }), + ); + + const event = { headers: {} }; + const response = await handler(event, defaultContext); + + ok(response.headers["WWW-Authenticate"].includes('realm="myapp"')); +}); + +test("Should use default realm 'api' when not specified", async () => { + const handler = middy(() => ({ statusCode: 200 })); + handler.use( + httpMpp({ + methods: [ + { method: "tempo", recipient: "0x", currency: "0x", amount: 0.01 }, + ], + }), + ); + + const event = { headers: {} }; + const response = await handler(event, defaultContext); + + ok(response.headers["WWW-Authenticate"].includes('realm="api"')); +}); + +test("Should set multiValueHeaders for multiple payment methods", async () => { + const handler = middy(() => ({ statusCode: 200 })); + handler.use( + httpMpp({ + methods: [ + { + method: "tempo", + recipient: "0xabc", + currency: "0xdef", + amount: 0.01, + }, + { + method: "lightning", + recipient: "lnbc...", + currency: "BTC", + amount: 0.0001, + }, + ], + }), + ); + + const event = { headers: {} }; + const response = await handler(event, defaultContext); + + strictEqual(response.statusCode, 402); + strictEqual( + Array.isArray(response.multiValueHeaders["WWW-Authenticate"]), + true, + ); + strictEqual(response.multiValueHeaders["WWW-Authenticate"].length, 2); +}); + +// *** validateOptions *** + +test("httpMppValidateOptions accepts valid options", () => { + doesNotThrow(() => + httpMppValidateOptions({ + realm: "api", + methods: [ + { + method: "tempo", + recipient: "0x", + currency: "0x", + amount: 0.01, + }, + ], + verify: () => true, + }), + ); +}); + +test("httpMppValidateOptions accepts empty options object", () => { + doesNotThrow(() => httpMppValidateOptions({})); +}); + +test("httpMppValidateOptions rejects unknown top-level keys", () => { + throws(() => httpMppValidateOptions({ unknownKey: "value" })); +}); + +test("httpMppValidateOptions rejects non-positive amount", () => { + throws(() => + httpMppValidateOptions({ + methods: [ + { method: "tempo", recipient: "0x", currency: "0x", amount: 0 }, + ], + }), + ); +}); + +test("httpMppValidateOptions rejects negative amount", () => { + throws(() => + httpMppValidateOptions({ + methods: [ + { method: "tempo", recipient: "0x", currency: "0x", amount: -1 }, + ], + }), + ); +}); + +test("httpMppValidateOptions rejects non-function verify", () => { + throws(() => httpMppValidateOptions({ verify: "not-a-function" })); +}); + +// *** construction errors *** + +test("Should throw at construction when methods is empty array", () => { + throws(() => httpMpp({ methods: [] }), { + message: "options.methods must be a non-empty array", + }); +}); + +test("Should throw at construction when methods is not provided", () => { + throws(() => httpMpp({}), { + message: "options.methods must be a non-empty array", + }); +}); diff --git a/packages/http-mpp/index.tst.ts b/packages/http-mpp/index.tst.ts new file mode 100644 index 000000000..62f9db51f --- /dev/null +++ b/packages/http-mpp/index.tst.ts @@ -0,0 +1,58 @@ +import type middy from "@middy/core"; +import { expect, test } from "tstyche"; +import httpMpp, { type MethodOptions } from "./index.js"; + +test("use with required methods", () => { + const middleware = httpMpp({ + methods: [ + { method: "tempo", recipient: "0x", currency: "0x", amount: 0.01 }, + ], + }); + expect(middleware).type.toBe>(); +}); + +test("use with all options", () => { + const middleware = httpMpp({ + realm: "api", + methods: [ + { method: "tempo", recipient: "0xabc", currency: "0xdef", amount: 0.01 }, + { + method: "lightning", + recipient: "lnbc...", + currency: "BTC", + amount: 0.0001, + }, + ], + verify: async (token: string) => token.length > 0, + }); + expect(middleware).type.toBe>(); +}); + +test("verify receives middy.Request as second argument", () => { + httpMpp({ + methods: [ + { method: "tempo", recipient: "0x", currency: "0x", amount: 0.01 }, + ], + verify: async (token: string, request: middy.Request) => + request.event !== undefined, + }); +}); + +test("verify can be synchronous", () => { + httpMpp({ + methods: [ + { method: "tempo", recipient: "0x", currency: "0x", amount: 0.01 }, + ], + verify: (token: string) => token.length > 0, + }); +}); + +test("MethodOptions interface is exported", () => { + const method: MethodOptions = { + method: "tempo", + recipient: "0x", + currency: "0x", + amount: 0.01, + }; + expect(method).type.toBe(); +}); diff --git a/packages/http-mpp/mppx.d.ts b/packages/http-mpp/mppx.d.ts new file mode 100644 index 000000000..b7f45c364 --- /dev/null +++ b/packages/http-mpp/mppx.d.ts @@ -0,0 +1,13 @@ +// Copyright 2017 - 2026 will Farrell, Luciano Mammino, and Middy contributors. +// SPDX-License-Identifier: MIT + +interface MppxConfig { + methods: unknown[]; + realm?: string; + secretKey?: string; + transport?: unknown; +} + +export declare function httpMppVerify( + mppxConfig: MppxConfig, +): (token: string) => Promise; diff --git a/packages/http-mpp/mppx.js b/packages/http-mpp/mppx.js new file mode 100644 index 000000000..120509b84 --- /dev/null +++ b/packages/http-mpp/mppx.js @@ -0,0 +1,15 @@ +// Copyright 2017 - 2026 will Farrell, Luciano Mammino, and Middy contributors. +// SPDX-License-Identifier: MIT +import { Mppx } from "mppx/server"; + +export const httpMppVerify = (mppxConfig) => { + const mppx = Mppx.create(mppxConfig); + return async (token) => { + try { + await mppx.verifyCredential(token); + return true; + } catch { + return false; + } + }; +}; diff --git a/packages/http-mpp/package.json b/packages/http-mpp/package.json new file mode 100644 index 000000000..a7830aef5 --- /dev/null +++ b/packages/http-mpp/package.json @@ -0,0 +1,89 @@ +{ + "name": "@middy/http-mpp", + "version": "7.3.4", + "description": "Machine Payments Protocol (MPP) HTTP 402 payment gate middleware for the middy framework", + "type": "module", + "engines": { + "node": ">=22" + }, + "engineStrict": true, + "publishConfig": { + "access": "public" + }, + "module": "./index.js", + "sideEffects": false, + "exports": { + ".": { + "import": { + "types": "./index.d.ts", + "default": "./index.js" + } + }, + "./mppx": { + "import": { + "types": "./mppx.d.ts", + "default": "./mppx.js" + } + } + }, + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "mppx.js", + "mppx.d.ts" + ], + "scripts": { + "test": "npm run test:unit && npm run test:fuzz", + "test:unit": "node --test", + "test:fuzz": "node --test index.fuzz.js", + "test:perf": "node --test index.perf.js" + }, + "license": "MIT", + "keywords": [ + "Lambda", + "Middleware", + "Serverless", + "Framework", + "AWS", + "AWS Lambda", + "Middy", + "MPP", + "402", + "payment", + "Machine Payments Protocol" + ], + "author": { + "name": "Middy contributors", + "url": "https://github.com/middyjs/middy/graphs/contributors" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/middyjs/middy.git", + "directory": "packages/http-mpp" + }, + "bugs": { + "url": "https://github.com/middyjs/middy/issues" + }, + "homepage": "https://middy.js.org", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/willfarrell" + }, + "dependencies": { + "@middy/util": "7.3.4" + }, + "peerDependencies": { + "mppx": ">=0.6.0" + }, + "peerDependenciesMeta": { + "mppx": { + "optional": true + } + }, + "devDependencies": { + "@middy/core": "7.3.4", + "@types/aws-lambda": "^8.0.0", + "@types/node": "^22.0.0" + } +} From 7627213cdd75a96a1b45ed0a5d8663d8b7dcc360 Mon Sep 17 00:00:00 2001 From: will Farrell Date: Fri, 1 May 2026 09:35:19 -0600 Subject: [PATCH 8/8] chore: version bump Signed-off-by: will Farrell --- .github/package.json | 2 +- package-lock.json | 414 +++++++++--------- package.json | 2 +- packages/appconfig/package.json | 6 +- packages/cloudformation-response/package.json | 4 +- packages/cloudformation-router/package.json | 4 +- packages/cloudwatch-metrics/package.json | 4 +- packages/core/package.json | 4 +- .../package.json | 6 +- packages/dsql-signer/package.json | 6 +- packages/dynamodb/package.json | 6 +- packages/error-logger/package.json | 4 +- packages/event-normalizer/package.json | 6 +- packages/http-content-encoding/package.json | 6 +- .../http-content-negotiation/package.json | 6 +- packages/http-cors/package.json | 6 +- packages/http-error-handler/package.json | 6 +- packages/http-event-normalizer/package.json | 4 +- packages/http-header-normalizer/package.json | 4 +- packages/http-json-body-parser/package.json | 6 +- .../http-multipart-body-parser/package.json | 6 +- packages/http-partial-response/package.json | 6 +- .../http-response-serializer/package.json | 6 +- packages/http-router/package.json | 6 +- packages/http-security-headers/package.json | 6 +- .../http-urlencode-body-parser/package.json | 6 +- .../http-urlencode-path-parser/package.json | 6 +- packages/input-output-logger/package.json | 6 +- packages/rds-signer/package.json | 6 +- packages/s3-object-response/package.json | 6 +- packages/s3/package.json | 6 +- packages/secrets-manager/package.json | 6 +- packages/service-discovery/package.json | 6 +- .../sqs-partial-batch-failure/package.json | 4 +- packages/ssm/package.json | 6 +- packages/sts/package.json | 6 +- packages/util/package.json | 4 +- packages/validator/package.json | 6 +- packages/warmup/package.json | 4 +- packages/ws-json-body-parser/package.json | 6 +- packages/ws-response/package.json | 6 +- packages/ws-router/package.json | 6 +- websites/middy.js.org/package.json | 2 +- 43 files changed, 317 insertions(+), 317 deletions(-) diff --git a/.github/package.json b/.github/package.json index 41be8df67..82c57c793 100644 --- a/.github/package.json +++ b/.github/package.json @@ -1,6 +1,6 @@ { "name": "@middy/github-workflows", - "version": "7.3.4", + "version": "7.4.0", "private": true, "engines": { "node": ">=24.0" diff --git a/package-lock.json b/package-lock.json index 01f17b0e4..7b9d0eadd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@middy/monorepo", - "version": "7.3.4", + "version": "7.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@middy/monorepo", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "workspaces": [ "packages/*", @@ -31,7 +31,7 @@ }, ".github": { "name": "@middy/github-workflows", - "version": "7.3.4", + "version": "7.4.0", "devDependencies": { "license-check-and-add": "4.0.5", "lockfile-lint": "5.0.0" @@ -275,9 +275,9 @@ } }, "node_modules/@aws-sdk/client-apigatewaymanagementapi": { - "version": "3.1039.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-apigatewaymanagementapi/-/client-apigatewaymanagementapi-3.1039.0.tgz", - "integrity": "sha512-hz0NvJjPkyv26eQxQ6k+civhRdN825O8lbxPVXkGD60DCIyrVcrP7r2OPOFyTAC2Ar1GECCrAfqrKdPYjg1XVw==", + "version": "3.1040.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-apigatewaymanagementapi/-/client-apigatewaymanagementapi-3.1040.0.tgz", + "integrity": "sha512-1CYjlZ9vLlj8ISjPL9An7irI2wBufXzdbn/jH6vTPH6lDYB6/+GvaMWLTTh+/JvpfhDxpB93/t1g5u0KaVHbRg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -326,9 +326,9 @@ } }, "node_modules/@aws-sdk/client-appconfigdata": { - "version": "3.1039.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-appconfigdata/-/client-appconfigdata-3.1039.0.tgz", - "integrity": "sha512-AIk6lqgbRhD6lDPTHZP8p5GjUFrgjaKa4iDf4KCz/gA4aQ6eC+9I5BkhVBKy97PXsuG7THwCkPAyrd0pMKc3Zg==", + "version": "3.1040.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-appconfigdata/-/client-appconfigdata-3.1040.0.tgz", + "integrity": "sha512-UyxDikm5SYclAVEvK7edAgEmyhTxoFsiyHCaQWFLaGoMEq5DNJMTOGSM1SaVMLOj+6i6w7hXm+VQTVxv7hJ0Pw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -378,9 +378,9 @@ } }, "node_modules/@aws-sdk/client-cognito-identity": { - "version": "3.1039.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.1039.0.tgz", - "integrity": "sha512-jue1zZeTxtyCzD7vMxw5gq4XE1q8SUDoePqgdPEPPgjg5FTJsmEhpswgeLV2dP4sHkyeap4f4Pf7BCmwkgJuNQ==", + "version": "3.1040.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-cognito-identity/-/client-cognito-identity-3.1040.0.tgz", + "integrity": "sha512-7nwgQlDc2brGoPbNP7h3EBXs30zX7sb8m+tLaSQdqnNWpXCpq/AY0H/Fd5BQ5UWPlMB9k3sHAkMkCxm6kZXuLQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -429,9 +429,9 @@ } }, "node_modules/@aws-sdk/client-dynamodb": { - "version": "3.1039.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.1039.0.tgz", - "integrity": "sha512-eqyrnSj2l0/y89LHENnGparEyKD33Wh8PQszgWbtb6t8bI5oriPaTHHXCdGUmFjOMkiNfqbsLCoo5HhMXlvbxw==", + "version": "3.1040.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.1040.0.tgz", + "integrity": "sha512-rxWgbYoaUwY11UnajsdJVKYYWp+ZE0AWquI2ImdPTodVcDVdYVhrxmb80my6QH1UFtOUmBCZIndx9fDT/Exp9g==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -483,9 +483,9 @@ } }, "node_modules/@aws-sdk/client-lambda": { - "version": "3.1039.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.1039.0.tgz", - "integrity": "sha512-G6k9MYrrvINhGhHDNheF9NcJlVQ4vbtyLD6pjsgL+vDwugQB84OR95T7bNI9If5DlOxKrLx9s6BGid1YoWQKYA==", + "version": "3.1040.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-lambda/-/client-lambda-3.1040.0.tgz", + "integrity": "sha512-QEpUqgsKVYZXfNmZs0vREVcxDKVvT1FKYH788s3JkPa6zCmT2wBEgCqqIgKS0QdbFMMTPDYIPkIInmIFu3YWjQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -539,9 +539,9 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.1039.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1039.0.tgz", - "integrity": "sha512-PVH9v0pHYBQnBADSR/m88NgcuJcYqPXfpmkcME66vRF75Y4swwbEVVFbTBFuvxu0YcZiLFXu3lw0FDK00vEa3A==", + "version": "3.1040.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.1040.0.tgz", + "integrity": "sha512-Ldfby1xDrlZwNY2NxP9pwdVrf8sqHbGBKP1UkoG/oWcePGlGhjY8iVwy8hRy9f1EQfHVFWIFunwHaPQxhYTnWQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -606,9 +606,9 @@ } }, "node_modules/@aws-sdk/client-secrets-manager": { - "version": "3.1039.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.1039.0.tgz", - "integrity": "sha512-Ew0tUX3tKpzGIMMX+RyRyxR4FHcPTEPMjE9YSemyau3NyNFjlpHQjeTNwFvplLEl/LPCC9A5gk58LcaHtySk7A==", + "version": "3.1040.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.1040.0.tgz", + "integrity": "sha512-qszI/6MUy57rSqA2eSN3jbdjU1RFUDkayw7HbSX02jRmP896mSssWM0BC+mwYXuY7d3s1QntxmMKiNwXEXRdWg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -657,9 +657,9 @@ } }, "node_modules/@aws-sdk/client-servicediscovery": { - "version": "3.1039.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-servicediscovery/-/client-servicediscovery-3.1039.0.tgz", - "integrity": "sha512-9lL3eJAe/C+jhtgHtgThcD2WaMjNEP8srglAfaarLL3szfrrl2+G+N/4KxvfgN2xC6anpGryomKtHnaR8j8nAQ==", + "version": "3.1040.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-servicediscovery/-/client-servicediscovery-3.1040.0.tgz", + "integrity": "sha512-c+OnV3SCbykjlY0H0OmD/RqzPaCEwqFY2k6uf6G5EZ4uLL/BN44GQL9jgGEC3BSk++uYkfaTJiIJbRqjlgWbHw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -708,9 +708,9 @@ } }, "node_modules/@aws-sdk/client-sqs": { - "version": "3.1039.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.1039.0.tgz", - "integrity": "sha512-U8rooijGy5OnSdxEaJ/sIxuGPgHfspxE8K/az22eJJ4qnO2XcVMtkHljvbfQG0yL3wd93XaypNSPI52okdTyCA==", + "version": "3.1040.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sqs/-/client-sqs-3.1040.0.tgz", + "integrity": "sha512-dzmxbJ6swad3IDAR3xTo0ju7HQKm1YMiwxWr5utZ223WagpkxnKhbAUAjPscQRzbPRLMU3ojXTDcrYEZ/IpUiw==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -761,9 +761,9 @@ } }, "node_modules/@aws-sdk/client-ssm": { - "version": "3.1039.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-ssm/-/client-ssm-3.1039.0.tgz", - "integrity": "sha512-UvFJ/9HSIkQWxcLNbq/QaWemhqi7TChLW+ZSWBv8mNmSybTj4ZrMFpzbH2y9gJq38dN1KVR6FyjtOA/+FE6avg==", + "version": "3.1040.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-ssm/-/client-ssm-3.1040.0.tgz", + "integrity": "sha512-Su6vrzo32lx7jL6Dk/fatQpjrGjBCQIDr7pRdlpHOGOSTfg2L+UFg/XRrZRL5blz7dCNdfhZf2uuuMK1n0hrkg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -813,9 +813,9 @@ } }, "node_modules/@aws-sdk/client-sts": { - "version": "3.1039.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.1039.0.tgz", - "integrity": "sha512-FKwfa4agOd6rctrp6D+W/sUKT+c+wI5m23oYR0VyE01oad6ZYff+113JP/X2xbrxIFJtVz7GieQZdVUFqOuMuQ==", + "version": "3.1040.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.1040.0.tgz", + "integrity": "sha512-k5m8ofhqQ2102h6l0iqNFRpuSXdyhk62U9i13kmGmscfqtVX4JeKjg//IK1Mhaz6Owi6WxAl1jQ8vcx81gpzww==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1088,13 +1088,13 @@ } }, "node_modules/@aws-sdk/credential-providers": { - "version": "3.1039.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.1039.0.tgz", - "integrity": "sha512-zJdE1J5p3lxk6kw40hZoI7O3aZDy3exFUWyZjjE4BGppXvfM4PQTDFRcq466JxjUc7x3X//H2oNejsH8+UErIg==", + "version": "3.1040.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-providers/-/credential-providers-3.1040.0.tgz", + "integrity": "sha512-9CUcij1A7f6i4XOp/7MczdQInHvHZ8KKoNHlXHq+UtR3HOtBbpk4/BzmLF/bSdvd4NI8i2XmQlP5fEUCo+zGPA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/client-cognito-identity": "3.1039.0", + "@aws-sdk/client-cognito-identity": "3.1040.0", "@aws-sdk/core": "^3.974.7", "@aws-sdk/credential-provider-cognito-identity": "^3.972.30", "@aws-sdk/credential-provider-env": "^3.972.33", @@ -1447,15 +1447,15 @@ } }, "node_modules/@aws-sdk/rds-signer": { - "version": "3.1039.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/rds-signer/-/rds-signer-3.1039.0.tgz", - "integrity": "sha512-dmIZ5Fs92tsew4pmPjq9TeP6dnLrIasZHu4EfIlRKEzBXqqtV+kVEjfnhZsmRZNdm/kd3gVvO5nFaCg3C4TBLQ==", + "version": "3.1040.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/rds-signer/-/rds-signer-3.1040.0.tgz", + "integrity": "sha512-O5qQzhYKjm+8ARZRAdqTpZmbcaY35cNvjRAcJboBkLNyNgjZ5KLSc4vxgX/WqS7qo0b4qZYnXwdippdaMV01LQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/credential-providers": "3.1039.0", + "@aws-sdk/credential-providers": "3.1040.0", "@aws-sdk/util-format-url": "^3.972.10", "@smithy/config-resolver": "^4.4.17", "@smithy/hash-node": "^4.2.14", @@ -1669,9 +1669,9 @@ } }, "node_modules/@aws/durable-execution-sdk-js": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@aws/durable-execution-sdk-js/-/durable-execution-sdk-js-1.1.1.tgz", - "integrity": "sha512-ubuZu494xiZsIDjjZupGoVJwh8fSfeibZVp/4SjgejdXOetzd9w1V8AmB4bzAwcgsmhHbk3BPdg2cmvTebGBpA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@aws/durable-execution-sdk-js/-/durable-execution-sdk-js-1.1.2.tgz", + "integrity": "sha512-STJU58rInEBfsPyUDtH/XIL8/jaCfpkTkx37EAwee7fFO1tdaXwInCmkOjxtIrImhSdkPJU8bRORKTij/N0I8A==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1923,13 +1923,13 @@ } }, "node_modules/@cloudflare/kv-asset-handler": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.2.tgz", - "integrity": "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.5.0.tgz", + "integrity": "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==", "dev": true, "license": "MIT OR Apache-2.0", "engines": { - "node": ">=18.0.0" + "node": ">=22.0.0" } }, "node_modules/@cloudflare/unenv-preset": { @@ -1949,9 +1949,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20260426.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260426.1.tgz", - "integrity": "sha512-Ch7DqsmYzSQRTY87pZpsGsFVz9VVBnLPnCBOHxKt1HH25a7oMu1w1PbPWqVmE0VerCLsj/TScX7Ob3v6E14TZw==", + "version": "1.20260430.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260430.1.tgz", + "integrity": "sha512-ADohZUHf7NBvPp2PdZig2Opxx+hDkk3ve7jrTne3JRx9kDSB73zc4LzcEeEN8LKkbAcqZmvfRJfpChSlusu0lA==", "cpu": [ "x64" ], @@ -1966,9 +1966,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20260426.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260426.1.tgz", - "integrity": "sha512-0m0U8vaPRH25SpKjbSyRql6gmPe4rCsETRV2WW0qBnuMdKNr5Vh5/Uez80xVrfiCCRMTULGeg63Nqg2vg6CDOA==", + "version": "1.20260430.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260430.1.tgz", + "integrity": "sha512-/DoYC/1wHs+YRZzzqSQg1/EHB4hiv1yV5U8FnmapRRIzVaPtnt+ApeOXeMrIdKidgKOI8TqQzgBU8xbIM7Cl4Q==", "cpu": [ "arm64" ], @@ -1983,9 +1983,9 @@ } }, "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20260426.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260426.1.tgz", - "integrity": "sha512-C8LlC8uSYzg49y51n++75esxZmMp+Uz1OKHHA/4lkv6rjOTbcHQJuEwSLppjybVIXpv7A8MBhbu9iyCTvyv1mw==", + "version": "1.20260430.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260430.1.tgz", + "integrity": "sha512-koJhBWvEVZPKCVFtMLp2iMHlYr+lFCF47wGbnlKdHVlemV0zTxJEyHI8aLlrhPLhBmOmYLp46rXw09/qJkRIhQ==", "cpu": [ "x64" ], @@ -2000,9 +2000,9 @@ } }, "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20260426.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260426.1.tgz", - "integrity": "sha512-ESVp/OIFMAqjQsa8BOP2BQQz5Vpfv6ncN6lNnIuNeOgsISQBdYk+LA60bwQHMud9tvmnSYtONp1zkZ8OQz+x6w==", + "version": "1.20260430.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260430.1.tgz", + "integrity": "sha512-hMdapNAzNQZDXGGkg4Slydc3fRJP5FUZLJVVcZCW/+imhhJro9Z1rv5n/wfR+txKoSWhTYR8eOp8Pyi2bzLzlw==", "cpu": [ "arm64" ], @@ -2017,9 +2017,9 @@ } }, "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20260426.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260426.1.tgz", - "integrity": "sha512-d3Xj/IjINRgNVwH+eKhpUn4xkkcEewbWXbOvBlapiirKWh5zl9m0Epi3qOqmjyRYK6MICqIGXg4qZBEt0lxudw==", + "version": "1.20260430.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260430.1.tgz", + "integrity": "sha512-jS3ffixjb5USOwz4frw4WzCz0HrjVxkgyU3WiYb06N7hBAfN6eOrveAJ4QRef0+suK4V1vQFoB1oKdRBsXe9Dw==", "cpu": [ "x64" ], @@ -2034,9 +2034,9 @@ } }, "node_modules/@cloudflare/workers-types": { - "version": "4.20260430.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20260430.1.tgz", - "integrity": "sha512-Rguf/SdQNm1HG2yZi8m57K4qvVh2fic+Xny4UidY1pCgHagOAq7Cy0WIp1JKQx54T8lgOgOWFzIaCK4iwLpxlg==", + "version": "4.20260501.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20260501.1.tgz", + "integrity": "sha512-B/VX2w3my/sCqxKyWOX7SxUpFC1uD8Gh7I2zbI1d3zA8p7Tx03AFsnuEx8lYLmcd8yONAA93YsAZb1wAaLK83w==", "dev": true, "license": "MIT OR Apache-2.0" }, @@ -6358,9 +6358,9 @@ } }, "node_modules/devalue": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.7.1.tgz", - "integrity": "sha512-MUbZ586EgQqdRnC4yDrlod3BEdyvE4TapGYHMW2CiaW+KkkFmWEFqBUaLltEZCGi0iFXCEjRF0OjF0DV2QHjOA==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.8.0.tgz", + "integrity": "sha512-2zA9pFEsnp7vWBZbXF5JAgAq0fsUIt/1XPbRiAmRV3lp/2C3upzH+sADiyy66aFCihoLEsrQHxNM5w1gIDfsBg==", "license": "MIT" }, "node_modules/diff": { @@ -8166,16 +8166,16 @@ "link": true }, "node_modules/miniflare": { - "version": "4.20260426.0", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260426.0.tgz", - "integrity": "sha512-KM+v76d04qT+NsPfVKVQEgnnuLNE3uzCCl2QKMTJ5OXor5JbBm1vpkQwQ+l7o5ELCrZ74RnyKhJKLiJyUA39Tw==", + "version": "4.20260430.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260430.0.tgz", + "integrity": "sha512-MWvMm3Siho9Yj7lbJZidLs8hbrRvIcOrif2mnsHQZdvoKfedpea+GaN8XJxbpRcq0B2WzNI1BB1ihdnqes3/ZA==", "dev": true, "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "0.8.1", "sharp": "^0.34.5", "undici": "7.24.8", - "workerd": "1.20260426.1", + "workerd": "1.20260430.1", "ws": "8.18.0", "youch": "4.1.0-beta.10" }, @@ -8183,7 +8183,7 @@ "miniflare": "bootstrap.js" }, "engines": { - "node": ">=18.0.0" + "node": ">=22.0.0" } }, "node_modules/miniflare/node_modules/undici": { @@ -8262,9 +8262,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "dev": true, "funding": [ { @@ -8701,9 +8701,9 @@ } }, "node_modules/postcss": { - "version": "8.5.12", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", - "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.13.tgz", + "integrity": "sha512-qif0+jGGZoLWdHey3UFHHWP0H7Gbmsk8T5VEqyYFbWqPr1XqvLGBbk/sl8V5exGmcYJklJOhOQq1pV9IcsiFag==", "dev": true, "funding": [ { @@ -10005,9 +10005,9 @@ "license": "ISC" }, "node_modules/workerd": { - "version": "1.20260426.1", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260426.1.tgz", - "integrity": "sha512-ELvGgN8c9oo+E6EPyecxk1TEf6/eAK4TxxQTW5mQ87C7jbjCzhMbg0P2ije49UBHV0dkBYPJcJvcklUltipl2A==", + "version": "1.20260430.1", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260430.1.tgz", + "integrity": "sha512-KEgIWyiw3Jmn+DCd/L3ePo5fmiiYb/UcwKvDWPf/nLLOiwShDFzDSsegU5NY/JcwgvO/QsLHVi2FYrbkcXNY5Q==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -10018,11 +10018,11 @@ "node": ">=16" }, "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20260426.1", - "@cloudflare/workerd-darwin-arm64": "1.20260426.1", - "@cloudflare/workerd-linux-64": "1.20260426.1", - "@cloudflare/workerd-linux-arm64": "1.20260426.1", - "@cloudflare/workerd-windows-64": "1.20260426.1" + "@cloudflare/workerd-darwin-64": "1.20260430.1", + "@cloudflare/workerd-darwin-arm64": "1.20260430.1", + "@cloudflare/workerd-linux-64": "1.20260430.1", + "@cloudflare/workerd-linux-arm64": "1.20260430.1", + "@cloudflare/workerd-windows-64": "1.20260430.1" } }, "node_modules/worktop": { @@ -10040,33 +10040,33 @@ } }, "node_modules/wrangler": { - "version": "4.86.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.86.0.tgz", - "integrity": "sha512-9aa/gbF/HiUeeUEwyQpW5LDPBEzyt7iaE6xHwm0vk2Ly8A6J+jh03pzchqVnCCWR832mNyA28MD8oAYt0Kfvlw==", + "version": "4.87.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.87.0.tgz", + "integrity": "sha512-lfhfKwLfQlowwgV0xhlYgE9fU3n0I30d4ccGY/rTCEm/n42Mjvlr0Ng3ZPNqlsrsKBcDR531V7dsPkgELvrk/Q==", "dev": true, "license": "MIT OR Apache-2.0", "dependencies": { - "@cloudflare/kv-asset-handler": "0.4.2", + "@cloudflare/kv-asset-handler": "0.5.0", "@cloudflare/unenv-preset": "2.16.1", "blake3-wasm": "2.1.5", "esbuild": "0.27.3", - "miniflare": "4.20260426.0", + "miniflare": "4.20260430.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", - "workerd": "1.20260426.1" + "workerd": "1.20260430.1" }, "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" }, "engines": { - "node": ">=20.3.0" + "node": ">=22.0.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { - "@cloudflare/workers-types": "^4.20260426.1" + "@cloudflare/workers-types": "^4.20260430.1" }, "peerDependenciesMeta": { "@cloudflare/workers-types": { @@ -10726,14 +10726,14 @@ }, "packages/appconfig": { "name": "@middy/appconfig", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws-sdk/client-appconfigdata": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" @@ -10773,10 +10773,10 @@ }, "packages/cloudformation-response": { "name": "@middy/cloudformation-response", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" }, @@ -10807,10 +10807,10 @@ }, "packages/cloudformation-router": { "name": "@middy/cloudformation-router", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" }, @@ -10841,13 +10841,13 @@ }, "packages/cloudwatch-metrics": { "name": "@middy/cloudwatch-metrics", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { "aws-embedded-metrics": "4.2.1" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" }, @@ -10878,10 +10878,10 @@ }, "packages/core": { "name": "@middy/core", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws/durable-execution-sdk-js": "^1.0.0", @@ -10925,13 +10925,13 @@ }, "packages/do-not-wait-for-empty-event-loop": { "name": "@middy/do-not-wait-for-empty-event-loop", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" }, @@ -10962,14 +10962,14 @@ }, "packages/dsql-signer": { "name": "@middy/dsql-signer", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws-sdk/dsql-signer": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" @@ -11009,15 +11009,15 @@ }, "packages/dynamodb": { "name": "@middy/dynamodb", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws-sdk/client-dynamodb": "^3.0.0", "@aws-sdk/util-dynamodb": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" @@ -11061,10 +11061,10 @@ }, "packages/error-logger": { "name": "@middy/error-logger", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" }, @@ -11095,13 +11095,13 @@ }, "packages/event-normalizer": { "name": "@middy/event-normalizer", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@serverless/event-mocks": "^1.1.1", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" @@ -11133,14 +11133,14 @@ }, "packages/http-content-encoding": { "name": "@middy/http-content-encoding", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@datastream/core": "0.4.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" }, @@ -11171,14 +11171,14 @@ }, "packages/http-content-negotiation": { "name": "@middy/http-content-negotiation", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4", + "@middy/util": "7.4.0", "negotiator": "1.0.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" }, @@ -11209,13 +11209,13 @@ }, "packages/http-cors": { "name": "@middy/http-cors", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" }, @@ -11246,13 +11246,13 @@ }, "packages/http-error-handler": { "name": "@middy/http-error-handler", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/http-errors": "^2.0.0", "@types/node": "^22.0.0" }, @@ -11283,10 +11283,10 @@ }, "packages/http-event-normalizer": { "name": "@middy/http-event-normalizer", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" }, @@ -11317,10 +11317,10 @@ }, "packages/http-header-normalizer": { "name": "@middy/http-header-normalizer", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/node": "^22.0.0" }, "engines": { @@ -11350,13 +11350,13 @@ }, "packages/http-json-body-parser": { "name": "@middy/http-json-body-parser", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "type-fest": "^5.0.0" @@ -11388,14 +11388,14 @@ }, "packages/http-multipart-body-parser": { "name": "@middy/http-multipart-body-parser", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { "@fastify/busboy": "3.2.0", - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "type-fest": "^5.0.0" @@ -11427,14 +11427,14 @@ }, "packages/http-partial-response": { "name": "@middy/http-partial-response", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4", + "@middy/util": "7.4.0", "json-mask": "2.0.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" }, @@ -11465,13 +11465,13 @@ }, "packages/http-response-serializer": { "name": "@middy/http-response-serializer", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" }, @@ -11502,13 +11502,13 @@ }, "packages/http-router": { "name": "@middy/http-router", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" }, @@ -11539,13 +11539,13 @@ }, "packages/http-security-headers": { "name": "@middy/http-security-headers", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" }, @@ -11576,13 +11576,13 @@ }, "packages/http-urlencode-body-parser": { "name": "@middy/http-urlencode-body-parser", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "type-fest": "^5.0.0" @@ -11614,13 +11614,13 @@ }, "packages/http-urlencode-path-parser": { "name": "@middy/http-urlencode-path-parser", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "type-fest": "^5.0.0" @@ -11652,14 +11652,14 @@ }, "packages/input-output-logger": { "name": "@middy/input-output-logger", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@datastream/core": "0.4.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" }, @@ -11690,14 +11690,14 @@ }, "packages/rds-signer": { "name": "@middy/rds-signer", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws-sdk/rds-signer": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" @@ -11737,14 +11737,14 @@ }, "packages/s3": { "name": "@middy/s3", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws-sdk/client-s3": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" @@ -11767,14 +11767,14 @@ }, "packages/s3-object-response": { "name": "@middy/s3-object-response", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws-sdk/client-s3": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" @@ -11831,14 +11831,14 @@ }, "packages/secrets-manager": { "name": "@middy/secrets-manager", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws-sdk/client-secrets-manager": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" @@ -11878,14 +11878,14 @@ }, "packages/service-discovery": { "name": "@middy/service-discovery", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws-sdk/client-servicediscovery": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" @@ -11925,11 +11925,11 @@ }, "packages/sqs-partial-batch-failure": { "name": "@middy/sqs-partial-batch-failure", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "devDependencies": { "@aws-sdk/client-sqs": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@serverless/event-mocks": "^1.1.1", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" @@ -11961,14 +11961,14 @@ }, "packages/ssm": { "name": "@middy/ssm", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws-sdk/client-ssm": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" @@ -12008,14 +12008,14 @@ }, "packages/sts": { "name": "@middy/sts", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws-sdk/client-sts": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" @@ -12055,11 +12055,11 @@ }, "packages/util": { "name": "@middy/util", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "devDependencies": { "@aws-sdk/client-ssm": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" @@ -12091,10 +12091,10 @@ }, "packages/validator": { "name": "@middy/validator", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4", + "@middy/util": "7.4.0", "@silverbucket/ajv-formats-draft2019": "1.6.5", "ajv": "8.20.0", "ajv-errors": "3.0.0", @@ -12103,7 +12103,7 @@ "ajv-keywords": "5.1.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/http-errors": "^2.0.0", "@types/node": "^22.0.0", @@ -12137,10 +12137,10 @@ }, "packages/warmup": { "name": "@middy/warmup", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" }, @@ -12171,13 +12171,13 @@ }, "packages/ws-json-body-parser": { "name": "@middy/ws-json-body-parser", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "type-fest": "^5.0.0" @@ -12209,14 +12209,14 @@ }, "packages/ws-response": { "name": "@middy/ws-response", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws-sdk/client-apigatewaymanagementapi": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" @@ -12256,13 +12256,13 @@ }, "packages/ws-router": { "name": "@middy/ws-router", - "version": "7.3.4", + "version": "7.4.0", "license": "MIT", "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" }, @@ -12292,7 +12292,7 @@ "license": "MIT" }, "websites/middy.js.org": { - "version": "7.3.4", + "version": "7.4.0", "dependencies": { "@plausible-analytics/tracker": "0.4.4", "@willfarrell-ds/svelte": "0.0.0-alpha.6", diff --git a/package.json b/package.json index f9a237a58..fb7980d2b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@middy/monorepo", - "version": "7.3.4", + "version": "7.4.0", "description": "🛵 The stylish Node.js middleware engine for AWS Lambda", "private": true, "type": "module", diff --git a/packages/appconfig/package.json b/packages/appconfig/package.json index 9249dd64d..e260de2f0 100644 --- a/packages/appconfig/package.json +++ b/packages/appconfig/package.json @@ -1,6 +1,6 @@ { "name": "@middy/appconfig", - "version": "7.3.4", + "version": "7.4.0", "description": "AppConfig middleware for the middy framework", "type": "module", "engines": { @@ -68,11 +68,11 @@ } }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws-sdk/client-appconfigdata": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" diff --git a/packages/cloudformation-response/package.json b/packages/cloudformation-response/package.json index d7caa724e..2cf17856e 100644 --- a/packages/cloudformation-response/package.json +++ b/packages/cloudformation-response/package.json @@ -1,6 +1,6 @@ { "name": "@middy/cloudformation-response", - "version": "7.3.4", + "version": "7.4.0", "description": "CloudFormation Custom Response event response handling for the middy framework", "type": "module", "engines": { @@ -61,7 +61,7 @@ "url": "https://github.com/sponsors/willfarrell" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" } diff --git a/packages/cloudformation-router/package.json b/packages/cloudformation-router/package.json index 18e44daf6..5462b5909 100644 --- a/packages/cloudformation-router/package.json +++ b/packages/cloudformation-router/package.json @@ -1,6 +1,6 @@ { "name": "@middy/cloudformation-router", - "version": "7.3.4", + "version": "7.4.0", "description": "CloudFormation Custom Response event router for the middy framework", "type": "module", "engines": { @@ -62,7 +62,7 @@ "url": "https://github.com/sponsors/willfarrell" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" } diff --git a/packages/cloudwatch-metrics/package.json b/packages/cloudwatch-metrics/package.json index 239a888f6..8d1a15954 100644 --- a/packages/cloudwatch-metrics/package.json +++ b/packages/cloudwatch-metrics/package.json @@ -1,6 +1,6 @@ { "name": "@middy/cloudwatch-metrics", - "version": "7.3.4", + "version": "7.4.0", "description": "Embedded CloudWatch metrics middleware for the middy framework", "type": "module", "engines": { @@ -68,7 +68,7 @@ "aws-embedded-metrics": "4.2.1" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" } diff --git a/packages/core/package.json b/packages/core/package.json index 9a15f93cd..fbf73f70f 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@middy/core", - "version": "7.3.4", + "version": "7.4.0", "description": "🛵 The stylish Node.js middleware engine for AWS Lambda (core package)", "type": "module", "engines": { @@ -103,7 +103,7 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "peerDependencies": { "@aws/durable-execution-sdk-js": "^1.0.0" diff --git a/packages/do-not-wait-for-empty-event-loop/package.json b/packages/do-not-wait-for-empty-event-loop/package.json index 409c247c8..1222fda19 100644 --- a/packages/do-not-wait-for-empty-event-loop/package.json +++ b/packages/do-not-wait-for-empty-event-loop/package.json @@ -1,6 +1,6 @@ { "name": "@middy/do-not-wait-for-empty-event-loop", - "version": "7.3.4", + "version": "7.4.0", "description": "Middleware for the middy framework that allows to easily disable the wait for empty event loop in a Lambda function", "type": "module", "engines": { @@ -60,10 +60,10 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" } diff --git a/packages/dsql-signer/package.json b/packages/dsql-signer/package.json index f50f08307..7bb5648fc 100644 --- a/packages/dsql-signer/package.json +++ b/packages/dsql-signer/package.json @@ -1,6 +1,6 @@ { "name": "@middy/dsql-signer", - "version": "7.3.4", + "version": "7.4.0", "description": "Aurora DSQL credentials middleware for the middy framework", "type": "module", "engines": { @@ -70,11 +70,11 @@ } }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws-sdk/dsql-signer": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" diff --git a/packages/dynamodb/package.json b/packages/dynamodb/package.json index d5b45880b..e1d64f0db 100644 --- a/packages/dynamodb/package.json +++ b/packages/dynamodb/package.json @@ -1,6 +1,6 @@ { "name": "@middy/dynamodb", - "version": "7.3.4", + "version": "7.4.0", "description": "DynamoDB middleware for the middy framework", "type": "module", "engines": { @@ -72,12 +72,12 @@ } }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws-sdk/client-dynamodb": "^3.0.0", "@aws-sdk/util-dynamodb": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" diff --git a/packages/error-logger/package.json b/packages/error-logger/package.json index 1d023c557..44ddac727 100644 --- a/packages/error-logger/package.json +++ b/packages/error-logger/package.json @@ -1,6 +1,6 @@ { "name": "@middy/error-logger", - "version": "7.3.4", + "version": "7.4.0", "description": "Error logger middleware for the middy framework", "type": "module", "engines": { @@ -61,7 +61,7 @@ "url": "https://github.com/sponsors/willfarrell" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" } diff --git a/packages/event-normalizer/package.json b/packages/event-normalizer/package.json index 88c9495d4..2849d2f39 100644 --- a/packages/event-normalizer/package.json +++ b/packages/event-normalizer/package.json @@ -1,6 +1,6 @@ { "name": "@middy/event-normalizer", - "version": "7.3.4", + "version": "7.4.0", "description": "Parse and normalize AWS events middleware for the middy framework", "type": "module", "engines": { @@ -64,10 +64,10 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@serverless/event-mocks": "^1.1.1", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" diff --git a/packages/http-content-encoding/package.json b/packages/http-content-encoding/package.json index 85fbb1e24..edd390bcb 100644 --- a/packages/http-content-encoding/package.json +++ b/packages/http-content-encoding/package.json @@ -1,6 +1,6 @@ { "name": "@middy/http-content-encoding", - "version": "7.3.4", + "version": "7.4.0", "description": "HTTP content encoding middleware for the middy framework", "type": "module", "engines": { @@ -65,11 +65,11 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@datastream/core": "0.4.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" } diff --git a/packages/http-content-negotiation/package.json b/packages/http-content-negotiation/package.json index 06a2b195c..be693f19e 100644 --- a/packages/http-content-negotiation/package.json +++ b/packages/http-content-negotiation/package.json @@ -1,6 +1,6 @@ { "name": "@middy/http-content-negotiation", - "version": "7.3.4", + "version": "7.4.0", "description": "HTTP content negotiation middleware for the middy framework", "type": "module", "engines": { @@ -62,11 +62,11 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4", + "@middy/util": "7.4.0", "negotiator": "1.0.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" } diff --git a/packages/http-cors/package.json b/packages/http-cors/package.json index fb0741230..c36ca9407 100644 --- a/packages/http-cors/package.json +++ b/packages/http-cors/package.json @@ -1,6 +1,6 @@ { "name": "@middy/http-cors", - "version": "7.3.4", + "version": "7.4.0", "description": "CORS (Cross-Origin Resource Sharing) middleware for the middy framework", "type": "module", "engines": { @@ -61,10 +61,10 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" } diff --git a/packages/http-error-handler/package.json b/packages/http-error-handler/package.json index 4d9bcb298..d67e187b3 100644 --- a/packages/http-error-handler/package.json +++ b/packages/http-error-handler/package.json @@ -1,6 +1,6 @@ { "name": "@middy/http-error-handler", - "version": "7.3.4", + "version": "7.4.0", "description": "HTTP error handler middleware for the middy framework", "type": "module", "engines": { @@ -63,10 +63,10 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/http-errors": "^2.0.0", "@types/node": "^22.0.0" } diff --git a/packages/http-event-normalizer/package.json b/packages/http-event-normalizer/package.json index b4dbeaaca..1a8172e0f 100644 --- a/packages/http-event-normalizer/package.json +++ b/packages/http-event-normalizer/package.json @@ -1,6 +1,6 @@ { "name": "@middy/http-event-normalizer", - "version": "7.3.4", + "version": "7.4.0", "description": "HTTP event normalizer middleware for the middy framework", "type": "module", "engines": { @@ -62,7 +62,7 @@ "url": "https://github.com/sponsors/willfarrell" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" } diff --git a/packages/http-header-normalizer/package.json b/packages/http-header-normalizer/package.json index 9dab9e9e3..c4a7da7d3 100644 --- a/packages/http-header-normalizer/package.json +++ b/packages/http-header-normalizer/package.json @@ -1,6 +1,6 @@ { "name": "@middy/http-header-normalizer", - "version": "7.3.4", + "version": "7.4.0", "description": "HTTP header normalizer middleware for the middy framework", "type": "module", "engines": { @@ -64,7 +64,7 @@ "url": "https://github.com/sponsors/willfarrell" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/node": "^22.0.0" } } diff --git a/packages/http-json-body-parser/package.json b/packages/http-json-body-parser/package.json index 70b367e22..7ff202207 100644 --- a/packages/http-json-body-parser/package.json +++ b/packages/http-json-body-parser/package.json @@ -1,6 +1,6 @@ { "name": "@middy/http-json-body-parser", - "version": "7.3.4", + "version": "7.4.0", "description": "HTTP JSON body parser middleware for the middy framework", "type": "module", "engines": { @@ -64,10 +64,10 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "type-fest": "^5.0.0" diff --git a/packages/http-multipart-body-parser/package.json b/packages/http-multipart-body-parser/package.json index b926ec576..c7937f01f 100644 --- a/packages/http-multipart-body-parser/package.json +++ b/packages/http-multipart-body-parser/package.json @@ -1,6 +1,6 @@ { "name": "@middy/http-multipart-body-parser", - "version": "7.3.4", + "version": "7.4.0", "description": "HTTP multipart body parser middleware for the middy framework", "type": "module", "engines": { @@ -63,10 +63,10 @@ }, "dependencies": { "@fastify/busboy": "3.2.0", - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "type-fest": "^5.0.0" diff --git a/packages/http-partial-response/package.json b/packages/http-partial-response/package.json index 513e1d6f3..ae416df1b 100644 --- a/packages/http-partial-response/package.json +++ b/packages/http-partial-response/package.json @@ -1,6 +1,6 @@ { "name": "@middy/http-partial-response", - "version": "7.3.4", + "version": "7.4.0", "description": "HTTP partial response middleware for the middy framework", "type": "module", "engines": { @@ -63,11 +63,11 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4", + "@middy/util": "7.4.0", "json-mask": "2.0.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" } diff --git a/packages/http-response-serializer/package.json b/packages/http-response-serializer/package.json index a15430fa8..d43a3e639 100644 --- a/packages/http-response-serializer/package.json +++ b/packages/http-response-serializer/package.json @@ -1,6 +1,6 @@ { "name": "@middy/http-response-serializer", - "version": "7.3.4", + "version": "7.4.0", "description": "HTTP response serializer middleware for the middy framework", "type": "module", "engines": { @@ -65,10 +65,10 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" } diff --git a/packages/http-router/package.json b/packages/http-router/package.json index c1f8a270a..a0ef51d0d 100644 --- a/packages/http-router/package.json +++ b/packages/http-router/package.json @@ -1,6 +1,6 @@ { "name": "@middy/http-router", - "version": "7.3.4", + "version": "7.4.0", "description": "HTTP event router for the middy framework", "type": "module", "engines": { @@ -62,10 +62,10 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" } diff --git a/packages/http-security-headers/package.json b/packages/http-security-headers/package.json index 58bd21ac5..a21519d8b 100644 --- a/packages/http-security-headers/package.json +++ b/packages/http-security-headers/package.json @@ -1,6 +1,6 @@ { "name": "@middy/http-security-headers", - "version": "7.3.4", + "version": "7.4.0", "description": "Applies best practice security headers to responses. It's a simplified port of HelmetJS", "type": "module", "engines": { @@ -65,10 +65,10 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" } diff --git a/packages/http-urlencode-body-parser/package.json b/packages/http-urlencode-body-parser/package.json index adf2b494f..b9ad1668a 100644 --- a/packages/http-urlencode-body-parser/package.json +++ b/packages/http-urlencode-body-parser/package.json @@ -1,6 +1,6 @@ { "name": "@middy/http-urlencode-body-parser", - "version": "7.3.4", + "version": "7.4.0", "description": "Urlencode body parser middleware for the middy framework", "type": "module", "engines": { @@ -63,10 +63,10 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "type-fest": "^5.0.0" diff --git a/packages/http-urlencode-path-parser/package.json b/packages/http-urlencode-path-parser/package.json index b29eb71f1..b02a73626 100644 --- a/packages/http-urlencode-path-parser/package.json +++ b/packages/http-urlencode-path-parser/package.json @@ -1,6 +1,6 @@ { "name": "@middy/http-urlencode-path-parser", - "version": "7.3.4", + "version": "7.4.0", "description": "Urlencode path parser middleware for the middy framework", "type": "module", "engines": { @@ -63,10 +63,10 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "type-fest": "^5.0.0" diff --git a/packages/input-output-logger/package.json b/packages/input-output-logger/package.json index 5892b3ae5..bb133ef63 100644 --- a/packages/input-output-logger/package.json +++ b/packages/input-output-logger/package.json @@ -1,6 +1,6 @@ { "name": "@middy/input-output-logger", - "version": "7.3.4", + "version": "7.4.0", "description": "Input and output logger middleware for the middy framework", "type": "module", "engines": { @@ -62,11 +62,11 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@datastream/core": "0.4.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" } diff --git a/packages/rds-signer/package.json b/packages/rds-signer/package.json index 2b288e7de..a6ea8fb0a 100644 --- a/packages/rds-signer/package.json +++ b/packages/rds-signer/package.json @@ -1,6 +1,6 @@ { "name": "@middy/rds-signer", - "version": "7.3.4", + "version": "7.4.0", "description": "RDS (Relational Database Service) credentials middleware for the middy framework", "type": "module", "engines": { @@ -70,11 +70,11 @@ } }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws-sdk/rds-signer": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" diff --git a/packages/s3-object-response/package.json b/packages/s3-object-response/package.json index 9c94451ab..ec2f354eb 100644 --- a/packages/s3-object-response/package.json +++ b/packages/s3-object-response/package.json @@ -1,6 +1,6 @@ { "name": "@middy/s3-object-response", - "version": "7.3.4", + "version": "7.4.0", "description": "S3 object response handling middleware for the middy framework", "type": "module", "engines": { @@ -70,11 +70,11 @@ } }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws-sdk/client-s3": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" diff --git a/packages/s3/package.json b/packages/s3/package.json index c8fde916b..66c776f36 100644 --- a/packages/s3/package.json +++ b/packages/s3/package.json @@ -1,6 +1,6 @@ { "name": "@middy/s3", - "version": "7.3.4", + "version": "7.4.0", "description": "S3 middleware for the middy framework", "type": "module", "engines": { @@ -60,7 +60,7 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "peerDependencies": { "@aws-sdk/client-s3": "^3.0.0" @@ -72,7 +72,7 @@ }, "devDependencies": { "@aws-sdk/client-s3": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" diff --git a/packages/secrets-manager/package.json b/packages/secrets-manager/package.json index 63f77bd38..522defbac 100644 --- a/packages/secrets-manager/package.json +++ b/packages/secrets-manager/package.json @@ -1,6 +1,6 @@ { "name": "@middy/secrets-manager", - "version": "7.3.4", + "version": "7.4.0", "description": "Secrets Manager middleware for the middy framework", "type": "module", "engines": { @@ -60,7 +60,7 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "peerDependencies": { "@aws-sdk/client-secrets-manager": "^3.0.0" @@ -72,7 +72,7 @@ }, "devDependencies": { "@aws-sdk/client-secrets-manager": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" diff --git a/packages/service-discovery/package.json b/packages/service-discovery/package.json index 26d2221c8..acb98f3bc 100644 --- a/packages/service-discovery/package.json +++ b/packages/service-discovery/package.json @@ -1,6 +1,6 @@ { "name": "@middy/service-discovery", - "version": "7.3.4", + "version": "7.4.0", "description": "Service Discovery (Cloud Map) instances middleware for the middy framework", "type": "module", "engines": { @@ -70,11 +70,11 @@ } }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { "@aws-sdk/client-servicediscovery": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" diff --git a/packages/sqs-partial-batch-failure/package.json b/packages/sqs-partial-batch-failure/package.json index e0ec6acec..2d2eb5f7d 100644 --- a/packages/sqs-partial-batch-failure/package.json +++ b/packages/sqs-partial-batch-failure/package.json @@ -1,6 +1,6 @@ { "name": "@middy/sqs-partial-batch-failure", - "version": "7.3.4", + "version": "7.4.0", "description": "SQS partial batch failure middleware for the middy framework", "type": "module", "engines": { @@ -63,7 +63,7 @@ }, "devDependencies": { "@aws-sdk/client-sqs": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@serverless/event-mocks": "^1.1.1", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" diff --git a/packages/ssm/package.json b/packages/ssm/package.json index c86741e64..d8e9ca59a 100644 --- a/packages/ssm/package.json +++ b/packages/ssm/package.json @@ -1,6 +1,6 @@ { "name": "@middy/ssm", - "version": "7.3.4", + "version": "7.4.0", "description": "SSM (EC2 Systems Manager) parameters middleware for the middy framework", "type": "module", "engines": { @@ -62,7 +62,7 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "peerDependencies": { "@aws-sdk/client-ssm": "^3.0.0" @@ -74,7 +74,7 @@ }, "devDependencies": { "@aws-sdk/client-ssm": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" diff --git a/packages/sts/package.json b/packages/sts/package.json index e21c2078c..59dae742e 100644 --- a/packages/sts/package.json +++ b/packages/sts/package.json @@ -1,6 +1,6 @@ { "name": "@middy/sts", - "version": "7.3.4", + "version": "7.4.0", "description": "STS (Security Token Service) credentials middleware for the middy framework", "type": "module", "engines": { @@ -62,7 +62,7 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "peerDependencies": { "@aws-sdk/client-sts": "^3.0.0" @@ -74,7 +74,7 @@ }, "devDependencies": { "@aws-sdk/client-sts": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" diff --git a/packages/util/package.json b/packages/util/package.json index e1bca6712..9ab892a89 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -1,6 +1,6 @@ { "name": "@middy/util", - "version": "7.3.4", + "version": "7.4.0", "description": "🛵 The stylish Node.js middleware engine for AWS Lambda (util package)", "type": "module", "engines": { @@ -57,7 +57,7 @@ }, "devDependencies": { "@aws-sdk/client-ssm": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" diff --git a/packages/validator/package.json b/packages/validator/package.json index 66fdee114..d3b43a873 100644 --- a/packages/validator/package.json +++ b/packages/validator/package.json @@ -1,6 +1,6 @@ { "name": "@middy/validator", - "version": "7.3.4", + "version": "7.4.0", "description": "Validator middleware for the middy framework", "type": "module", "engines": { @@ -69,7 +69,7 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4", + "@middy/util": "7.4.0", "ajv": "8.20.0", "ajv-errors": "3.0.0", "ajv-formats": "3.0.1", @@ -78,7 +78,7 @@ "ajv-keywords": "5.1.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/http-errors": "^2.0.0", "@types/node": "^22.0.0", diff --git a/packages/warmup/package.json b/packages/warmup/package.json index 7518bdecb..1e024dfa0 100644 --- a/packages/warmup/package.json +++ b/packages/warmup/package.json @@ -1,6 +1,6 @@ { "name": "@middy/warmup", - "version": "7.3.4", + "version": "7.4.0", "description": "Warmup (cold start mitigation) middleware for the middy framework", "type": "module", "engines": { @@ -62,7 +62,7 @@ "url": "https://github.com/sponsors/willfarrell" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" } diff --git a/packages/ws-json-body-parser/package.json b/packages/ws-json-body-parser/package.json index 9d00dedd3..0bdcb154d 100644 --- a/packages/ws-json-body-parser/package.json +++ b/packages/ws-json-body-parser/package.json @@ -1,6 +1,6 @@ { "name": "@middy/ws-json-body-parser", - "version": "7.3.4", + "version": "7.4.0", "description": "WebSocket JSON body parser middleware for the middy framework", "type": "module", "engines": { @@ -64,10 +64,10 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "type-fest": "^5.0.0" diff --git a/packages/ws-response/package.json b/packages/ws-response/package.json index 8d1367139..0fad8b7e2 100644 --- a/packages/ws-response/package.json +++ b/packages/ws-response/package.json @@ -1,6 +1,6 @@ { "name": "@middy/ws-response", - "version": "7.3.4", + "version": "7.4.0", "description": "WebSocket response handling middleware for the middy framework", "type": "module", "engines": { @@ -62,7 +62,7 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "peerDependencies": { "@aws-sdk/client-apigatewaymanagementapi": "^3.0.0" @@ -74,7 +74,7 @@ }, "devDependencies": { "@aws-sdk/client-apigatewaymanagementapi": "^3.0.0", - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0", "aws-xray-sdk": "^3.3.3" diff --git a/packages/ws-router/package.json b/packages/ws-router/package.json index aed714c5b..8dee4e917 100644 --- a/packages/ws-router/package.json +++ b/packages/ws-router/package.json @@ -1,6 +1,6 @@ { "name": "@middy/ws-router", - "version": "7.3.4", + "version": "7.4.0", "description": "WebSocket event router for the middy framework", "type": "module", "engines": { @@ -62,10 +62,10 @@ "url": "https://github.com/sponsors/willfarrell" }, "dependencies": { - "@middy/util": "7.3.4" + "@middy/util": "7.4.0" }, "devDependencies": { - "@middy/core": "7.3.4", + "@middy/core": "7.4.0", "@types/aws-lambda": "^8.0.0", "@types/node": "^22.0.0" } diff --git a/websites/middy.js.org/package.json b/websites/middy.js.org/package.json index c172c56be..dab5f11a5 100644 --- a/websites/middy.js.org/package.json +++ b/websites/middy.js.org/package.json @@ -2,7 +2,7 @@ "name": "middy.js.org", "description": "SvelteKit SSR", "private": true, - "version": "7.3.4", + "version": "7.4.0", "type": "module", "scripts": { "start": "vite dev",