diff --git a/packages/error-handler/.eslintignore b/packages/error-handler/.eslintignore new file mode 100644 index 000000000..7c8131f7e --- /dev/null +++ b/packages/error-handler/.eslintignore @@ -0,0 +1,4 @@ +.eslintrc.cjs +coverage +dist +node_modules diff --git a/packages/error-handler/.eslintrc.cjs b/packages/error-handler/.eslintrc.cjs new file mode 100644 index 000000000..7de245ade --- /dev/null +++ b/packages/error-handler/.eslintrc.cjs @@ -0,0 +1,4 @@ +module.exports = { + root: true, + extends: ["@prefabs.tech/eslint-config/fastify"], +}; diff --git a/packages/error-handler/.gitignore b/packages/error-handler/.gitignore new file mode 100644 index 000000000..62853f374 --- /dev/null +++ b/packages/error-handler/.gitignore @@ -0,0 +1,4 @@ +**/*.log* +/coverage +/dist +/node_modules diff --git a/packages/error-handler/README.md b/packages/error-handler/README.md new file mode 100644 index 000000000..2991c6cfa --- /dev/null +++ b/packages/error-handler/README.md @@ -0,0 +1,76 @@ +# @prefabs.tech/fastify-error-handler + +A [Fastify](https://github.com/fastify/fastify) plugin that provides an easy integration of error handler in fastify API. + +## Requirements + +* [@prefabs.tech/fastify-config](../config/) +* [@fastify/sensible](https://github.com/fastify/fastify-sensible) + +## Installation + +Install with npm: + +```bash +npm install @prefabs.tech/fastify-error-handler +``` + +Install with pnpm: + +```bash +pnpm add --filter "@scope/project @prefabs.tech/fastify-error-handler +``` + +## Usage + +### Register Plugin + +Register @prefabs.tech/fastify-error-handler package with your Fastify instance: + +Note: Register the errorHandler plugin as early as possible (Before all your routes and plugin registration). + +```typescript +import errorHandlerPlugin from "@prefabs.tech/fastify-error-handler"; +import Fastify from "fastify"; + +const start = async () => { + // Create fastify instance + const fastify = Fastify(); + + // Register fastify-error-handler plugin + await fastify.register(errorHandlerPlugin, {}); + + await fastify.listen({ + port: config.port, + host: "0.0.0.0", + }); +}; + +start(); +``` +### Options + +#### stackTrace + +When enabled, the error handler will include the error’s stack trace in the HTTP response body. + +By default, it is set to false. + +```ts +stackTrace?: boolean; // Default: false +``` + +#### preErrorHandler + +preErrorHandler is an optional error handler that runs before the default error handler logic. +It allows you to intercept specific errors, handle them yourself, and prevent the default handler from running. + +This is especially useful when you need to integrate with other libraries that have their own error formats — for example, handling SuperTokens errors before your API’s standard error response. + +```ts +preErrorHandler?: ( + error: FastifyError, + request: FastifyRequest, + reply: FastifyReply, +) => void | Promise; +``` diff --git a/packages/error-handler/package.json b/packages/error-handler/package.json new file mode 100644 index 000000000..3581d4978 --- /dev/null +++ b/packages/error-handler/package.json @@ -0,0 +1,67 @@ +{ + "name": "@prefabs.tech/fastify-error-handler", + "version": "0.88.2", + "description": "Fastify error-handler plugin", + "homepage": "https://github.com/prefabs-tech/fastify/tree/main/packages/error-handler#readme", + "repository": { + "type": "git", + "url": "git+https://github.com/prefabs-tech/fastify.git", + "directory": "packages/error-handler" + }, + "license": "MIT", + "type": "module", + "exports": { + ".": { + "import": "./dist/prefabs-tech-fastify-error-handler.js", + "require": "./dist/prefabs-tech-fastify-error-handler.umd.cjs" + } + }, + "main": "./dist/prefabs-tech-fastify-error-handler.umd.cjs", + "module": "./dist/prefabs-tech-fastify-error-handler.js", + "types": "./dist/types/index.d.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "vite build && tsc --emitDeclarationOnly && mv dist/src dist/types", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "sort-package": "npx sort-package-json", + "typecheck": "tsc --noEmit -p tsconfig.json --composite false" + }, + "dependencies": { + "@fastify/sensible": "6.0.3", + "stack-trace":"1.0.0-pre2" + }, + "devDependencies": { + "@prefabs.tech/eslint-config": "0.2.0", + "@prefabs.tech/tsconfig": "0.2.0", + "@types/node": "20.19.9", + "@types/stack-trace": "0.0.33", + "@typescript-eslint/eslint-plugin": "8.38.0", + "@typescript-eslint/parser": "8.38.0", + "@vitest/coverage-istanbul": "3.2.4", + "eslint": "8.57.1", + "eslint-config-prettier": "9.1.2", + "eslint-import-resolver-alias": "1.1.2", + "eslint-import-resolver-typescript": "3.10.1", + "eslint-plugin-import": "2.32.0", + "eslint-plugin-n": "14.0.0", + "eslint-plugin-prettier": "5.5.3", + "eslint-plugin-promise": "7.2.1", + "eslint-plugin-unicorn": "56.0.1", + "fastify": "5.4.0", + "fastify-plugin": "5.0.1", + "prettier": "3.6.2", + "typescript": "5.8.3", + "vite": "6.3.5", + "vitest": "3.2.4" + }, + "peerDependencies": { + "fastify": ">=5.2.1", + "fastify-plugin": ">=5.0.1" + }, + "engines": { + "node": ">=20" + } +} diff --git a/packages/error-handler/src/errorHandler.ts b/packages/error-handler/src/errorHandler.ts new file mode 100644 index 000000000..6f8047df0 --- /dev/null +++ b/packages/error-handler/src/errorHandler.ts @@ -0,0 +1,82 @@ +import { STATUS_CODES } from "node:http"; + +import { HttpError } from "@fastify/sensible"; +import { FastifyReply, FastifyRequest } from "fastify"; +import { parse } from "stack-trace"; + +import { CustomError } from "./utils/error"; + +import type { ErrorResponse } from "./types"; + +const getHttpStatusText = (statusCode: number): string => + STATUS_CODES[statusCode] ?? "Internal Server Error"; + +export const errorHandler = ( + error: Error, + request: FastifyRequest, + reply: FastifyReply, +) => { + const { log: logger } = request; + + const isStackTraceEnabled = request.server.stackTrace || false; + + const isHttpError = error instanceof HttpError; + + if (isHttpError) { + const statusCode = error.statusCode || 500; + + if (statusCode >= 500) { + logger.error(error); + } else if (statusCode >= 400) { + logger.info(error); + } else { + logger.error(error); + } + + const response: ErrorResponse = { + code: error.code, + error: error.error || getHttpStatusText(statusCode), + message: error.message, + name: error.name, + statusCode, + }; + + if (isStackTraceEnabled && error.stack) { + response.stack = parse(error); + } + + void reply.code(statusCode).send(response); + + return; + } + + let message = "Server error, please contact support"; + let code = "INTERNAL_SERVER_ERROR"; + + if (error instanceof CustomError) { + code = error.code || code; + message = "Server has an error that is not handled, please contact support"; + } + + if (isStackTraceEnabled && error.stack) { + const response: ErrorResponse = { + code: code, + message: error.message, + name: error.name, + statusCode: 500, + }; + + response.stack = parse(error); + + void reply.code(500).send(response); + + return; + } + + // remove stack and message from error + delete error.stack; + error.message = message; + + // let fastify handle the error + throw error; +}; diff --git a/packages/error-handler/src/index.ts b/packages/error-handler/src/index.ts new file mode 100644 index 000000000..0d5f8f8bf --- /dev/null +++ b/packages/error-handler/src/index.ts @@ -0,0 +1,18 @@ +import type { HttpErrors } from "@fastify/sensible"; + +declare module "fastify" { + interface FastifyInstance { + httpErrors: HttpErrors; + stackTrace: boolean; + } +} + +export { default } from "./plugin"; + +export { errorHandler } from "./errorHandler"; + +export { CustomError } from "./utils/error"; + +export type { HttpErrors } from "@fastify/sensible"; + +export type * from "./types"; diff --git a/packages/error-handler/src/plugin.ts b/packages/error-handler/src/plugin.ts new file mode 100644 index 000000000..f186210ae --- /dev/null +++ b/packages/error-handler/src/plugin.ts @@ -0,0 +1,36 @@ +import fastifySensible from "@fastify/sensible"; +import FastifyPlugin from "fastify-plugin"; + +import { errorHandler } from "./errorHandler"; + +import type { ErrorHandlerOptions } from "./types"; +import type { FastifyInstance } from "fastify"; + +const plugin = async ( + fastify: FastifyInstance, + options: ErrorHandlerOptions, +) => { + fastify.log.info("Registering fastify-error-handler plugin"); + + fastify.decorate("stackTrace", options.stackTrace || false); + + await fastify.register(fastifySensible); + + fastify.setErrorHandler(async (error, request, reply) => { + if (options.preErrorHandler) { + try { + await options.preErrorHandler(error, request, reply); + } catch { + // If preErrorHandler throws an error, we can ignore it and continue + } + + if (reply.sent) { + return; + } + } + + return errorHandler(error, request, reply); + }); +}; + +export default FastifyPlugin(plugin); diff --git a/packages/error-handler/src/types.ts b/packages/error-handler/src/types.ts new file mode 100644 index 000000000..930a7ca6c --- /dev/null +++ b/packages/error-handler/src/types.ts @@ -0,0 +1,27 @@ +import { FastifyError, FastifyRequest, FastifyReply } from "fastify"; + +import type { StackFrame } from "stack-trace"; + +type ErrorHandler = ( + error: FastifyError, + request: FastifyRequest, + reply: FastifyReply, +) => void | Promise; + +interface ErrorHandlerOptions { + preErrorHandler?: ErrorHandler; + stackTrace?: boolean; +} + +type ErrorResponse = { + error?: string; + code?: string; + message: string; + name: string; + stack?: StackFrame[]; + statusCode: number; +}; + +export type { ErrorHandler, ErrorHandlerOptions, ErrorResponse }; + +export { type StackFrame } from "stack-trace"; diff --git a/packages/error-handler/src/utils/error.ts b/packages/error-handler/src/utils/error.ts new file mode 100644 index 000000000..e30e3c8c7 --- /dev/null +++ b/packages/error-handler/src/utils/error.ts @@ -0,0 +1,13 @@ +export class CustomError extends Error { + public code?: string; + + constructor(message: string, code?: string) { + super(message); + + this.code = code; + this.name = this.constructor.name; // sets name to "CustomError" so that it works in logs + + // (error instanceof CustomError) and (error instanceof Error) both works because of this + Object.setPrototypeOf(this, new.target.prototype); + } +} diff --git a/packages/error-handler/tsconfig.json b/packages/error-handler/tsconfig.json new file mode 100644 index 000000000..50005d55b --- /dev/null +++ b/packages/error-handler/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "@prefabs.tech/tsconfig/fastify.json", + "compilerOptions": { + "outDir": "./dist", + }, + "include": [ + "src/**/*.ts" + ] +} diff --git a/packages/error-handler/vite.config.ts b/packages/error-handler/vite.config.ts new file mode 100644 index 000000000..813ef1e4c --- /dev/null +++ b/packages/error-handler/vite.config.ts @@ -0,0 +1,50 @@ +import { resolve, dirname } from "node:path"; +import { fileURLToPath } from "node:url"; + +import { defineConfig, loadEnv } from "vite"; + +import { dependencies, peerDependencies } from "./package.json"; + +// https://vitejs.dev/config/ +export default defineConfig(({ mode }) => { + process.env = { ...process.env, ...loadEnv(mode, process.cwd()) }; + + return { + build: { + lib: { + entry: resolve(dirname(fileURLToPath(import.meta.url)), "src/index.ts"), + fileName: "prefabs-tech-fastify-error-handler", + name: "PrefabsTechFastifyErrorHandler", + }, + rollupOptions: { + external: [ + ...Object.keys(dependencies), + ...Object.keys(peerDependencies), + "node:http", + ], + output: { + exports: "named", + globals: { + "@fastify/sensible": "FastifySensible", + fastify: "Fastify", + "fastify-plugin": "FastifyPlugin", + "node:http": "NodeHttp", + "stack-trace": "StackTrace", + }, + }, + }, + target: "es2022", + }, + resolve: { + alias: { + "@/": new URL("src/", import.meta.url).pathname, + }, + }, + test: { + coverage: { + provider: "istanbul", + reporter: ["text", "json", "html"], + }, + }, + }; +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 552db8a36..36995490d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -99,6 +99,82 @@ importers: specifier: 3.2.4 version: 3.2.4(@types/node@20.19.9)(jiti@2.4.2)(yaml@2.7.1) + packages/error-handler: + dependencies: + '@fastify/sensible': + specifier: 6.0.3 + version: 6.0.3 + stack-trace: + specifier: 1.0.0-pre2 + version: 1.0.0-pre2 + devDependencies: + '@prefabs.tech/eslint-config': + specifier: 0.2.0 + version: 0.2.0(@typescript-eslint/eslint-plugin@8.38.0(@typescript-eslint/parser@8.38.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3))(@typescript-eslint/parser@8.38.0(eslint@8.57.1)(typescript@5.8.3))(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint-import-resolver-alias@1.1.2(eslint-plugin-import@2.32.0))(eslint-import-resolver-typescript@3.10.1)(eslint-plugin-import@2.32.0)(eslint-plugin-n@14.0.0(eslint@8.57.1))(eslint-plugin-prettier@5.5.3(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint@8.57.1)(prettier@3.6.2))(eslint-plugin-promise@7.2.1(eslint@8.57.1))(eslint-plugin-unicorn@56.0.1(eslint@8.57.1))(eslint@8.57.1)(prettier@3.6.2)(typescript@5.8.3) + '@prefabs.tech/tsconfig': + specifier: 0.2.0 + version: 0.2.0(@types/node@20.19.9) + '@types/node': + specifier: 20.19.9 + version: 20.19.9 + '@types/stack-trace': + specifier: 0.0.33 + version: 0.0.33 + '@typescript-eslint/eslint-plugin': + specifier: 8.38.0 + version: 8.38.0(@typescript-eslint/parser@8.38.0(eslint@8.57.1)(typescript@5.8.3))(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/parser': + specifier: 8.38.0 + version: 8.38.0(eslint@8.57.1)(typescript@5.8.3) + '@vitest/coverage-istanbul': + specifier: 3.2.4 + version: 3.2.4(vitest@3.2.4(@types/node@20.19.9)(jiti@2.4.2)(yaml@2.7.1)) + eslint: + specifier: 8.57.1 + version: 8.57.1 + eslint-config-prettier: + specifier: 9.1.2 + version: 9.1.2(eslint@8.57.1) + eslint-import-resolver-alias: + specifier: 1.1.2 + version: 1.1.2(eslint-plugin-import@2.32.0) + eslint-import-resolver-typescript: + specifier: 3.10.1 + version: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1) + eslint-plugin-import: + specifier: 2.32.0 + version: 2.32.0(@typescript-eslint/parser@8.38.0(eslint@8.57.1)(typescript@5.8.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-plugin-n: + specifier: 14.0.0 + version: 14.0.0(eslint@8.57.1) + eslint-plugin-prettier: + specifier: 5.5.3 + version: 5.5.3(eslint-config-prettier@9.1.2(eslint@8.57.1))(eslint@8.57.1)(prettier@3.6.2) + eslint-plugin-promise: + specifier: 7.2.1 + version: 7.2.1(eslint@8.57.1) + eslint-plugin-unicorn: + specifier: 56.0.1 + version: 56.0.1(eslint@8.57.1) + fastify: + specifier: 5.4.0 + version: 5.4.0 + fastify-plugin: + specifier: 5.0.1 + version: 5.0.1 + prettier: + specifier: 3.6.2 + version: 3.6.2 + typescript: + specifier: 5.8.3 + version: 5.8.3 + vite: + specifier: 6.3.5 + version: 6.3.5(@types/node@20.19.9)(jiti@2.4.2)(yaml@2.7.1) + vitest: + specifier: 3.2.4 + version: 3.2.4(@types/node@20.19.9)(jiti@2.4.2)(yaml@2.7.1) + packages/firebase: dependencies: firebase-admin: @@ -1257,12 +1333,6 @@ packages: cpu: [x64] os: [win32] - '@eslint-community/eslint-utils@4.4.0': - resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} - engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} - peerDependencies: - eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - '@eslint-community/eslint-utils@4.7.0': resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1323,6 +1393,9 @@ packages: '@fastify/send@3.3.1': resolution: {integrity: sha512-6pofeVwaHN+E/MAofCwDqkWUliE3i++jlD0VH/LOfU8TJlCkMUSgKvA9bawDdVXxjve7XrdYMyDmkiYaoGWEtA==} + '@fastify/sensible@6.0.3': + resolution: {integrity: sha512-Iyn8698hp/e5+v8SNBBruTa7UfrMEP52R16dc9jMpqSyEcPsvWFQo+R6WwHCUnJiLIsuci2ZoEZ7ilrSSCPIVg==} + '@fastify/static@8.1.1': resolution: {integrity: sha512-TW9eyVHJLytZNpBlSIqd0bl1giJkEaRaPZG+5AT3L/OBKq9U8D7g/OYmc2NPQZnzPURGhMt3IAWuyVkvd2nOkQ==} @@ -2078,6 +2151,9 @@ packages: '@types/serve-static@1.15.5': resolution: {integrity: sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==} + '@types/stack-trace@0.0.33': + resolution: {integrity: sha512-O7in6531Bbvlb2KEsJ0dq0CHZvc3iWSR5ZYMtvGgnHA56VgriAN/AU2LorfmcvAl2xc9N5fbCTRyMRRl8nd74g==} + '@types/tough-cookie@4.0.5': resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} @@ -2797,15 +2873,6 @@ packages: supports-color: optional: true - debug@4.3.6: - resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - debug@4.3.7: resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} engines: {node: '>=6.0'} @@ -3389,6 +3456,10 @@ packages: resolution: {integrity: sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==} engines: {node: '>= 0.12'} + forwarded@0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} + fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -3802,10 +3873,6 @@ packages: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - is-core-module@2.15.1: - resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} - engines: {node: '>= 0.4'} - is-core-module@2.16.1: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} @@ -4237,6 +4304,10 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + media-typer@0.3.0: + resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} + engines: {node: '>= 0.6'} + mensch@0.3.4: resolution: {integrity: sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==} @@ -4442,9 +4513,6 @@ packages: resolution: {integrity: sha512-8RGlznQx/Nb1xC3xKUFXHWov7pn7JdH++YVwlr6SLT6k3ft1h+ImGqZdVudbdKruFckIq9wheq9s4hgCivJDow==} engines: {node: '>=16'} - ms@2.1.2: - resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -5429,6 +5497,10 @@ packages: stable-hash@0.0.5: resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} + stack-trace@1.0.0-pre2: + resolution: {integrity: sha512-2ztBJRek8IVofG9DBJqdy2N5kulaacX30Nz7xmkYF6ale9WBVmIy6mFBchvGX7Vx/MyjBhx+Rcxqrj+dbOnQ6A==} + engines: {node: '>=16'} + stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} @@ -5609,10 +5681,6 @@ packages: tinyexec@1.0.1: resolution: {integrity: sha512-5uC6DDlmeqiOwCPmK9jMSdOuZTh8bU39Ys6yidB+UTt5hfZUPGAypSgFRiEp+jbi9qH40BLDvy85jIU88wKSqw==} - tinyglobby@0.2.13: - resolution: {integrity: sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==} - engines: {node: '>=12.0.0'} - tinyglobby@0.2.14: resolution: {integrity: sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==} engines: {node: '>=12.0.0'} @@ -5736,6 +5804,10 @@ packages: resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} engines: {node: '>=8'} + type-is@1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} + typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} @@ -5841,6 +5913,10 @@ packages: resolution: {integrity: sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==} engines: {node: '>= 0.10'} + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + vite-node@3.2.4: resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} @@ -6852,11 +6928,6 @@ snapshots: '@esbuild/win32-x64@0.25.4': optional: true - '@eslint-community/eslint-utils@4.4.0(eslint@8.57.1)': - dependencies: - eslint: 8.57.1 - eslint-visitor-keys: 3.4.3 - '@eslint-community/eslint-utils@4.7.0(eslint@8.57.1)': dependencies: eslint: 8.57.1 @@ -6867,7 +6938,7 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: ajv: 6.12.6 - debug: 4.4.0 + debug: 4.4.1 espree: 9.6.1 globals: 13.24.0 ignore: 5.3.2 @@ -6939,6 +7010,16 @@ snapshots: http-errors: 2.0.0 mime: 3.0.0 + '@fastify/sensible@6.0.3': + dependencies: + '@lukeed/ms': 2.0.2 + dequal: 2.0.3 + fastify-plugin: 5.0.1 + forwarded: 0.2.0 + http-errors: 2.0.0 + type-is: 1.6.18 + vary: 1.1.2 + '@fastify/static@8.1.1': dependencies: '@fastify/accept-negotiator': 2.0.1 @@ -7101,7 +7182,7 @@ snapshots: '@humanwhocodes/config-array@0.13.0': dependencies: '@humanwhocodes/object-schema': 2.0.3 - debug: 4.4.0 + debug: 4.4.1 minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -7926,6 +8007,8 @@ snapshots: '@types/mime': 3.0.4 '@types/node': 22.9.0 + '@types/stack-trace@0.0.33': {} + '@types/tough-cookie@4.0.5': optional: true @@ -8751,10 +8834,6 @@ snapshots: dependencies: ms: 2.1.3 - debug@4.3.6: - dependencies: - ms: 2.1.2 - debug@4.3.7: dependencies: ms: 2.1.3 @@ -9152,7 +9231,7 @@ snapshots: eslint-plugin-es: 4.1.0(eslint@8.57.1) eslint-utils: 3.0.0(eslint@8.57.1) ignore: 5.3.2 - is-core-module: 2.15.1 + is-core-module: 2.16.1 minimatch: 3.1.2 resolve: 1.22.8 semver: 6.3.1 @@ -9177,13 +9256,13 @@ snapshots: eslint-plugin-promise@7.2.1(eslint@8.57.1): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) + '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1) eslint: 8.57.1 eslint-plugin-unicorn@56.0.1(eslint@8.57.1): dependencies: '@babel/helper-validator-identifier': 7.25.9 - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) + '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1) ci-info: 4.0.0 clean-regexp: 1.0.0 core-js-compat: 3.38.1 @@ -9197,7 +9276,7 @@ snapshots: read-pkg-up: 7.0.1 regexp-tree: 0.1.27 regjsparser: 0.10.0 - semver: 7.6.3 + semver: 7.7.2 strip-indent: 3.0.0 eslint-scope@7.2.2: @@ -9224,7 +9303,7 @@ snapshots: eslint@8.57.1: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) + '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1) '@eslint-community/regexpp': 4.10.0 '@eslint/eslintrc': 2.1.4 '@eslint/js': 8.57.1 @@ -9235,7 +9314,7 @@ snapshots: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.6 + debug: 4.4.1 doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -9391,7 +9470,7 @@ snapshots: process-warning: 5.0.0 rfdc: 1.4.1 secure-json-parse: 4.0.0 - semver: 7.6.3 + semver: 7.7.2 toad-cache: 3.7.0 fastparallel@2.4.1: @@ -9498,6 +9577,8 @@ snapshots: mime-types: 2.1.35 optional: true + forwarded@0.2.0: {} + fs.realpath@1.0.0: {} fsevents@2.3.3: @@ -10023,10 +10104,6 @@ snapshots: is-callable@1.2.7: {} - is-core-module@2.15.1: - dependencies: - hasown: 2.0.2 - is-core-module@2.16.1: dependencies: hasown: 2.0.2 @@ -10279,7 +10356,7 @@ snapshots: jws: 3.2.2 lodash: 4.17.21 ms: 2.1.3 - semver: 7.6.3 + semver: 7.7.2 juice@10.0.0: dependencies: @@ -10308,7 +10385,7 @@ snapshots: dependencies: '@types/express': 4.17.21 '@types/jsonwebtoken': 9.0.5 - debug: 4.4.0 + debug: 4.4.1 jose: 4.15.4 limiter: 1.1.5 lru-memoizer: 2.2.0 @@ -10461,6 +10538,8 @@ snapshots: math-intrinsics@1.1.0: {} + media-typer@0.3.0: {} + mensch@0.3.4: {} meow@12.1.1: {} @@ -10513,13 +10592,11 @@ snapshots: braces: 3.0.2 picomatch: 2.3.1 - mime-db@1.52.0: - optional: true + mime-db@1.52.0: {} mime-types@2.1.35: dependencies: mime-db: 1.52.0 - optional: true mime@2.6.0: {} @@ -10869,8 +10946,6 @@ snapshots: fastparallel: 2.4.1 qlobber: 8.0.1 - ms@2.1.2: {} - ms@2.1.3: {} mustache@4.2.0: {} @@ -11275,7 +11350,7 @@ snapshots: process-warning: 3.0.0 quick-format-unescaped: 4.0.4 real-require: 0.2.0 - safe-stable-stringify: 2.4.3 + safe-stable-stringify: 2.5.0 sonic-boom: 3.8.1 thread-stream: 2.7.0 @@ -11529,7 +11604,7 @@ snapshots: resolve@1.22.8: dependencies: - is-core-module: 2.15.1 + is-core-module: 2.16.1 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -11878,6 +11953,8 @@ snapshots: stable-hash@0.0.5: {} + stack-trace@1.0.0-pre2: {} + stackback@0.0.2: {} stacktrace-parser@0.1.10: @@ -12083,11 +12160,6 @@ snapshots: tinyexec@1.0.1: {} - tinyglobby@0.2.13: - dependencies: - fdir: 6.4.4(picomatch@4.0.2) - picomatch: 4.0.2 - tinyglobby@0.2.14: dependencies: fdir: 6.4.4(picomatch@4.0.2) @@ -12189,6 +12261,11 @@ snapshots: type-fest@0.8.1: {} + type-is@1.6.18: + dependencies: + media-typer: 0.3.0 + mime-types: 2.1.35 + typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 @@ -12316,6 +12393,8 @@ snapshots: validator@13.15.15: {} + vary@1.1.2: {} + vite-node@3.2.4(@types/node@20.19.9)(jiti@2.4.2)(yaml@2.7.1): dependencies: cac: 6.7.14 @@ -12344,7 +12423,7 @@ snapshots: picomatch: 4.0.2 postcss: 8.5.3 rollup: 4.41.0 - tinyglobby: 0.2.13 + tinyglobby: 0.2.14 optionalDependencies: '@types/node': 20.19.9 fsevents: 2.3.3