From 8d55ac5a3eab0bc322de612f461692ae15d52470 Mon Sep 17 00:00:00 2001 From: "Ben Scholzen (DASPRiD)" Date: Mon, 4 Mar 2024 15:11:24 +0100 Subject: [PATCH] feat: add methodNotAllowedHandler to remove downstream boilerplate --- examples/complete/node_modules/.bin/tsx | 4 +- .../complete/node_modules/@mikro-orm/core | 2 +- .../complete/node_modules/@mikro-orm/sqlite | 2 +- examples/complete/node_modules/@types/koa | 2 +- .../complete/node_modules/mikro-orm-js-joda | 2 +- examples/complete/node_modules/tsx | 2 +- examples/complete/package.json | 8 +- examples/complete/src/index.ts | 36 +----- package.json | 2 +- pnpm-lock.yaml | 122 ++++++++++-------- src/index.ts | 1 + src/router.ts | 34 +++++ 12 files changed, 121 insertions(+), 96 deletions(-) create mode 100644 src/router.ts diff --git a/examples/complete/node_modules/.bin/tsx b/examples/complete/node_modules/.bin/tsx index de15cc5..36d8294 100755 --- a/examples/complete/node_modules/.bin/tsx +++ b/examples/complete/node_modules/.bin/tsx @@ -6,9 +6,9 @@ case `uname` in esac if [ -z "$NODE_PATH" ]; then - export NODE_PATH="/home/dasprid/dev/koa-jsonapi-zod/node_modules/.pnpm/tsx@4.7.0/node_modules/tsx/dist/node_modules:/home/dasprid/dev/koa-jsonapi-zod/node_modules/.pnpm/tsx@4.7.0/node_modules/tsx/node_modules:/home/dasprid/dev/koa-jsonapi-zod/node_modules/.pnpm/tsx@4.7.0/node_modules:/home/dasprid/dev/koa-jsonapi-zod/node_modules/.pnpm/node_modules" + export NODE_PATH="/home/dasprid/dev/koa-jsonapi-zod/node_modules/.pnpm/tsx@4.7.1/node_modules/tsx/dist/node_modules:/home/dasprid/dev/koa-jsonapi-zod/node_modules/.pnpm/tsx@4.7.1/node_modules/tsx/node_modules:/home/dasprid/dev/koa-jsonapi-zod/node_modules/.pnpm/tsx@4.7.1/node_modules:/home/dasprid/dev/koa-jsonapi-zod/node_modules/.pnpm/node_modules" else - export NODE_PATH="/home/dasprid/dev/koa-jsonapi-zod/node_modules/.pnpm/tsx@4.7.0/node_modules/tsx/dist/node_modules:/home/dasprid/dev/koa-jsonapi-zod/node_modules/.pnpm/tsx@4.7.0/node_modules/tsx/node_modules:/home/dasprid/dev/koa-jsonapi-zod/node_modules/.pnpm/tsx@4.7.0/node_modules:/home/dasprid/dev/koa-jsonapi-zod/node_modules/.pnpm/node_modules:$NODE_PATH" + export NODE_PATH="/home/dasprid/dev/koa-jsonapi-zod/node_modules/.pnpm/tsx@4.7.1/node_modules/tsx/dist/node_modules:/home/dasprid/dev/koa-jsonapi-zod/node_modules/.pnpm/tsx@4.7.1/node_modules/tsx/node_modules:/home/dasprid/dev/koa-jsonapi-zod/node_modules/.pnpm/tsx@4.7.1/node_modules:/home/dasprid/dev/koa-jsonapi-zod/node_modules/.pnpm/node_modules:$NODE_PATH" fi if [ -x "$basedir/node" ]; then exec "$basedir/node" "$basedir/../tsx/dist/cli.mjs" "$@" diff --git a/examples/complete/node_modules/@mikro-orm/core b/examples/complete/node_modules/@mikro-orm/core index 0cb6569..fc5d3c1 120000 --- a/examples/complete/node_modules/@mikro-orm/core +++ b/examples/complete/node_modules/@mikro-orm/core @@ -1 +1 @@ -../../../../node_modules/.pnpm/@mikro-orm+core@6.1.0/node_modules/@mikro-orm/core \ No newline at end of file +../../../../node_modules/.pnpm/@mikro-orm+core@6.1.7/node_modules/@mikro-orm/core \ No newline at end of file diff --git a/examples/complete/node_modules/@mikro-orm/sqlite b/examples/complete/node_modules/@mikro-orm/sqlite index 8fec939..ba18e89 120000 --- a/examples/complete/node_modules/@mikro-orm/sqlite +++ b/examples/complete/node_modules/@mikro-orm/sqlite @@ -1 +1 @@ -../../../../node_modules/.pnpm/@mikro-orm+sqlite@6.1.0_@mikro-orm+core@6.1.0/node_modules/@mikro-orm/sqlite \ No newline at end of file +../../../../node_modules/.pnpm/@mikro-orm+sqlite@6.1.7_@mikro-orm+core@6.1.7/node_modules/@mikro-orm/sqlite \ No newline at end of file diff --git a/examples/complete/node_modules/@types/koa b/examples/complete/node_modules/@types/koa index 6652cb5..c80644e 120000 --- a/examples/complete/node_modules/@types/koa +++ b/examples/complete/node_modules/@types/koa @@ -1 +1 @@ -../../../../node_modules/.pnpm/@types+koa@2.14.0/node_modules/@types/koa \ No newline at end of file +../../../../node_modules/.pnpm/@types+koa@2.15.0/node_modules/@types/koa \ No newline at end of file diff --git a/examples/complete/node_modules/mikro-orm-js-joda b/examples/complete/node_modules/mikro-orm-js-joda index 06894e5..c934aca 120000 --- a/examples/complete/node_modules/mikro-orm-js-joda +++ b/examples/complete/node_modules/mikro-orm-js-joda @@ -1 +1 @@ -../../../node_modules/.pnpm/mikro-orm-js-joda@1.0.1_@js-joda+core@5.6.1_@mikro-orm+core@6.1.0/node_modules/mikro-orm-js-joda \ No newline at end of file +../../../node_modules/.pnpm/mikro-orm-js-joda@1.0.1_@js-joda+core@5.6.1_@mikro-orm+core@6.1.7/node_modules/mikro-orm-js-joda \ No newline at end of file diff --git a/examples/complete/node_modules/tsx b/examples/complete/node_modules/tsx index f75ce99..f6e4463 120000 --- a/examples/complete/node_modules/tsx +++ b/examples/complete/node_modules/tsx @@ -1 +1 @@ -../../../node_modules/.pnpm/tsx@4.7.0/node_modules/tsx \ No newline at end of file +../../../node_modules/.pnpm/tsx@4.7.1/node_modules/tsx \ No newline at end of file diff --git a/examples/complete/package.json b/examples/complete/package.json index 852a007..a93e18e 100644 --- a/examples/complete/package.json +++ b/examples/complete/package.json @@ -3,8 +3,8 @@ "type": "module", "dependencies": { "@js-joda/core": "^5.6.1", - "@mikro-orm/core": "^6.1.0", - "@mikro-orm/sqlite": "^6.1.0", + "@mikro-orm/core": "^6.1.7", + "@mikro-orm/sqlite": "^6.1.7", "flat": "^6.0.1", "http-errors": "^2.0.0", "koa": "^2.15.0", @@ -16,9 +16,9 @@ }, "devDependencies": { "@types/http-errors": "^2.0.4", - "@types/koa": "^2.14.0", + "@types/koa": "^2.15.0", "@types/koa-bodyparser": "^4.3.12", - "tsx": "^4.7.0", + "tsx": "^4.7.1", "typescript": "^5.3.3" }, "scripts": { diff --git a/examples/complete/src/index.ts b/examples/complete/src/index.ts index 1aa5ea6..6cbd037 100644 --- a/examples/complete/src/index.ts +++ b/examples/complete/src/index.ts @@ -1,9 +1,9 @@ import Koa from "koa"; import bodyParser from "koa-bodyparser"; import { - JsonApiErrorBody, jsonApiErrorMiddleware, jsonApiRequestMiddleware, + methodNotAllowedHandler, } from "koa-jsonapi-zod"; import Router from "koa-tree-router"; import { registerRoutes } from "./route/index.js"; @@ -11,12 +11,7 @@ import { requestContextMiddleware } from "./util/mikro-orm.js"; const app = new Koa(); -app.use( - jsonApiRequestMiddleware({ - excludedPaths: ["/health"], - }), -); - +app.use(jsonApiRequestMiddleware()); app.use( jsonApiErrorMiddleware({ logError: (error, exposed) => { @@ -27,10 +22,8 @@ app.use( }, }), ); - app.use(bodyParser()); -// This is a non-resource endpoint, hence it was excluded from the `jsonApiMiddleware` app.use(async (context, next) => { if (context.url === "/health") { context.body = { status: "alive" }; @@ -40,30 +33,9 @@ app.use(async (context, next) => { return next(); }); -const router = new Router({ - onMethodNotAllowed: (context) => { - if (context.response.headers.allow === "") { - context.remove("allow"); - context.status = 404; - context.body = new JsonApiErrorBody({ - status: "404", - code: "not_found", - title: "Resource not found", - }); - return; - } - - context.status = 405; - context.body = new JsonApiErrorBody({ - status: "405", - code: "method_not_allowed", - title: "Method not allowed", - detail: `Allowed methods: ${context.response.headers.allow}`, - }); - }, -}); - +const router = new Router({ onMethodNotAllowed: methodNotAllowedHandler }); registerRoutes(router); + app.use(requestContextMiddleware); app.use(router.routes()); diff --git a/package.json b/package.json index d3b7069..02f26a8 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@tsconfig/node20": "^20.1.2", "@types/content-type": "^1.1.8", "@types/http-errors": "^2.0.4", - "@types/koa": "^2.14.0", + "@types/koa": "^2.15.0", "@types/node": "^20.11.17", "@types/qs": "^6.9.11", "@vitest/coverage-v8": "^1.2.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 99349fa..a6c6496 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,8 +40,8 @@ importers: specifier: ^2.0.4 version: 2.0.4 '@types/koa': - specifier: ^2.14.0 - version: 2.14.0 + specifier: ^2.15.0 + version: 2.15.0 '@types/node': specifier: ^20.11.17 version: 20.11.17 @@ -76,11 +76,11 @@ importers: specifier: ^5.6.1 version: 5.6.1 '@mikro-orm/core': - specifier: ^6.1.0 - version: 6.1.0 + specifier: ^6.1.7 + version: 6.1.7 '@mikro-orm/sqlite': - specifier: ^6.1.0 - version: 6.1.0(@mikro-orm/core@6.1.0) + specifier: ^6.1.7 + version: 6.1.7(@mikro-orm/core@6.1.7) flat: specifier: ^6.0.1 version: 6.0.1 @@ -101,7 +101,7 @@ importers: version: 0.12.1 mikro-orm-js-joda: specifier: ^1.0.1 - version: 1.0.1(@js-joda/core@5.6.1)(@mikro-orm/core@6.1.0) + version: 1.0.1(@js-joda/core@5.6.1)(@mikro-orm/core@6.1.7) zod: specifier: ^3.22.4 version: 3.22.4 @@ -110,14 +110,14 @@ importers: specifier: ^2.0.4 version: 2.0.4 '@types/koa': - specifier: ^2.14.0 - version: 2.14.0 + specifier: ^2.15.0 + version: 2.15.0 '@types/koa-bodyparser': specifier: ^4.3.12 version: 4.3.12 tsx: - specifier: ^4.7.0 - version: 4.7.0 + specifier: ^4.7.1 + version: 4.7.1 typescript: specifier: ^5.3.3 version: 5.3.3 @@ -720,26 +720,26 @@ packages: resolution: {integrity: sha512-Xla/d7ZMMR6+zRd6lTio0wRZECfcfFJP7GGe9A9L4tDOlD5CX4YcZ4YZle9w58bBYzssojVapI84RraKWDQZRg==} dev: false - /@mikro-orm/core@6.1.0: - resolution: {integrity: sha512-jP0UzzpyWuE/jxXMesix16SLCsg8nBbogSuKKx3HQOnjd1xafPo4cCjaNFLlmSQbllbEbgTgsvy8Zh354A0X2g==} + /@mikro-orm/core@6.1.7: + resolution: {integrity: sha512-FKHZpeXrYFLzFQ3fuFlDmehk0Q9bpBQVy+0mOIYi1tdBPeCuBqipuxsU9FdeJ2K6OwIZp1Xh+8quGU0s/HdSyg==} engines: {node: '>= 18.12.0'} dependencies: dataloader: 2.2.2 - dotenv: 16.4.1 + dotenv: 16.4.4 esprima: 4.0.1 fs-extra: 11.2.0 globby: 11.1.0 - mikro-orm: 6.1.0 + mikro-orm: 6.1.7 reflect-metadata: 0.2.1 dev: false - /@mikro-orm/knex@6.1.0(@mikro-orm/core@6.1.0)(sqlite3@5.1.7): - resolution: {integrity: sha512-GEY/ywUxfo/te+1STi8yVXRNUqqS76QC0doDwrh2JHC73iq5tuuq7Nfw7VdLU6QGbarzUIhJOQHclfwTdZuHlQ==} + /@mikro-orm/knex@6.1.7(@mikro-orm/core@6.1.7)(sqlite3@5.1.7): + resolution: {integrity: sha512-GkAHV0oejKf0Vw/n+URLtd5rWFpTIpONpMM3jCBF3pguzVCfsJFkw99J9LZRbm/FCxZ7P3BWme4RnHLhe99GnQ==} engines: {node: '>= 18.12.0'} peerDependencies: '@mikro-orm/core': ^6.0.0 dependencies: - '@mikro-orm/core': 6.1.0 + '@mikro-orm/core': 6.1.7 fs-extra: 11.2.0 knex: 3.1.0(sqlite3@5.1.7) sqlstring: 2.3.3 @@ -754,14 +754,14 @@ packages: - tedious dev: false - /@mikro-orm/sqlite@6.1.0(@mikro-orm/core@6.1.0): - resolution: {integrity: sha512-JGc8/Xae6o6LZswKQuRvk5/zMS50l53a0iHG4TMX/QCXYYnuuvEfhcPfRObHVzyBCjBGPXbt9t5ZDUmRYbe7pw==} + /@mikro-orm/sqlite@6.1.7(@mikro-orm/core@6.1.7): + resolution: {integrity: sha512-EiLLvyjf3kjJaceHvONZllPmWVthsZyZhQvmBBYh3SE+KPV+4qswvYGw0a9lXfChAv7xCSPTaQyqfOJxJ6fZKg==} engines: {node: '>= 18.12.0'} peerDependencies: '@mikro-orm/core': ^6.0.0 dependencies: - '@mikro-orm/core': 6.1.0 - '@mikro-orm/knex': 6.1.0(@mikro-orm/core@6.1.0)(sqlite3@5.1.7) + '@mikro-orm/core': 6.1.7 + '@mikro-orm/knex': 6.1.7(@mikro-orm/core@6.1.7)(sqlite3@5.1.7) fs-extra: 11.2.0 sqlite3: 5.1.7 sqlstring-sqlite: 0.1.1 @@ -1010,16 +1010,16 @@ packages: /@types/koa-bodyparser@4.3.12: resolution: {integrity: sha512-hKMmRMVP889gPIdLZmmtou/BijaU1tHPyMNmcK7FAHAdATnRcGQQy78EqTTxLH1D4FTsrxIzklAQCso9oGoebQ==} dependencies: - '@types/koa': 2.14.0 + '@types/koa': 2.15.0 dev: true /@types/koa-compose@3.2.8: resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==} dependencies: - '@types/koa': 2.14.0 + '@types/koa': 2.15.0 - /@types/koa@2.14.0: - resolution: {integrity: sha512-DTDUyznHGNHAl+wd1n0z1jxNajduyTh8R53xoewuerdBzGo6Ogj6F2299BFtrexJw4NtgjsI5SMPCmV9gZwGXA==} + /@types/koa@2.15.0: + resolution: {integrity: sha512-7QFsywoE5URbuVnG3loe03QXuGajrnotr3gQkXcEBShORai23MePfFYdhz90FEtBBpkyIYQbVD+evKtloCgX3g==} dependencies: '@types/accepts': 1.3.7 '@types/content-disposition': 0.5.8 @@ -1028,7 +1028,7 @@ packages: '@types/http-errors': 2.0.4 '@types/keygrip': 1.0.6 '@types/koa-compose': 3.2.8 - '@types/node': 20.11.17 + '@types/node': 20.11.24 /@types/mime@1.3.5: resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} @@ -1045,6 +1045,11 @@ packages: dependencies: undici-types: 5.26.5 + /@types/node@20.11.24: + resolution: {integrity: sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==} + dependencies: + undici-types: 5.26.5 + /@types/normalize-package-data@2.4.4: resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} dev: true @@ -1747,8 +1752,8 @@ packages: is-obj: 2.0.0 dev: true - /dotenv@16.4.1: - resolution: {integrity: sha512-CjA3y+Dr3FyFDOAMnxZEGtnW9KBR2M0JvvUtXNW+dYJL5ROWxP9DUHCwgFqpMk0OXCc0ljhaNTr2w/kutYIcHQ==} + /dotenv@16.4.4: + resolution: {integrity: sha512-XvPXc8XAQThSjAbY6cQ/9PcBXmFoWuw1sQ3b8HqUCR6ziGXjkTi//kB9SWa2UwqlgdAIuRqAa/9hVljzPehbYg==} engines: {node: '>=12'} dev: false @@ -2363,9 +2368,13 @@ packages: engines: {node: '>= 0.10'} dev: false - /ip@2.0.0: - resolution: {integrity: sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==} + /ip-address@9.0.5: + resolution: {integrity: sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==} + engines: {node: '>= 12'} requiresBuild: true + dependencies: + jsbn: 1.1.0 + sprintf-js: 1.1.3 dev: false optional: true @@ -2502,6 +2511,12 @@ packages: argparse: 2.0.1 dev: true + /jsbn@1.1.0: + resolution: {integrity: sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==} + requiresBuild: true + dev: false + optional: true + /json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} dev: true @@ -2611,7 +2626,7 @@ packages: resolution: {integrity: sha512-U/jJoV+rDFYtbaU/X6r2hcNKT7+DZs8HeXONWA7/OSIMk6/cYhoW5P9MPrjg7vHWRrmZOAiFkPoW7vtxvwLWpw==} engines: {node: '>=12.0'} dependencies: - '@types/koa': 2.14.0 + '@types/koa': 2.15.0 koa-compose: 4.1.0 dev: false @@ -2915,18 +2930,18 @@ packages: picomatch: 2.3.1 dev: false - /mikro-orm-js-joda@1.0.1(@js-joda/core@5.6.1)(@mikro-orm/core@6.1.0): + /mikro-orm-js-joda@1.0.1(@js-joda/core@5.6.1)(@mikro-orm/core@6.1.7): resolution: {integrity: sha512-JzppOGakja4Z3PWMiBqolJ+ekYWFE96Sw1tScH00nUmc8SKPCDWjosT4aTAOf1LDI13AgcJLfT2GprJH6MpK3g==} peerDependencies: '@js-joda/core': ^5.6.1 '@mikro-orm/core': ^6.0.5 dependencies: '@js-joda/core': 5.6.1 - '@mikro-orm/core': 6.1.0 + '@mikro-orm/core': 6.1.7 dev: false - /mikro-orm@6.1.0: - resolution: {integrity: sha512-NLdDdAE6rAj3886am2GSz7E7ntSXVTNDLGdU349q4PfkRFZniZBmc16bWW0JNBhZYZYkBVqfdm2Y2h39iqmI4g==} + /mikro-orm@6.1.7: + resolution: {integrity: sha512-iql7OTmfY1eTa6UVeZv2A8VPqRlyU3b6IAlgdrCcjpZLUR5iDo1bZkuDlGMb2CRdjxY1nPjYYbVidBHxOJ4kAw==} engines: {node: '>= 18.12.0'} dev: false @@ -3099,8 +3114,8 @@ packages: requiresBuild: true dev: false - /node-abi@3.54.0: - resolution: {integrity: sha512-p7eGEiQil0YUV3ItH4/tBb781L5impVmmx2E9FRKF7d18XXzp4PGT2tdYMFY6wQqgxD0IwNZOiSJ0/K0fSi/OA==} + /node-abi@3.56.0: + resolution: {integrity: sha512-fZjdhDOeRcaS+rcpve7XuwHBmktS1nS1gzgghwKUQQ8nTy2FdSDr6ZT8k6YhvlJeHmmQMYiT/IH9hfco5zeW2Q==} engines: {node: '>=10'} dependencies: semver: 7.6.0 @@ -3366,8 +3381,8 @@ packages: source-map-js: 1.0.2 dev: true - /prebuild-install@7.1.1: - resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} + /prebuild-install@7.1.2: + resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==} engines: {node: '>=10'} hasBin: true dependencies: @@ -3377,7 +3392,7 @@ packages: minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 1.0.2 - node-abi: 3.54.0 + node-abi: 3.56.0 pump: 3.0.0 rc: 1.2.8 simple-get: 4.0.1 @@ -3716,18 +3731,18 @@ packages: dependencies: agent-base: 6.0.2 debug: 4.3.4 - socks: 2.7.1 + socks: 2.8.1 transitivePeerDependencies: - supports-color dev: false optional: true - /socks@2.7.1: - resolution: {integrity: sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ==} - engines: {node: '>= 10.13.0', npm: '>= 3.0.0'} + /socks@2.8.1: + resolution: {integrity: sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==} + engines: {node: '>= 10.0.0', npm: '>= 3.0.0'} requiresBuild: true dependencies: - ip: 2.0.0 + ip-address: 9.0.5 smart-buffer: 4.2.0 dev: false optional: true @@ -3775,16 +3790,19 @@ packages: engines: {node: '>= 10.x'} dev: true + /sprintf-js@1.1.3: + resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} + requiresBuild: true + dev: false + optional: true + /sqlite3@5.1.7: resolution: {integrity: sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==} requiresBuild: true - peerDependenciesMeta: - node-gyp: - optional: true dependencies: bindings: 1.5.0 node-addon-api: 7.1.0 - prebuild-install: 7.1.1 + prebuild-install: 7.1.2 tar: 6.2.0 optionalDependencies: node-gyp: 8.4.1 @@ -4021,8 +4039,8 @@ packages: engines: {node: '>=0.6.x'} dev: false - /tsx@4.7.0: - resolution: {integrity: sha512-I+t79RYPlEYlHn9a+KzwrvEwhJg35h/1zHsLC2JXvhC2mdynMv6Zxzvhv5EMV6VF5qJlLlkSnMVvdZV3PSIGcg==} + /tsx@4.7.1: + resolution: {integrity: sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==} engines: {node: '>=18.0.0'} hasBin: true dependencies: diff --git a/src/index.ts b/src/index.ts index 5078e71..9ae1c75 100644 --- a/src/index.ts +++ b/src/index.ts @@ -17,6 +17,7 @@ export { type Sort, ZodValidationError, } from "./request.js"; +export { methodNotAllowedHandler } from "./router.js"; export { type EntityRelationships, type EntityRelationship, diff --git a/src/router.ts b/src/router.ts new file mode 100644 index 0000000..9a35645 --- /dev/null +++ b/src/router.ts @@ -0,0 +1,34 @@ +import type { OutgoingHttpHeaders } from "http"; +import { JsonApiErrorBody } from "./body.js"; + +// We need to define the context manually here, otherwise we'll get a TypeScript clash with whatever +// is defined in the downstream application. +type Context = { + response: { + headers: OutgoingHttpHeaders; + }; + remove: (field: string) => void; + status: number; + body: unknown; +}; + +export const methodNotAllowedHandler = (context: TContext) => { + if (context.response.headers.allow === "") { + context.remove("allow"); + context.status = 404; + context.body = new JsonApiErrorBody({ + status: "404", + code: "not_found", + title: "Resource not found", + }); + return; + } + + context.status = 405; + context.body = new JsonApiErrorBody({ + status: "405", + code: "method_not_allowed", + title: "Method not allowed", + detail: `Allowed methods: ${context.response.headers.allow}`, + }); +};