diff --git a/db/errors/postgres-error-constants-parsers.ts b/db/errors/postgres-error-constants-parsers.ts index 2a117b5..cd23aae 100644 --- a/db/errors/postgres-error-constants-parsers.ts +++ b/db/errors/postgres-error-constants-parsers.ts @@ -1,4 +1,5 @@ import { + CLIENT_FAULT_POSTGRES_ERROR_CLASSES, POSTGRES_ERROR_CLASS_TO_TITLE_MAP, POSTGRES_ERROR_CODE_TO_MESSAGE_MAP, POSTGRES_ERROR_SEVERITIES, @@ -12,6 +13,9 @@ export const postgresErrorCodeParser = zodEnumFromObjKeys( POSTGRES_ERROR_CODE_TO_MESSAGE_MAP, ); +export const clientFaultPostgresErrorClassParser = zodEnumFromObjKeys( + POSTGRES_ERROR_CLASS_TO_TITLE_MAP, +).extract(CLIENT_FAULT_POSTGRES_ERROR_CLASSES); export const publicPostgresErrorClassParser = zodEnumFromObjKeys( POSTGRES_ERROR_CLASS_TO_TITLE_MAP, ).extract(PUBLIC_POSTGRES_ERROR_CLASSES); diff --git a/db/errors/postgres-error-constants.ts b/db/errors/postgres-error-constants.ts index 5c66a28..ddbdf94 100644 --- a/db/errors/postgres-error-constants.ts +++ b/db/errors/postgres-error-constants.ts @@ -1,4 +1,5 @@ -export const PUBLIC_POSTGRES_ERROR_CLASSES = ["22", "23"] as const; +export const PUBLIC_POSTGRES_ERROR_CLASSES = ["02", "22", "23"] as const; +export const CLIENT_FAULT_POSTGRES_ERROR_CLASSES = ["02", "22", "23"] as const; // from https://www.postgresql.org/docs/9.3/protocol-error-fields.html export const POSTGRES_ERROR_SEVERITIES = ["ERROR", "FATAL", "PANIC"] as const; diff --git a/db/errors/postgres-error.ts b/db/errors/postgres-error.ts index 13cb8e8..242821e 100644 --- a/db/errors/postgres-error.ts +++ b/db/errors/postgres-error.ts @@ -1,4 +1,5 @@ import { + clientFaultPostgresErrorClassParser, generatePostgresErrorCodeInfo, postgresErrorCodeParser, postgresSeverityParser, @@ -53,6 +54,12 @@ export const postgresErrorParser = z export type ParsedPostgresError = z.infer; +export function isPostgresErrorClientFault( + databaseError: ParsedPostgresError, +): boolean { + const codeInfo = generatePostgresErrorCodeInfo(databaseError.code); + return clientFaultPostgresErrorClassParser.safeParse(codeInfo.class).success; +} export function getDatabaseErrorPublicMessage( databaseError: ParsedPostgresError, ): string { diff --git a/package.json b/package.json index e2bae66..b27f022 100644 --- a/package.json +++ b/package.json @@ -23,16 +23,17 @@ "license": "ISC", "dependencies": { "cors": "^2.8.5", - "dotenv": "^16.4.5", + "dotenv": "^16.4.7", "drizzle-orm": "^0.33.0", "drizzle-seed": "^0.3.1", "drizzle-zod": "^0.5.1", - "express": "^4.21.1", - "pg": "^8.13.0", + "express": "^4.21.2", + "express-zod-safe": "^1.3.3", + "pg": "^8.14.1", "swagger-jsdoc": "^6.2.8", "swagger-ui-express": "^5.0.1", - "validator": "^13.12.0", - "zod": "^3.23.8", + "validator": "^13.15.0", + "zod": "^3.24.2", "zod-openapi": "^3.3.0", "zod-validation-error": "^3.4.0" }, @@ -40,18 +41,18 @@ "@biomejs/biome": "1.9.3", "@types/cors": "^2.8.17", "@types/express": "^4.17.21", - "@types/node": "^22.7.8", - "@types/pg": "^8.11.10", - "@types/supertest": "^6.0.2", + "@types/node": "^22.14.0", + "@types/pg": "^8.11.11", + "@types/supertest": "^6.0.3", "@types/swagger-jsdoc": "^6.0.4", - "@types/swagger-ui-express": "^4.1.7", - "@types/validator": "^13.12.2", + "@types/swagger-ui-express": "^4.1.8", + "@types/validator": "^13.12.3", "drizzle-kit": "^0.24.2", - "supertest": "^7.0.0", - "tsc-alias": "^1.8.10", - "tsx": "^4.19.1", - "typescript": "^5.6.3", - "yaml": "^2.6.0" + "supertest": "^7.1.0", + "tsc-alias": "^1.8.13", + "tsx": "^4.19.3", + "typescript": "^5.8.2", + "yaml": "^2.7.1" }, - "packageManager": "pnpm@10.6.4+sha512.da3d715bfd22a9a105e6e8088cfc7826699332ded60c423b14ec613a185f1602206702ff0fe4c438cb15c979081ce4cb02568e364b15174503a63c7a8e2a5f6c" + "packageManager": "pnpm@10.6.5+sha512.cdf928fca20832cd59ec53826492b7dc25dc524d4370b6b4adbf65803d32efaa6c1c88147c0ae4e8d579a6c9eec715757b50d4fa35eea179d868eada4ed043af" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index dc2d014..64e555a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,23 +12,26 @@ importers: specifier: ^2.8.5 version: 2.8.5 dotenv: - specifier: ^16.4.5 + specifier: ^16.4.7 version: 16.4.7 drizzle-orm: specifier: ^0.33.0 - version: 0.33.0(@types/pg@8.11.11)(pg@8.13.3) + version: 0.33.0(@types/pg@8.11.11)(pg@8.14.1) drizzle-seed: specifier: ^0.3.1 - version: 0.3.1(drizzle-orm@0.33.0(@types/pg@8.11.11)(pg@8.13.3)) + version: 0.3.1(drizzle-orm@0.33.0(@types/pg@8.11.11)(pg@8.14.1)) drizzle-zod: specifier: ^0.5.1 - version: 0.5.1(drizzle-orm@0.33.0(@types/pg@8.11.11)(pg@8.13.3))(zod@3.24.2) + version: 0.5.1(drizzle-orm@0.33.0(@types/pg@8.11.11)(pg@8.14.1))(zod@3.24.2) express: - specifier: ^4.21.1 + specifier: ^4.21.2 version: 4.21.2 + express-zod-safe: + specifier: ^1.3.3 + version: 1.3.3(@types/express@4.17.21)(express@4.21.2)(zod@3.24.2) pg: - specifier: ^8.13.0 - version: 8.13.3 + specifier: ^8.14.1 + version: 8.14.1 swagger-jsdoc: specifier: ^6.2.8 version: 6.2.8(openapi-types@12.1.3) @@ -36,10 +39,10 @@ importers: specifier: ^5.0.1 version: 5.0.1(express@4.21.2) validator: - specifier: ^13.12.0 - version: 13.12.0 + specifier: ^13.15.0 + version: 13.15.0 zod: - specifier: ^3.23.8 + specifier: ^3.24.2 version: 3.24.2 zod-openapi: specifier: ^3.3.0 @@ -58,41 +61,41 @@ importers: specifier: ^4.17.21 version: 4.17.21 '@types/node': - specifier: ^22.7.8 - version: 22.13.9 + specifier: ^22.14.0 + version: 22.14.0 '@types/pg': - specifier: ^8.11.10 + specifier: ^8.11.11 version: 8.11.11 '@types/supertest': - specifier: ^6.0.2 - version: 6.0.2 + specifier: ^6.0.3 + version: 6.0.3 '@types/swagger-jsdoc': specifier: ^6.0.4 version: 6.0.4 '@types/swagger-ui-express': - specifier: ^4.1.7 + specifier: ^4.1.8 version: 4.1.8 '@types/validator': - specifier: ^13.12.2 - version: 13.12.2 + specifier: ^13.12.3 + version: 13.12.3 drizzle-kit: specifier: ^0.24.2 version: 0.24.2 supertest: - specifier: ^7.0.0 - version: 7.0.0 + specifier: ^7.1.0 + version: 7.1.0 tsc-alias: - specifier: ^1.8.10 - version: 1.8.11 + specifier: ^1.8.13 + version: 1.8.13 tsx: - specifier: ^4.19.1 + specifier: ^4.19.3 version: 4.19.3 typescript: - specifier: ^5.6.3 + specifier: ^5.8.2 version: 5.8.2 yaml: - specifier: ^2.6.0 - version: 2.7.0 + specifier: ^2.7.1 + version: 2.7.1 packages: @@ -181,8 +184,8 @@ packages: cpu: [ppc64] os: [aix] - '@esbuild/aix-ppc64@0.25.0': - resolution: {integrity: sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==} + '@esbuild/aix-ppc64@0.25.2': + resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] @@ -199,8 +202,8 @@ packages: cpu: [arm64] os: [android] - '@esbuild/android-arm64@0.25.0': - resolution: {integrity: sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==} + '@esbuild/android-arm64@0.25.2': + resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==} engines: {node: '>=18'} cpu: [arm64] os: [android] @@ -217,8 +220,8 @@ packages: cpu: [arm] os: [android] - '@esbuild/android-arm@0.25.0': - resolution: {integrity: sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==} + '@esbuild/android-arm@0.25.2': + resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==} engines: {node: '>=18'} cpu: [arm] os: [android] @@ -235,8 +238,8 @@ packages: cpu: [x64] os: [android] - '@esbuild/android-x64@0.25.0': - resolution: {integrity: sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==} + '@esbuild/android-x64@0.25.2': + resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==} engines: {node: '>=18'} cpu: [x64] os: [android] @@ -253,8 +256,8 @@ packages: cpu: [arm64] os: [darwin] - '@esbuild/darwin-arm64@0.25.0': - resolution: {integrity: sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==} + '@esbuild/darwin-arm64@0.25.2': + resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] @@ -271,8 +274,8 @@ packages: cpu: [x64] os: [darwin] - '@esbuild/darwin-x64@0.25.0': - resolution: {integrity: sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==} + '@esbuild/darwin-x64@0.25.2': + resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] @@ -289,8 +292,8 @@ packages: cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-arm64@0.25.0': - resolution: {integrity: sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==} + '@esbuild/freebsd-arm64@0.25.2': + resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] @@ -307,8 +310,8 @@ packages: cpu: [x64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.0': - resolution: {integrity: sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==} + '@esbuild/freebsd-x64@0.25.2': + resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] @@ -325,8 +328,8 @@ packages: cpu: [arm64] os: [linux] - '@esbuild/linux-arm64@0.25.0': - resolution: {integrity: sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==} + '@esbuild/linux-arm64@0.25.2': + resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==} engines: {node: '>=18'} cpu: [arm64] os: [linux] @@ -343,8 +346,8 @@ packages: cpu: [arm] os: [linux] - '@esbuild/linux-arm@0.25.0': - resolution: {integrity: sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==} + '@esbuild/linux-arm@0.25.2': + resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==} engines: {node: '>=18'} cpu: [arm] os: [linux] @@ -361,8 +364,8 @@ packages: cpu: [ia32] os: [linux] - '@esbuild/linux-ia32@0.25.0': - resolution: {integrity: sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==} + '@esbuild/linux-ia32@0.25.2': + resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==} engines: {node: '>=18'} cpu: [ia32] os: [linux] @@ -379,8 +382,8 @@ packages: cpu: [loong64] os: [linux] - '@esbuild/linux-loong64@0.25.0': - resolution: {integrity: sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==} + '@esbuild/linux-loong64@0.25.2': + resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==} engines: {node: '>=18'} cpu: [loong64] os: [linux] @@ -397,8 +400,8 @@ packages: cpu: [mips64el] os: [linux] - '@esbuild/linux-mips64el@0.25.0': - resolution: {integrity: sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==} + '@esbuild/linux-mips64el@0.25.2': + resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] @@ -415,8 +418,8 @@ packages: cpu: [ppc64] os: [linux] - '@esbuild/linux-ppc64@0.25.0': - resolution: {integrity: sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==} + '@esbuild/linux-ppc64@0.25.2': + resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] @@ -433,8 +436,8 @@ packages: cpu: [riscv64] os: [linux] - '@esbuild/linux-riscv64@0.25.0': - resolution: {integrity: sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==} + '@esbuild/linux-riscv64@0.25.2': + resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] @@ -451,8 +454,8 @@ packages: cpu: [s390x] os: [linux] - '@esbuild/linux-s390x@0.25.0': - resolution: {integrity: sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==} + '@esbuild/linux-s390x@0.25.2': + resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==} engines: {node: '>=18'} cpu: [s390x] os: [linux] @@ -469,14 +472,14 @@ packages: cpu: [x64] os: [linux] - '@esbuild/linux-x64@0.25.0': - resolution: {integrity: sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==} + '@esbuild/linux-x64@0.25.2': + resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==} engines: {node: '>=18'} cpu: [x64] os: [linux] - '@esbuild/netbsd-arm64@0.25.0': - resolution: {integrity: sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==} + '@esbuild/netbsd-arm64@0.25.2': + resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] @@ -493,14 +496,14 @@ packages: cpu: [x64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.0': - resolution: {integrity: sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==} + '@esbuild/netbsd-x64@0.25.2': + resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-arm64@0.25.0': - resolution: {integrity: sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==} + '@esbuild/openbsd-arm64@0.25.2': + resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] @@ -517,8 +520,8 @@ packages: cpu: [x64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.0': - resolution: {integrity: sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==} + '@esbuild/openbsd-x64@0.25.2': + resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] @@ -535,8 +538,8 @@ packages: cpu: [x64] os: [sunos] - '@esbuild/sunos-x64@0.25.0': - resolution: {integrity: sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==} + '@esbuild/sunos-x64@0.25.2': + resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==} engines: {node: '>=18'} cpu: [x64] os: [sunos] @@ -553,8 +556,8 @@ packages: cpu: [arm64] os: [win32] - '@esbuild/win32-arm64@0.25.0': - resolution: {integrity: sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==} + '@esbuild/win32-arm64@0.25.2': + resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==} engines: {node: '>=18'} cpu: [arm64] os: [win32] @@ -571,8 +574,8 @@ packages: cpu: [ia32] os: [win32] - '@esbuild/win32-ia32@0.25.0': - resolution: {integrity: sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==} + '@esbuild/win32-ia32@0.25.2': + resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==} engines: {node: '>=18'} cpu: [ia32] os: [win32] @@ -589,8 +592,8 @@ packages: cpu: [x64] os: [win32] - '@esbuild/win32-x64@0.25.0': - resolution: {integrity: sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==} + '@esbuild/win32-x64@0.25.2': + resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -643,8 +646,8 @@ packages: '@types/mime@1.3.5': resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} - '@types/node@22.13.9': - resolution: {integrity: sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==} + '@types/node@22.14.0': + resolution: {integrity: sha512-Kmpl+z84ILoG+3T/zQFyAJsU6EPTmOCj8/2+83fSN6djd6I4o7uOuGIH6vq3PrjY5BGitSbFuMN18j3iknubbA==} '@types/pg@8.11.11': resolution: {integrity: sha512-kGT1qKM8wJQ5qlawUrEkXgvMSXoV213KfMGXcwfDwUIfUHXqXYXOfS1nE1LINRJVVVx5wCm70XnFlMHaIcQAfw==} @@ -664,8 +667,8 @@ packages: '@types/superagent@8.1.9': resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==} - '@types/supertest@6.0.2': - resolution: {integrity: sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==} + '@types/supertest@6.0.3': + resolution: {integrity: sha512-8WzXq62EXFhJ7QsH3Ocb/iKQ/Ty9ZVWnVzoTKc9tyyFRRF3a74Tk2+TLFgaFFw364Ere+npzHKEJ6ga2LzIL7w==} '@types/swagger-jsdoc@6.0.4': resolution: {integrity: sha512-W+Xw5epcOZrF/AooUM/PccNMSAFOKWZA5dasNyMujTwsBkU74njSJBpvCCJhHAJ95XRMzQrrW844Btu0uoetwQ==} @@ -673,8 +676,8 @@ packages: '@types/swagger-ui-express@4.1.8': resolution: {integrity: sha512-AhZV8/EIreHFmBV5wAs0gzJUNq9JbbSXgJLQubCC0jtIo6prnI9MIRRxnU4MZX9RB9yXxF1V4R7jtLl/Wcj31g==} - '@types/validator@13.12.2': - resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==} + '@types/validator@13.12.3': + resolution: {integrity: sha512-2ipwZ2NydGQJImne+FhNdhgRM37e9lCev99KnqkbFHd94Xn/mErARWI1RSLem1QA19ch5kOhzIZd7e8CA2FI8g==} accepts@1.3.8: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} @@ -977,8 +980,8 @@ packages: engines: {node: '>=12'} hasBin: true - esbuild@0.25.0: - resolution: {integrity: sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==} + esbuild@0.25.2: + resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==} engines: {node: '>=18'} hasBin: true @@ -993,6 +996,13 @@ packages: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} engines: {node: '>= 0.6'} + express-zod-safe@1.3.3: + resolution: {integrity: sha512-DDYamsIyHpQJOoYOS395+zsAdFIuuqteDcVKUEBGU4Pi5vFf6mwLReSiReT0aNW1kpnXKbktgKzw5wUP7SmPgA==} + peerDependencies: + '@types/express': ^4.0.0 || ^5.0.0 + express: ^4.0.0 || ^5.0.0 + zod: ^3.0.0 + express@4.21.2: resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} engines: {node: '>= 0.10.0'} @@ -1250,13 +1260,13 @@ packages: resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==} engines: {node: '>=4'} - pg-pool@3.7.1: - resolution: {integrity: sha512-xIOsFoh7Vdhojas6q3596mXFsR8nwBQBXX5JiV7p9buEVAGqYL4yFzclON5P9vFrpu1u7Zwl2oriyDa89n0wbw==} + pg-pool@3.8.0: + resolution: {integrity: sha512-VBw3jiVm6ZOdLBTIcXLNdSotb6Iy3uOCwDGFAksZCXmi10nyRvnP2v3jl4d+IsLYRyXf6o9hIm/ZtUzlByNUdw==} peerDependencies: pg: '>=8.0' - pg-protocol@1.7.1: - resolution: {integrity: sha512-gjTHWGYWsEgy9MsY0Gp6ZJxV24IjDqdpTW7Eh0x+WfJLFsm/TJx1MzL6T0D88mBvkpxotCQ6TwW6N+Kko7lhgQ==} + pg-protocol@1.8.0: + resolution: {integrity: sha512-jvuYlEkL03NRvOoyoRktBK7+qU5kOvlAwvmrH8sr3wbLrOdVWsRxQfz8mMy9sZFsqJ1hEWNfdWKI4SAmoL+j7g==} pg-types@2.2.0: resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} @@ -1266,8 +1276,8 @@ packages: resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==} engines: {node: '>=10'} - pg@8.13.3: - resolution: {integrity: sha512-P6tPt9jXbL9HVu/SSRERNYaYG++MjnscnegFh9pPHihfoBSujsrka0hyuymMzeJKFWrcG8wvCKy8rCe8e5nDUQ==} + pg@8.14.1: + resolution: {integrity: sha512-0TdbqfjwIun9Fm/r89oB7RFQ0bLgduAhiIqIXOsyKoiC/L54DbuAAzIEN/9Op0f1Po9X7iCPXGoa/Ah+2aI8Xw==} engines: {node: '>= 8.0.0'} peerDependencies: pg-native: '>=3.0.1' @@ -1290,8 +1300,8 @@ packages: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} - postgres-array@3.0.2: - resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==} + postgres-array@3.0.4: + resolution: {integrity: sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==} engines: {node: '>=12'} postgres-bytea@1.0.0: @@ -1332,6 +1342,10 @@ packages: resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} engines: {node: '>=0.6'} + qs@6.14.0: + resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==} + engines: {node: '>=0.6'} + queue-lit@1.5.2: resolution: {integrity: sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==} engines: {node: '>=12'} @@ -1417,8 +1431,8 @@ packages: resolution: {integrity: sha512-xuW7dzkUpcJq7QnhOsnNUgtYp3xRwpt2F7abdRYIpCsAt0hhUqia0EdxyXZQQpNmGtsCzYHryaKSV3q3GJnq7w==} engines: {node: '>=14.18.0'} - supertest@7.0.0: - resolution: {integrity: sha512-qlsr7fIC0lSddmA3tzojvzubYxvlGtzumcdHgPwbFWMISQwL22MhM2Y3LNt+6w9Yyx7559VW5ab70dgphm8qQA==} + supertest@7.1.0: + resolution: {integrity: sha512-5QeSO8hSrKghtcWEoPiO036fxH0Ii2wVQfFZSP0oqQhmjk8bOLhDFXr4JrvaFmPuEWUoq4znY3uSi8UzLKxGqw==} engines: {node: '>=14.18.0'} swagger-jsdoc@6.2.8: @@ -1430,8 +1444,8 @@ packages: resolution: {integrity: sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==} engines: {node: '>=10'} - swagger-ui-dist@5.20.0: - resolution: {integrity: sha512-V5pozVTZxivdoQq/SQWxj3A4cOu5opk9MEbcZANX3Pj8X8xCrD1QCtBT7744Pz9msOvt0nnmy9JvM/9PGonCdg==} + swagger-ui-dist@5.20.4: + resolution: {integrity: sha512-3AdoFZyYBsqk65HpBckS6MVUxMZxpCgukDfGb9pz83faUGziO5k5QTO4gYrPBtkqiJC9b1rC0QwLwH8MkcWJ0g==} swagger-ui-express@5.0.1: resolution: {integrity: sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==} @@ -1447,8 +1461,9 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} - tsc-alias@1.8.11: - resolution: {integrity: sha512-2DuEQ58A9Rj2NE2c1+/qaGKlshni9MCK95MJzRGhQG0CYLw0bE/ACgbhhTSf/p1svLelwqafOd8stQate2bYbg==} + tsc-alias@1.8.13: + resolution: {integrity: sha512-hpuglrm2DoHZE62L8ntYqRNiSQ7J8kvIxEsajzY/QfGOm7EcdhgG5asqoWYi2E2KX0SqUuhOTnV8Ry8D/TnsEA==} + engines: {node: '>=16.20.2'} hasBin: true tsx@4.19.3: @@ -1465,8 +1480,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - undici-types@6.20.0: - resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} unpipe@1.0.0: resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} @@ -1476,8 +1491,8 @@ packages: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} - validator@13.12.0: - resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} + validator@13.15.0: + resolution: {integrity: sha512-36B2ryl4+oL5QxZ3AzD0t5SsMNGvTtQHpjgFO5tbNxfXbMFkY822ktCDe1MnlqV3301QQI9SLHDNJokDI+Z9pA==} engines: {node: '>= 0.10'} vary@1.1.2: @@ -1495,8 +1510,8 @@ packages: resolution: {integrity: sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==} engines: {node: '>= 6'} - yaml@2.7.0: - resolution: {integrity: sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==} + yaml@2.7.1: + resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==} engines: {node: '>= 14'} hasBin: true @@ -1593,7 +1608,7 @@ snapshots: '@esbuild/aix-ppc64@0.19.12': optional: true - '@esbuild/aix-ppc64@0.25.0': + '@esbuild/aix-ppc64@0.25.2': optional: true '@esbuild/android-arm64@0.18.20': @@ -1602,7 +1617,7 @@ snapshots: '@esbuild/android-arm64@0.19.12': optional: true - '@esbuild/android-arm64@0.25.0': + '@esbuild/android-arm64@0.25.2': optional: true '@esbuild/android-arm@0.18.20': @@ -1611,7 +1626,7 @@ snapshots: '@esbuild/android-arm@0.19.12': optional: true - '@esbuild/android-arm@0.25.0': + '@esbuild/android-arm@0.25.2': optional: true '@esbuild/android-x64@0.18.20': @@ -1620,7 +1635,7 @@ snapshots: '@esbuild/android-x64@0.19.12': optional: true - '@esbuild/android-x64@0.25.0': + '@esbuild/android-x64@0.25.2': optional: true '@esbuild/darwin-arm64@0.18.20': @@ -1629,7 +1644,7 @@ snapshots: '@esbuild/darwin-arm64@0.19.12': optional: true - '@esbuild/darwin-arm64@0.25.0': + '@esbuild/darwin-arm64@0.25.2': optional: true '@esbuild/darwin-x64@0.18.20': @@ -1638,7 +1653,7 @@ snapshots: '@esbuild/darwin-x64@0.19.12': optional: true - '@esbuild/darwin-x64@0.25.0': + '@esbuild/darwin-x64@0.25.2': optional: true '@esbuild/freebsd-arm64@0.18.20': @@ -1647,7 +1662,7 @@ snapshots: '@esbuild/freebsd-arm64@0.19.12': optional: true - '@esbuild/freebsd-arm64@0.25.0': + '@esbuild/freebsd-arm64@0.25.2': optional: true '@esbuild/freebsd-x64@0.18.20': @@ -1656,7 +1671,7 @@ snapshots: '@esbuild/freebsd-x64@0.19.12': optional: true - '@esbuild/freebsd-x64@0.25.0': + '@esbuild/freebsd-x64@0.25.2': optional: true '@esbuild/linux-arm64@0.18.20': @@ -1665,7 +1680,7 @@ snapshots: '@esbuild/linux-arm64@0.19.12': optional: true - '@esbuild/linux-arm64@0.25.0': + '@esbuild/linux-arm64@0.25.2': optional: true '@esbuild/linux-arm@0.18.20': @@ -1674,7 +1689,7 @@ snapshots: '@esbuild/linux-arm@0.19.12': optional: true - '@esbuild/linux-arm@0.25.0': + '@esbuild/linux-arm@0.25.2': optional: true '@esbuild/linux-ia32@0.18.20': @@ -1683,7 +1698,7 @@ snapshots: '@esbuild/linux-ia32@0.19.12': optional: true - '@esbuild/linux-ia32@0.25.0': + '@esbuild/linux-ia32@0.25.2': optional: true '@esbuild/linux-loong64@0.18.20': @@ -1692,7 +1707,7 @@ snapshots: '@esbuild/linux-loong64@0.19.12': optional: true - '@esbuild/linux-loong64@0.25.0': + '@esbuild/linux-loong64@0.25.2': optional: true '@esbuild/linux-mips64el@0.18.20': @@ -1701,7 +1716,7 @@ snapshots: '@esbuild/linux-mips64el@0.19.12': optional: true - '@esbuild/linux-mips64el@0.25.0': + '@esbuild/linux-mips64el@0.25.2': optional: true '@esbuild/linux-ppc64@0.18.20': @@ -1710,7 +1725,7 @@ snapshots: '@esbuild/linux-ppc64@0.19.12': optional: true - '@esbuild/linux-ppc64@0.25.0': + '@esbuild/linux-ppc64@0.25.2': optional: true '@esbuild/linux-riscv64@0.18.20': @@ -1719,7 +1734,7 @@ snapshots: '@esbuild/linux-riscv64@0.19.12': optional: true - '@esbuild/linux-riscv64@0.25.0': + '@esbuild/linux-riscv64@0.25.2': optional: true '@esbuild/linux-s390x@0.18.20': @@ -1728,7 +1743,7 @@ snapshots: '@esbuild/linux-s390x@0.19.12': optional: true - '@esbuild/linux-s390x@0.25.0': + '@esbuild/linux-s390x@0.25.2': optional: true '@esbuild/linux-x64@0.18.20': @@ -1737,10 +1752,10 @@ snapshots: '@esbuild/linux-x64@0.19.12': optional: true - '@esbuild/linux-x64@0.25.0': + '@esbuild/linux-x64@0.25.2': optional: true - '@esbuild/netbsd-arm64@0.25.0': + '@esbuild/netbsd-arm64@0.25.2': optional: true '@esbuild/netbsd-x64@0.18.20': @@ -1749,10 +1764,10 @@ snapshots: '@esbuild/netbsd-x64@0.19.12': optional: true - '@esbuild/netbsd-x64@0.25.0': + '@esbuild/netbsd-x64@0.25.2': optional: true - '@esbuild/openbsd-arm64@0.25.0': + '@esbuild/openbsd-arm64@0.25.2': optional: true '@esbuild/openbsd-x64@0.18.20': @@ -1761,7 +1776,7 @@ snapshots: '@esbuild/openbsd-x64@0.19.12': optional: true - '@esbuild/openbsd-x64@0.25.0': + '@esbuild/openbsd-x64@0.25.2': optional: true '@esbuild/sunos-x64@0.18.20': @@ -1770,7 +1785,7 @@ snapshots: '@esbuild/sunos-x64@0.19.12': optional: true - '@esbuild/sunos-x64@0.25.0': + '@esbuild/sunos-x64@0.25.2': optional: true '@esbuild/win32-arm64@0.18.20': @@ -1779,7 +1794,7 @@ snapshots: '@esbuild/win32-arm64@0.19.12': optional: true - '@esbuild/win32-arm64@0.25.0': + '@esbuild/win32-arm64@0.25.2': optional: true '@esbuild/win32-ia32@0.18.20': @@ -1788,7 +1803,7 @@ snapshots: '@esbuild/win32-ia32@0.19.12': optional: true - '@esbuild/win32-ia32@0.25.0': + '@esbuild/win32-ia32@0.25.2': optional: true '@esbuild/win32-x64@0.18.20': @@ -1797,7 +1812,7 @@ snapshots: '@esbuild/win32-x64@0.19.12': optional: true - '@esbuild/win32-x64@0.25.0': + '@esbuild/win32-x64@0.25.2': optional: true '@jsdevtools/ono@7.1.3': {} @@ -1819,21 +1834,21 @@ snapshots: '@types/body-parser@1.19.5': dependencies: '@types/connect': 3.4.38 - '@types/node': 22.13.9 + '@types/node': 22.14.0 '@types/connect@3.4.38': dependencies: - '@types/node': 22.13.9 + '@types/node': 22.14.0 '@types/cookiejar@2.1.5': {} '@types/cors@2.8.17': dependencies: - '@types/node': 22.13.9 + '@types/node': 22.14.0 '@types/express-serve-static-core@4.19.6': dependencies: - '@types/node': 22.13.9 + '@types/node': 22.14.0 '@types/qs': 6.9.18 '@types/range-parser': 1.2.7 '@types/send': 0.17.4 @@ -1853,14 +1868,14 @@ snapshots: '@types/mime@1.3.5': {} - '@types/node@22.13.9': + '@types/node@22.14.0': dependencies: - undici-types: 6.20.0 + undici-types: 6.21.0 '@types/pg@8.11.11': dependencies: - '@types/node': 22.13.9 - pg-protocol: 1.7.1 + '@types/node': 22.14.0 + pg-protocol: 1.8.0 pg-types: 4.0.2 '@types/qs@6.9.18': {} @@ -1870,22 +1885,22 @@ snapshots: '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 - '@types/node': 22.13.9 + '@types/node': 22.14.0 '@types/serve-static@1.15.7': dependencies: '@types/http-errors': 2.0.4 - '@types/node': 22.13.9 + '@types/node': 22.14.0 '@types/send': 0.17.4 '@types/superagent@8.1.9': dependencies: '@types/cookiejar': 2.1.5 '@types/methods': 1.1.4 - '@types/node': 22.13.9 + '@types/node': 22.14.0 form-data: 4.0.2 - '@types/supertest@6.0.2': + '@types/supertest@6.0.3': dependencies: '@types/methods': 1.1.4 '@types/superagent': 8.1.9 @@ -1897,7 +1912,7 @@ snapshots: '@types/express': 4.17.21 '@types/serve-static': 1.15.7 - '@types/validator@13.12.2': {} + '@types/validator@13.12.3': {} accepts@1.3.8: dependencies: @@ -2044,20 +2059,20 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.33.0(@types/pg@8.11.11)(pg@8.13.3): + drizzle-orm@0.33.0(@types/pg@8.11.11)(pg@8.14.1): optionalDependencies: '@types/pg': 8.11.11 - pg: 8.13.3 + pg: 8.14.1 - drizzle-seed@0.3.1(drizzle-orm@0.33.0(@types/pg@8.11.11)(pg@8.13.3)): + drizzle-seed@0.3.1(drizzle-orm@0.33.0(@types/pg@8.11.11)(pg@8.14.1)): dependencies: pure-rand: 6.1.0 optionalDependencies: - drizzle-orm: 0.33.0(@types/pg@8.11.11)(pg@8.13.3) + drizzle-orm: 0.33.0(@types/pg@8.11.11)(pg@8.14.1) - drizzle-zod@0.5.1(drizzle-orm@0.33.0(@types/pg@8.11.11)(pg@8.13.3))(zod@3.24.2): + drizzle-zod@0.5.1(drizzle-orm@0.33.0(@types/pg@8.11.11)(pg@8.14.1))(zod@3.24.2): dependencies: - drizzle-orm: 0.33.0(@types/pg@8.11.11)(pg@8.13.3) + drizzle-orm: 0.33.0(@types/pg@8.11.11)(pg@8.14.1) zod: 3.24.2 dunder-proto@1.0.1: @@ -2145,33 +2160,33 @@ snapshots: '@esbuild/win32-ia32': 0.19.12 '@esbuild/win32-x64': 0.19.12 - esbuild@0.25.0: + esbuild@0.25.2: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.0 - '@esbuild/android-arm': 0.25.0 - '@esbuild/android-arm64': 0.25.0 - '@esbuild/android-x64': 0.25.0 - '@esbuild/darwin-arm64': 0.25.0 - '@esbuild/darwin-x64': 0.25.0 - '@esbuild/freebsd-arm64': 0.25.0 - '@esbuild/freebsd-x64': 0.25.0 - '@esbuild/linux-arm': 0.25.0 - '@esbuild/linux-arm64': 0.25.0 - '@esbuild/linux-ia32': 0.25.0 - '@esbuild/linux-loong64': 0.25.0 - '@esbuild/linux-mips64el': 0.25.0 - '@esbuild/linux-ppc64': 0.25.0 - '@esbuild/linux-riscv64': 0.25.0 - '@esbuild/linux-s390x': 0.25.0 - '@esbuild/linux-x64': 0.25.0 - '@esbuild/netbsd-arm64': 0.25.0 - '@esbuild/netbsd-x64': 0.25.0 - '@esbuild/openbsd-arm64': 0.25.0 - '@esbuild/openbsd-x64': 0.25.0 - '@esbuild/sunos-x64': 0.25.0 - '@esbuild/win32-arm64': 0.25.0 - '@esbuild/win32-ia32': 0.25.0 - '@esbuild/win32-x64': 0.25.0 + '@esbuild/aix-ppc64': 0.25.2 + '@esbuild/android-arm': 0.25.2 + '@esbuild/android-arm64': 0.25.2 + '@esbuild/android-x64': 0.25.2 + '@esbuild/darwin-arm64': 0.25.2 + '@esbuild/darwin-x64': 0.25.2 + '@esbuild/freebsd-arm64': 0.25.2 + '@esbuild/freebsd-x64': 0.25.2 + '@esbuild/linux-arm': 0.25.2 + '@esbuild/linux-arm64': 0.25.2 + '@esbuild/linux-ia32': 0.25.2 + '@esbuild/linux-loong64': 0.25.2 + '@esbuild/linux-mips64el': 0.25.2 + '@esbuild/linux-ppc64': 0.25.2 + '@esbuild/linux-riscv64': 0.25.2 + '@esbuild/linux-s390x': 0.25.2 + '@esbuild/linux-x64': 0.25.2 + '@esbuild/netbsd-arm64': 0.25.2 + '@esbuild/netbsd-x64': 0.25.2 + '@esbuild/openbsd-arm64': 0.25.2 + '@esbuild/openbsd-x64': 0.25.2 + '@esbuild/sunos-x64': 0.25.2 + '@esbuild/win32-arm64': 0.25.2 + '@esbuild/win32-ia32': 0.25.2 + '@esbuild/win32-x64': 0.25.2 escape-html@1.0.3: {} @@ -2179,6 +2194,12 @@ snapshots: etag@1.8.1: {} + express-zod-safe@1.3.3(@types/express@4.17.21)(express@4.21.2)(zod@3.24.2): + dependencies: + '@types/express': 4.17.21 + express: 4.21.2 + zod: 3.24.2 + express@4.21.2: dependencies: accepts: 1.3.8 @@ -2444,11 +2465,11 @@ snapshots: pg-numeric@1.0.2: {} - pg-pool@3.7.1(pg@8.13.3): + pg-pool@3.8.0(pg@8.14.1): dependencies: - pg: 8.13.3 + pg: 8.14.1 - pg-protocol@1.7.1: {} + pg-protocol@1.8.0: {} pg-types@2.2.0: dependencies: @@ -2462,17 +2483,17 @@ snapshots: dependencies: pg-int8: 1.0.1 pg-numeric: 1.0.2 - postgres-array: 3.0.2 + postgres-array: 3.0.4 postgres-bytea: 3.0.0 postgres-date: 2.1.0 postgres-interval: 3.0.0 postgres-range: 1.1.4 - pg@8.13.3: + pg@8.14.1: dependencies: pg-connection-string: 2.7.0 - pg-pool: 3.7.1(pg@8.13.3) - pg-protocol: 1.7.1 + pg-pool: 3.8.0(pg@8.14.1) + pg-protocol: 1.8.0 pg-types: 2.2.0 pgpass: 1.0.5 optionalDependencies: @@ -2490,7 +2511,7 @@ snapshots: postgres-array@2.0.0: {} - postgres-array@3.0.2: {} + postgres-array@3.0.4: {} postgres-bytea@1.0.0: {} @@ -2521,6 +2542,10 @@ snapshots: dependencies: side-channel: 1.1.0 + qs@6.14.0: + dependencies: + side-channel: 1.1.0 + queue-lit@1.5.2: {} queue-microtask@1.2.3: {} @@ -2630,11 +2655,11 @@ snapshots: formidable: 3.5.2 methods: 1.1.2 mime: 2.6.0 - qs: 6.13.0 + qs: 6.14.0 transitivePeerDependencies: - supports-color - supertest@7.0.0: + supertest@7.1.0: dependencies: methods: 1.1.2 superagent: 9.0.2 @@ -2658,14 +2683,14 @@ snapshots: transitivePeerDependencies: - openapi-types - swagger-ui-dist@5.20.0: + swagger-ui-dist@5.20.4: dependencies: '@scarf/scarf': 1.4.0 swagger-ui-express@5.0.1(express@4.21.2): dependencies: express: 4.21.2 - swagger-ui-dist: 5.20.0 + swagger-ui-dist: 5.20.4 to-regex-range@5.0.1: dependencies: @@ -2673,7 +2698,7 @@ snapshots: toidentifier@1.0.1: {} - tsc-alias@1.8.11: + tsc-alias@1.8.13: dependencies: chokidar: 3.6.0 commander: 9.5.0 @@ -2684,7 +2709,7 @@ snapshots: tsx@4.19.3: dependencies: - esbuild: 0.25.0 + esbuild: 0.25.2 get-tsconfig: 4.10.0 optionalDependencies: fsevents: 2.3.3 @@ -2696,13 +2721,13 @@ snapshots: typescript@5.8.2: {} - undici-types@6.20.0: {} + undici-types@6.21.0: {} unpipe@1.0.0: {} utils-merge@1.0.1: {} - validator@13.12.0: {} + validator@13.15.0: {} vary@1.1.2: {} @@ -2712,13 +2737,13 @@ snapshots: yaml@2.0.0-1: {} - yaml@2.7.0: {} + yaml@2.7.1: {} z-schema@5.0.5: dependencies: lodash.get: 4.4.2 lodash.isequal: 4.5.0 - validator: 13.12.0 + validator: 13.15.0 optionalDependencies: commander: 9.5.0 diff --git a/src/error/error-messages.ts b/src/error/error-messages.ts index 813c390..bd749d6 100644 --- a/src/error/error-messages.ts +++ b/src/error/error-messages.ts @@ -68,6 +68,7 @@ const HTTP_CLIENT_ERROR_MESSAGES = [ "Database schema mismatch", "Failed to execute the database command", "Error parsing database response", + "Database error", ] as const; const HTTP_SERVER_ERROR_MESSAGES = [ "Internal server error occurred", diff --git a/src/error/orm-error.ts b/src/error/orm-error.ts index f5cce47..bfd9174 100644 --- a/src/error/orm-error.ts +++ b/src/error/orm-error.ts @@ -2,6 +2,7 @@ import { type ParsedPostgresError, getDatabaseErrorPrivateMessage, getDatabaseErrorPublicMessage, + isPostgresErrorClientFault, postgresErrorParser, } from "@/db/errors/postgres-error"; import type { Result } from "@/lib/types"; @@ -43,6 +44,9 @@ class OrmError extends Error { } return this.customMessage?.private; } + isClientFault(): boolean { + return !this.hasDatabaseCause() || isPostgresErrorClientFault(this.cause); + } private hasCustomMessage(): this is { public: string; private: string } { return this.customMessage !== undefined; } diff --git a/src/main.ts b/src/main.ts index e8b2935..bf70ab9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,35 +1,39 @@ import "dotenv/config"; +import { hostOptions } from "@/src/enviroment"; import { - defaultErrorHandler, errorHandler, + httpErrorHandler, + ormErrorHandler, + zodErrorHandler, } from "@/src/middleware/error-middleware"; import { logger } from "@/src/middleware/logging-middleware"; -import express from "express"; - +import { openapiSpecification } from "@/src/openapi/config"; import { teamApplicationRouter } from "@/src/routers/applications"; import { expensesRouter } from "@/src/routers/expenses"; -import { customCors, customHelmetSecurity } from "@/src/security"; - -import { openapiSpecification } from "@/src/openapi/config"; import { sponsorsRouter } from "@/src/routers/sponsors"; import { teamsRouter } from "@/src/routers/teams"; import { usersRouter } from "@/src/routers/users"; +import { customCors, customHelmetSecurity } from "@/src/security"; +import express from "express"; import openapiExpressHandler from "swagger-ui-express"; -import { hostOptions } from "./enviroment"; export const api = express(); // Security -api.use(customHelmetSecurity); +api.use(customHelmetSecurity, customCors()); api.disable("x-powered-by"); -api.use(customCors()); // OpenAPI -api.use("/docs/api", openapiExpressHandler.serve); -api.get("/docs/api", openapiExpressHandler.setup(openapiSpecification)); +api.get( + "/docs/openapi", + openapiExpressHandler.serve, + openapiExpressHandler.setup(openapiSpecification), +); -api.use("", logger); +// Logger +api.use(logger); +// Routes api.use("/expenses", expensesRouter); api.use("/sponsors", sponsorsRouter); @@ -39,8 +43,8 @@ api.use("/users", usersRouter); api.use("/teamapplications", teamApplicationRouter); api.use("/teams", teamsRouter); -api.use("", errorHandler); -api.use("", defaultErrorHandler); +// Error handling +api.use(ormErrorHandler, zodErrorHandler, httpErrorHandler, errorHandler); api.listen(hostOptions.port, () => { console.info( diff --git a/src/middleware/error-middleware.ts b/src/middleware/error-middleware.ts index 82b2ad1..094e364 100644 --- a/src/middleware/error-middleware.ts +++ b/src/middleware/error-middleware.ts @@ -1,21 +1,35 @@ -import { isHttpError } from "@/src/error/http-errors"; +import { clientError, isHttpError, serverError } from "@/src/error/http-errors"; +import { isOrmError } from "@/src/error/orm-error"; import type { ErrorRequestHandler } from "express"; +import { isZodErrorLike } from "zod-validation-error"; -export const errorHandler: ErrorRequestHandler = (err, _req, res, next) => { - if (!isHttpError(err)) { - return next(err); +export const httpErrorHandler: ErrorRequestHandler = (err, _req, res, next) => { + if (isHttpError(err)) { + return res + .status(err.getResponseCode()) + .json({ error: true, message: err.getResponseString() }); } - res - .status(err.getResponseCode()) - .json({ error: true, message: err.getResponseString() }); + return next(err); }; -export const defaultErrorHandler: ErrorRequestHandler = ( - _err, - _req, - res, - _next, -) => { +export const ormErrorHandler: ErrorRequestHandler = (err, _req, _res, next) => { + if (isOrmError(err)) { + if (err.isClientFault()) { + return next(clientError(400, "Database error")); + } + return next(serverError(500, "Data processing error")); + } + return next(err); +}; + +export const zodErrorHandler: ErrorRequestHandler = (err, _req, _res, next) => { + if (isZodErrorLike(err)) { + return next(clientError(401, "Bad request syntax")); + } + return next(err); +}; + +export const errorHandler: ErrorRequestHandler = (_err, _req, res, _next) => { console.warn("WARNING! DEFAULT EXPRESS ERRORHANDLER IS USED."); res.status(500).json({ error: true, message: "Unknown error." }); };