diff --git a/.eslintrc.js b/.eslintrc.js index e260d9e..f78cb5b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -4,8 +4,9 @@ module.exports = { plugins: ['@typescript-eslint'], root: true, rules: { - prettier: { - 'space-before-function-paren': ['error', 'never'] - } + 'func-call-spacing': 'off', + 'space-before-function-paren': 'off', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/ban-ts-comment': 'off' } }; diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 1b5adb7..c7c6515 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -12,7 +12,7 @@ jobs: steps: - uses: actions/setup-node@v2 with: - node-version: 16.20.0 + node-version: 18.19.0 - uses: shogo82148/actions-setup-mysql@v1.23.0 with: mysql-version: '8.0' diff --git a/.gitignore b/.gitignore index 9738884..c3e50f4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ node_modules dist .DS_Store .idea -.env \ No newline at end of file +.env +.next \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index b6c4d36..62ec7d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.8.0] + +### Added + +- Optional `enableMetricsServer` option to enable or disable the metrics server. +- Optional `enabled` option to conditionally enable or disable OpenAPM. +- Exposed `getMetrics` function to get the metrics in prometheus exposition format. +- Add support to instrument applications based on `Next.js` framework in Node.js environment. +- Automatically add Prisma metrics if they are available. + ## [0.7.0] - 2024-04-01 ### Added diff --git a/README.md b/README.md index 6a2d21c..4866363 100644 --- a/README.md +++ b/README.md @@ -9,8 +9,9 @@ An APM solution based on metrics and open-source tools such as Prometheus and Gr 1. [Installation](#installation) 2. [Usage](#usage) 3. [Options](#options) -4. [Setup Locally](#setup-locally) -5. [Grafana Dashboard View](#grafana-dashboard-view) +4. [API Reference](#api-reference) +5. [Setup Locally](#setup-locally) +6. [Grafana Dashboard View](#grafana-dashboard-view) ## Installation @@ -51,6 +52,7 @@ process.on('SIGTERM', gracefullyShutdown); 1. [Express](#express) 2. [MySQL](#mysql) 3. [NestJS](#nestjs) +4. [Next.js][#nextjs] ### Express @@ -83,6 +85,16 @@ OpenAPM currently supports RED Metrics for NestJS v4 and above. openapm.instrument('nestjs'); ``` +### Next.js + +OpenAPM supports RED metrics for both pages and app router in a Next.js application. + +```js +openapm.instrument('nextjs'); +``` + +> Note: You can only use the library if Next.js runs in a Node.js environment. Since OpenAPM relies on prom-client for capturing metrics data, a serverless environment might not be able persist them. + ## Options ### Usage @@ -147,6 +159,8 @@ const openapm = new OpenAPM({ 9. `levitateConfig`: (Optional) Configuration for Levitate TSDB. Adding this configuration will enable the [Change Events](https://docs.last9.io/docs/change-events). +10. `enableMetricsServer`: (Optional) Defaults to `true`. When set to `false` the OpenAPM won't start a metrics server. To get the metrics users can rely on the `.getMetrics()` function. + ```js { ... @@ -161,6 +175,48 @@ const openapm = new OpenAPM({ } ``` +## API Reference + +1. `instrument`: Used to instrument supported technologies. Refer the [usage][#usage] section. + +2. `getMetrics`: Returns a Promise of string which contains metrics in Prometheus exposition format. You can use this function to expose a metrics endpoint if `enableMetricsServer` is set to false. For example, + +```js +const openapm = new OpenAPM({ + enableMetricsServer: false +}); + +openapm.instrument('express'); + +const app = express(); + +app.get('/metrics', async (_, res) => { + const metrics = await openapm.getMetrics(); + res.setHeader('Content-Type', 'text/plain; version=0.0.4; charset=utf-8'); + res.end(metrics); +}); +``` + +3. `shutdown`: Returns a promise which is resolved after the cleanup in OpenAPM. The cleanup includes closing the metrics server if it has started and cleared the prom-client register. + +```js +const gracefullyShutdown = () => { + server.close(() => { + openapm + .shutdown() + .then(() => { + console.log('OpenAPM shutdown successful.'); + }) + .catch((err) => { + console.log('Error shutting down OpenAPM', err); + }); + }); +}; + +process.on('SIGINT', gracefullyShutdown); +process.on('SIGTERM', gracefullyShutdown); +``` + ## Setup locally Make sure you are in the express directory. diff --git a/package-lock.json b/package-lock.json index ccfa7b0..21b1d1e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,25 +1,22 @@ { "name": "@last9/openapm", -<<<<<<< HEAD - "version": "0.5.2-alpha-1", -======= - "version": "0.5.1-alpha.2", ->>>>>>> fef21961797c357f05cdc70919ce3330ba1f8c70 + "version": "0.8.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@last9/openapm", -<<<<<<< HEAD - "version": "0.5.2-alpha-1", -======= - "version": "0.5.1-alpha.2", ->>>>>>> fef21961797c357f05cdc70919ce3330ba1f8c70 + "version": "0.8.0", "license": "Apache-2.0", "dependencies": { + "@prisma/client": "^5.13.0", + "@rollup/plugin-commonjs": "^25.0.7", + "@types/supertest": "^6.0.2", "chalk": "^4.1.2", + "chokidar": "^3.6.0", "prom-client": "^14.2.0", "response-time": "^2.3.2", + "rollup": "^4.14.3", "undici": "^5.27.2", "url-value-parser": "^2.2.0" }, @@ -31,6 +28,7 @@ "@types/express": "^4.17.17", "@types/mysql2": "github:types/mysql2", "@types/node": "^20.4.4", + "@types/react": "^18.2.79", "@types/response-time": "^2.3.5", "@typescript-eslint/eslint-plugin": "^5.61.0", "@typescript-eslint/parser": "^5.61.0", @@ -45,9 +43,12 @@ "eslint-plugin-promise": "^6.1.1", "express": "^4.18.2", "mysql2": "^3.6.0", + "next": "^14.2.2", "node-fetch": "^3.3.1", "parse-prometheus-text-format": "^1.1.1", "prettier": "2.8.8", + "prisma": "^5.13.0", + "react": "^18.2.0", "supertest": "^6.3.3", "ts-node": "^10.9.1", "tslib": "^2.6.0", @@ -62,11 +63,17 @@ "@nestjs/core": { "optional": true }, + "@prisma/client": { + "optional": true + }, "express": { "optional": true }, "mysql2": { "optional": true + }, + "next": { + "optional": true } } }, @@ -2291,8 +2298,7 @@ "node_modules/@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.20", @@ -2381,6 +2387,156 @@ } } }, + "node_modules/@next/env": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.2.tgz", + "integrity": "sha512-sk72qRfM1Q90XZWYRoJKu/UWlTgihrASiYw/scb15u+tyzcze3bOuJ/UV6TBOQEeUaxOkRqGeuGUdiiuxc5oqw==", + "dev": true + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.2.tgz", + "integrity": "sha512-3iPgMhzbalizGwHNFUcGnDhFPSgVBHQ8aqSTAMxB5BvJG0oYrDf1WOJZlbXBgunOEj/8KMVbejEur/FpvFsgFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.2.tgz", + "integrity": "sha512-x7Afi/jt0ZBRUZHTi49yyej4o8znfIMHO4RvThuoc0P+uli8Jd99y5GKjxoYunPKsXL09xBXEM1+OQy2xEL0Ag==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.2.tgz", + "integrity": "sha512-zbfPtkk7L41ODMJwSp5VbmPozPmMMQrzAc0HAUomVeVIIwlDGs/UCqLJvLNDt4jpWgc21SjjyIn762lNGrMaUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.2.tgz", + "integrity": "sha512-wPbS3pI/JU16rm3XdLvvTmlsmm1nd+sBa2ohXgBZcShX4TgOjD4R+RqHKlI1cjo/jDZKXt6OxmcU0Iys0OC/yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.2.tgz", + "integrity": "sha512-NqWOHqqq8iC9tuHvZxjQ2tX+jWy2X9y8NX2mcB4sj2bIccuCxbIZrU/ThFPZZPauygajZuVQ6zediejQHwZHwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.2.tgz", + "integrity": "sha512-lGepHhwb9sGhCcU7999+iK1ZZT+6rrIoVg40MP7DZski9GIZP80wORSbt5kJzh9v2x2ev2lxC6VgwMQT0PcgTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.2.tgz", + "integrity": "sha512-TZSh/48SfcLEQ4rD25VVn2kdIgUWmMflRX3OiyPwGNXn3NiyPqhqei/BaqCYXViIQ+6QsG9R0C8LftMqy8JPMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.2.tgz", + "integrity": "sha512-M0tBVNMEBJN2ZNQWlcekMn6pvLria7Sa2Fai5znm7CCJz4pP3lrvlSxhKdkCerk0D9E0bqx5yAo3o2Q7RrD4gA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.2.tgz", + "integrity": "sha512-a/20E/wtTJZ3Ykv3f/8F0l7TtgQa2LWHU2oNB9bsu0VjqGuGGHmm/q6waoUNQYTVPYrrlxxaHjJcDV6aiSTt/w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -2482,6 +2638,342 @@ "integrity": "sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==", "dev": true }, + "node_modules/@prisma/client": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.13.0.tgz", + "integrity": "sha512-uYdfpPncbZ/syJyiYBwGZS8Gt1PTNoErNYMuqHDa2r30rNSFtgTA/LXsSk55R7pdRTMi5pHkeP9B14K6nHmwkg==", + "hasInstallScript": true, + "engines": { + "node": ">=16.13" + }, + "peerDependencies": { + "prisma": "*" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + } + } + }, + "node_modules/@prisma/debug": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.13.0.tgz", + "integrity": "sha512-699iqlEvzyCj9ETrXhs8o8wQc/eVW+FigSsHpiskSFydhjVuwTJEfj/nIYqTaWFYuxiWQRfm3r01meuW97SZaQ==", + "devOptional": true + }, + "node_modules/@prisma/engines": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.13.0.tgz", + "integrity": "sha512-hIFLm4H1boj6CBZx55P4xKby9jgDTeDG0Jj3iXtwaaHmlD5JmiDkZhh8+DYWkTGchu+rRF36AVROLnk0oaqhHw==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/debug": "5.13.0", + "@prisma/engines-version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", + "@prisma/fetch-engine": "5.13.0", + "@prisma/get-platform": "5.13.0" + } + }, + "node_modules/@prisma/engines-version": { + "version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b.tgz", + "integrity": "sha512-AyUuhahTINGn8auyqYdmxsN+qn0mw3eg+uhkp8zwknXYIqoT3bChG4RqNY/nfDkPvzWAPBa9mrDyBeOnWSgO6A==", + "devOptional": true + }, + "node_modules/@prisma/fetch-engine": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.13.0.tgz", + "integrity": "sha512-Yh4W+t6YKyqgcSEB3odBXt7QyVSm0OQlBSldQF2SNXtmOgMX8D7PF/fvH6E6qBCpjB/yeJLy/FfwfFijoHI6sA==", + "devOptional": true, + "dependencies": { + "@prisma/debug": "5.13.0", + "@prisma/engines-version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", + "@prisma/get-platform": "5.13.0" + } + }, + "node_modules/@prisma/get-platform": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.13.0.tgz", + "integrity": "sha512-B/WrQwYTzwr7qCLifQzYOmQhZcFmIFhR81xC45gweInSUn2hTEbfKUPd2keAog+y5WI5xLAFNJ3wkXplvSVkSw==", + "devOptional": true, + "dependencies": { + "@prisma/debug": "5.13.0" + } + }, + "node_modules/@rollup/plugin-commonjs": { + "version": "25.0.7", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz", + "integrity": "sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "glob": "^8.0.3", + "is-reference": "1.2.1", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.68.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@rollup/plugin-commonjs/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.3.tgz", + "integrity": "sha512-X9alQ3XM6I9IlSlmC8ddAvMSyG1WuHk5oUnXGw+yUBs3BFoTizmG1La/Gr8fVJvDWAq+zlYTZ9DBgrlKRVY06g==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.3.tgz", + "integrity": "sha512-eQK5JIi+POhFpzk+LnjKIy4Ks+pwJ+NXmPxOCSvOKSNRPONzKuUvWE+P9JxGZVxrtzm6BAYMaL50FFuPe0oWMQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.3.tgz", + "integrity": "sha512-Od4vE6f6CTT53yM1jgcLqNfItTsLt5zE46fdPaEmeFHvPs5SjZYlLpHrSiHEKR1+HdRfxuzXHjDOIxQyC3ptBA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.3.tgz", + "integrity": "sha512-0IMAO21axJeNIrvS9lSe/PGthc8ZUS+zC53O0VhF5gMxfmcKAP4ESkKOCwEi6u2asUrt4mQv2rjY8QseIEb1aw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.3.tgz", + "integrity": "sha512-ge2DC7tHRHa3caVEoSbPRJpq7azhG+xYsd6u2MEnJ6XzPSzQsTKyXvh6iWjXRf7Rt9ykIUWHtl0Uz3T6yXPpKw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.14.3.tgz", + "integrity": "sha512-ljcuiDI4V3ySuc7eSk4lQ9wU8J8r8KrOUvB2U+TtK0TiW6OFDmJ+DdIjjwZHIw9CNxzbmXY39wwpzYuFDwNXuw==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.3.tgz", + "integrity": "sha512-Eci2us9VTHm1eSyn5/eEpaC7eP/mp5n46gTRB3Aar3BgSvDQGJZuicyq6TsH4HngNBgVqC5sDYxOzTExSU+NjA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.3.tgz", + "integrity": "sha512-UrBoMLCq4E92/LCqlh+blpqMz5h1tJttPIniwUgOFJyjWI1qrtrDhhpHPuFxULlUmjFHfloWdixtDhSxJt5iKw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.3.tgz", + "integrity": "sha512-5aRjvsS8q1nWN8AoRfrq5+9IflC3P1leMoy4r2WjXyFqf3qcqsxRCfxtZIV58tCxd+Yv7WELPcO9mY9aeQyAmw==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.3.tgz", + "integrity": "sha512-sk/Qh1j2/RJSX7FhEpJn8n0ndxy/uf0kI/9Zc4b1ELhqULVdTfN6HL31CDaTChiBAOgLcsJ1sgVZjWv8XNEsAQ==", + "cpu": [ + "riscv64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.3.tgz", + "integrity": "sha512-jOO/PEaDitOmY9TgkxF/TQIjXySQe5KVYB57H/8LRP/ux0ZoO8cSHCX17asMSv3ruwslXW/TLBcxyaUzGRHcqg==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.3.tgz", + "integrity": "sha512-8ybV4Xjy59xLMyWo3GCfEGqtKV5M5gCSrZlxkPGvEPCGDLNla7v48S662HSGwRd6/2cSneMQWiv+QzcttLrrOA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.3.tgz", + "integrity": "sha512-s+xf1I46trOY10OqAtZ5Rm6lzHre/UiLA1J2uOhCFXWkbZrJRkYBPO6FhvGfHmdtQ3Bx793MNa7LvoWFAm93bg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.3.tgz", + "integrity": "sha512-+4h2WrGOYsOumDQ5S2sYNyhVfrue+9tc9XcLWLh+Kw3UOxAvrfOrSMFon60KspcDdytkNDh7K2Vs6eMaYImAZg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.3.tgz", + "integrity": "sha512-T1l7y/bCeL/kUwh9OD4PQT4aM7Bq43vX05htPJJ46RTI4r5KNt6qJRzAfNfM+OYMNEVBWQzR2Gyk+FXLZfogGw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.3.tgz", + "integrity": "sha512-/BypzV0H1y1HzgYpxqRaXGBRqfodgoBBCcsrujT6QRcakDQdfU+Lq9PENPh5jB4I44YWq+0C2eHsHya+nZY1sA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -2687,11 +3179,21 @@ } }, "node_modules/@swc/counter": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz", - "integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", "dev": true }, + "node_modules/@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "dev": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, "node_modules/@swc/types": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", @@ -2756,6 +3258,16 @@ "@types/node": "*" } }, + "node_modules/@types/cookiejar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", + "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==" + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, "node_modules/@types/express": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.20.tgz", @@ -2798,6 +3310,11 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/methods": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", + "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==" + }, "node_modules/@types/mime": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.4.tgz", @@ -2823,11 +3340,16 @@ "version": "20.8.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.9.tgz", "integrity": "sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg==", - "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, + "node_modules/@types/prop-types": { + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "dev": true + }, "node_modules/@types/qs": { "version": "6.9.9", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.9.tgz", @@ -2840,6 +3362,16 @@ "integrity": "sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA==", "dev": true }, + "node_modules/@types/react": { + "version": "18.2.79", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz", + "integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==", + "dev": true, + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, "node_modules/@types/response-time": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/@types/response-time/-/response-time-2.3.7.tgz", @@ -2877,6 +3409,25 @@ "@types/node": "*" } }, + "node_modules/@types/superagent": { + "version": "8.1.6", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.6.tgz", + "integrity": "sha512-yzBOv+6meEHSzV2NThYYOA6RtqvPr3Hbob9ZLp3i07SH27CrYVfm8CrF7ydTmidtelsFiKx2I4gZAiAOamGgvQ==", + "dependencies": { + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", + "@types/node": "*" + } + }, + "node_modules/@types/supertest": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.2.tgz", + "integrity": "sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==", + "dependencies": { + "@types/methods": "^1.1.4", + "@types/superagent": "^8.1.0" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", @@ -3404,7 +3955,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -3634,14 +4184,12 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true, "engines": { "node": ">=8" } @@ -3704,7 +4252,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "dependencies": { "fill-range": "^7.0.1" }, @@ -3801,6 +4348,18 @@ "esbuild": ">=0.17" } }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dev": true, + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -3843,9 +4402,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001558", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001558.tgz", - "integrity": "sha512-/Et7DwLqpjS47JPEcz6VnxU9PwcIdVi0ciLXRWBQdj1XFye68pSQYpV0QtPTfUKWuOaEig+/Vez2l74eDc1tPQ==", + "version": "1.0.30001608", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001608.tgz", + "integrity": "sha512-cjUJTQkk9fQlJR2s4HMuPMvTiRggl0rAVMtthQuyOlDWuqHXqN8azLq+pi8B2TjwKJ32diHjUqRIKeFX4z1FoA==", "dev": true, "funding": [ { @@ -3957,16 +4516,9 @@ } }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -3979,6 +4531,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -3987,7 +4542,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "dependencies": { "is-glob": "^4.0.1" }, @@ -3995,6 +4549,12 @@ "node": ">= 6" } }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "dev": true + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -4031,6 +4591,11 @@ "node": ">= 6" } }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + }, "node_modules/component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -4130,6 +4695,12 @@ "node": ">= 8" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -4952,6 +5523,11 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -5162,7 +5738,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "dependencies": { "to-regex-range": "^5.0.1" }, @@ -5330,14 +5905,12 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "optional": true, "os": [ @@ -5541,6 +6114,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -5712,7 +6291,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -5721,8 +6299,7 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/internal-slot": { "version": "1.0.6", @@ -5777,7 +6354,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "dependencies": { "binary-extensions": "^2.0.0" }, @@ -5844,7 +6420,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -5853,7 +6428,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "dependencies": { "is-extglob": "^2.1.1" }, @@ -5877,7 +6451,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "engines": { "node": ">=0.12.0" } @@ -5912,6 +6485,14 @@ "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", "dev": true }, + "node_modules/is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -6202,6 +6783,18 @@ "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", "dev": true }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, "node_modules/loupe": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", @@ -6224,7 +6817,6 @@ "version": "0.30.5", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", - "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, @@ -6491,6 +7083,56 @@ "node": ">= 0.6" } }, + "node_modules/next": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.2.tgz", + "integrity": "sha512-oGwUaa2bCs47FbuxWMpOoXtBMPYpvTPgdZr3UAo+pu7Ns00z9otmYpoeV1HEiYL06AlRQQIA/ypK526KjJfaxg==", + "dev": true, + "dependencies": { + "@next/env": "14.2.2", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": ">=18.17.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "14.2.2", + "@next/swc-darwin-x64": "14.2.2", + "@next/swc-linux-arm64-gnu": "14.2.2", + "@next/swc-linux-arm64-musl": "14.2.2", + "@next/swc-linux-x64-gnu": "14.2.2", + "@next/swc-linux-x64-musl": "14.2.2", + "@next/swc-win32-arm64-msvc": "14.2.2", + "@next/swc-win32-ia32-msvc": "14.2.2", + "@next/swc-win32-x64-msvc": "14.2.2" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -6538,7 +7180,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -6670,7 +7311,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "dependencies": { "wrappy": "1" } @@ -6840,7 +7480,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "engines": { "node": ">=8.6" }, @@ -6975,6 +7614,22 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/prisma": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.13.0.tgz", + "integrity": "sha512-kGtcJaElNRAdAGsCNykFSZ7dBKpL14Cbs+VaQ8cECxQlRPDjBlMHNFYeYt0SKovAVy2Y65JXQwB3A5+zIQwnTg==", + "devOptional": true, + "hasInstallScript": true, + "dependencies": { + "@prisma/engines": "5.13.0" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=16.13" + } + }, "node_modules/prom-client": { "version": "14.2.0", "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-14.2.0.tgz", @@ -7073,6 +7728,32 @@ "node": ">= 0.8" } }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dev": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dev": true, + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -7083,7 +7764,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "dependencies": { "picomatch": "^2.2.1" }, @@ -7270,18 +7950,36 @@ } }, "node_modules/rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", - "dev": true, + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.3.tgz", + "integrity": "sha512-ag5tTQKYsj1bhrFC9+OEWqb5O6VYgtQDO9hPDBMmIbePwhfSr+ExlcU741t8Dhw5DkPCQf6noz0jb36D6W9/hw==", + "dependencies": { + "@types/estree": "1.0.5" + }, "bin": { "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=14.18.0", + "node": ">=18.0.0", "npm": ">=8.0.0" }, "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.14.3", + "@rollup/rollup-android-arm64": "4.14.3", + "@rollup/rollup-darwin-arm64": "4.14.3", + "@rollup/rollup-darwin-x64": "4.14.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.3", + "@rollup/rollup-linux-arm-musleabihf": "4.14.3", + "@rollup/rollup-linux-arm64-gnu": "4.14.3", + "@rollup/rollup-linux-arm64-musl": "4.14.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.3", + "@rollup/rollup-linux-riscv64-gnu": "4.14.3", + "@rollup/rollup-linux-s390x-gnu": "4.14.3", + "@rollup/rollup-linux-x64-gnu": "4.14.3", + "@rollup/rollup-linux-x64-musl": "4.14.3", + "@rollup/rollup-win32-arm64-msvc": "4.14.3", + "@rollup/rollup-win32-ia32-msvc": "4.14.3", + "@rollup/rollup-win32-x64-msvc": "4.14.3", "fsevents": "~2.3.2" } }, @@ -7376,6 +8074,16 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dev": true, + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + } + }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -7613,6 +8321,15 @@ "integrity": "sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==", "dev": true }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/string.prototype.trim": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", @@ -7712,6 +8429,29 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "dev": true, + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, "node_modules/sucrase": { "version": "3.34.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", @@ -7929,7 +8669,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "dependencies": { "is-number": "^7.0.0" }, @@ -8112,7 +8851,23 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "engines": { - "node": ">=8" + "node": ">=8" + } + }, + "node_modules/tsup/node_modules/rollup": { + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, "node_modules/tsutils": { @@ -8307,8 +9062,7 @@ "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", @@ -8508,6 +9262,22 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/vite/node_modules/rollup": { + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, "node_modules/vitest": { "version": "0.32.4", "resolved": "https://registry.npmjs.org/vitest/-/vitest-0.32.4.tgz", @@ -8694,8 +9464,7 @@ "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/yallist": { "version": "3.1.1", @@ -10192,8 +10961,7 @@ "@jridgewell/sourcemap-codec": { "version": "1.4.15", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "@jridgewell/trace-mapping": { "version": "0.3.20", @@ -10237,6 +11005,75 @@ "uid": "2.0.2" } }, + "@next/env": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.2.tgz", + "integrity": "sha512-sk72qRfM1Q90XZWYRoJKu/UWlTgihrASiYw/scb15u+tyzcze3bOuJ/UV6TBOQEeUaxOkRqGeuGUdiiuxc5oqw==", + "dev": true + }, + "@next/swc-darwin-arm64": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.2.tgz", + "integrity": "sha512-3iPgMhzbalizGwHNFUcGnDhFPSgVBHQ8aqSTAMxB5BvJG0oYrDf1WOJZlbXBgunOEj/8KMVbejEur/FpvFsgFQ==", + "dev": true, + "optional": true + }, + "@next/swc-darwin-x64": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.2.tgz", + "integrity": "sha512-x7Afi/jt0ZBRUZHTi49yyej4o8znfIMHO4RvThuoc0P+uli8Jd99y5GKjxoYunPKsXL09xBXEM1+OQy2xEL0Ag==", + "dev": true, + "optional": true + }, + "@next/swc-linux-arm64-gnu": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.2.tgz", + "integrity": "sha512-zbfPtkk7L41ODMJwSp5VbmPozPmMMQrzAc0HAUomVeVIIwlDGs/UCqLJvLNDt4jpWgc21SjjyIn762lNGrMaUA==", + "dev": true, + "optional": true + }, + "@next/swc-linux-arm64-musl": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.2.tgz", + "integrity": "sha512-wPbS3pI/JU16rm3XdLvvTmlsmm1nd+sBa2ohXgBZcShX4TgOjD4R+RqHKlI1cjo/jDZKXt6OxmcU0Iys0OC/yg==", + "dev": true, + "optional": true + }, + "@next/swc-linux-x64-gnu": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.2.tgz", + "integrity": "sha512-NqWOHqqq8iC9tuHvZxjQ2tX+jWy2X9y8NX2mcB4sj2bIccuCxbIZrU/ThFPZZPauygajZuVQ6zediejQHwZHwQ==", + "dev": true, + "optional": true + }, + "@next/swc-linux-x64-musl": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.2.tgz", + "integrity": "sha512-lGepHhwb9sGhCcU7999+iK1ZZT+6rrIoVg40MP7DZski9GIZP80wORSbt5kJzh9v2x2ev2lxC6VgwMQT0PcgTA==", + "dev": true, + "optional": true + }, + "@next/swc-win32-arm64-msvc": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.2.tgz", + "integrity": "sha512-TZSh/48SfcLEQ4rD25VVn2kdIgUWmMflRX3OiyPwGNXn3NiyPqhqei/BaqCYXViIQ+6QsG9R0C8LftMqy8JPMA==", + "dev": true, + "optional": true + }, + "@next/swc-win32-ia32-msvc": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.2.tgz", + "integrity": "sha512-M0tBVNMEBJN2ZNQWlcekMn6pvLria7Sa2Fai5znm7CCJz4pP3lrvlSxhKdkCerk0D9E0bqx5yAo3o2Q7RrD4gA==", + "dev": true, + "optional": true + }, + "@next/swc-win32-x64-msvc": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.2.tgz", + "integrity": "sha512-a/20E/wtTJZ3Ykv3f/8F0l7TtgQa2LWHU2oNB9bsu0VjqGuGGHmm/q6waoUNQYTVPYrrlxxaHjJcDV6aiSTt/w==", + "dev": true, + "optional": true + }, "@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -10313,6 +11150,205 @@ "integrity": "sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==", "dev": true }, + "@prisma/client": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-5.13.0.tgz", + "integrity": "sha512-uYdfpPncbZ/syJyiYBwGZS8Gt1PTNoErNYMuqHDa2r30rNSFtgTA/LXsSk55R7pdRTMi5pHkeP9B14K6nHmwkg==", + "requires": {} + }, + "@prisma/debug": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-5.13.0.tgz", + "integrity": "sha512-699iqlEvzyCj9ETrXhs8o8wQc/eVW+FigSsHpiskSFydhjVuwTJEfj/nIYqTaWFYuxiWQRfm3r01meuW97SZaQ==", + "devOptional": true + }, + "@prisma/engines": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-5.13.0.tgz", + "integrity": "sha512-hIFLm4H1boj6CBZx55P4xKby9jgDTeDG0Jj3iXtwaaHmlD5JmiDkZhh8+DYWkTGchu+rRF36AVROLnk0oaqhHw==", + "devOptional": true, + "requires": { + "@prisma/debug": "5.13.0", + "@prisma/engines-version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", + "@prisma/fetch-engine": "5.13.0", + "@prisma/get-platform": "5.13.0" + } + }, + "@prisma/engines-version": { + "version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b.tgz", + "integrity": "sha512-AyUuhahTINGn8auyqYdmxsN+qn0mw3eg+uhkp8zwknXYIqoT3bChG4RqNY/nfDkPvzWAPBa9mrDyBeOnWSgO6A==", + "devOptional": true + }, + "@prisma/fetch-engine": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-5.13.0.tgz", + "integrity": "sha512-Yh4W+t6YKyqgcSEB3odBXt7QyVSm0OQlBSldQF2SNXtmOgMX8D7PF/fvH6E6qBCpjB/yeJLy/FfwfFijoHI6sA==", + "devOptional": true, + "requires": { + "@prisma/debug": "5.13.0", + "@prisma/engines-version": "5.13.0-23.b9a39a7ee606c28e3455d0fd60e78c3ba82b1a2b", + "@prisma/get-platform": "5.13.0" + } + }, + "@prisma/get-platform": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-5.13.0.tgz", + "integrity": "sha512-B/WrQwYTzwr7qCLifQzYOmQhZcFmIFhR81xC45gweInSUn2hTEbfKUPd2keAog+y5WI5xLAFNJ3wkXplvSVkSw==", + "devOptional": true, + "requires": { + "@prisma/debug": "5.13.0" + } + }, + "@rollup/plugin-commonjs": { + "version": "25.0.7", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-25.0.7.tgz", + "integrity": "sha512-nEvcR+LRjEjsaSsc4x3XZfCCvZIaSMenZu/OiwOKGN2UhQpAYI7ru7czFvyWbErlpoGjnSX3D5Ch5FcMA3kRWQ==", + "requires": { + "@rollup/pluginutils": "^5.0.1", + "commondir": "^1.0.1", + "estree-walker": "^2.0.2", + "glob": "^8.0.3", + "is-reference": "1.2.1", + "magic-string": "^0.30.3" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "@rollup/pluginutils": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.0.tgz", + "integrity": "sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==", + "requires": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + } + }, + "@rollup/rollup-android-arm-eabi": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.3.tgz", + "integrity": "sha512-X9alQ3XM6I9IlSlmC8ddAvMSyG1WuHk5oUnXGw+yUBs3BFoTizmG1La/Gr8fVJvDWAq+zlYTZ9DBgrlKRVY06g==", + "optional": true + }, + "@rollup/rollup-android-arm64": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.3.tgz", + "integrity": "sha512-eQK5JIi+POhFpzk+LnjKIy4Ks+pwJ+NXmPxOCSvOKSNRPONzKuUvWE+P9JxGZVxrtzm6BAYMaL50FFuPe0oWMQ==", + "optional": true + }, + "@rollup/rollup-darwin-arm64": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.3.tgz", + "integrity": "sha512-Od4vE6f6CTT53yM1jgcLqNfItTsLt5zE46fdPaEmeFHvPs5SjZYlLpHrSiHEKR1+HdRfxuzXHjDOIxQyC3ptBA==", + "optional": true + }, + "@rollup/rollup-darwin-x64": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.3.tgz", + "integrity": "sha512-0IMAO21axJeNIrvS9lSe/PGthc8ZUS+zC53O0VhF5gMxfmcKAP4ESkKOCwEi6u2asUrt4mQv2rjY8QseIEb1aw==", + "optional": true + }, + "@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.3.tgz", + "integrity": "sha512-ge2DC7tHRHa3caVEoSbPRJpq7azhG+xYsd6u2MEnJ6XzPSzQsTKyXvh6iWjXRf7Rt9ykIUWHtl0Uz3T6yXPpKw==", + "optional": true + }, + "@rollup/rollup-linux-arm-musleabihf": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.14.3.tgz", + "integrity": "sha512-ljcuiDI4V3ySuc7eSk4lQ9wU8J8r8KrOUvB2U+TtK0TiW6OFDmJ+DdIjjwZHIw9CNxzbmXY39wwpzYuFDwNXuw==", + "optional": true + }, + "@rollup/rollup-linux-arm64-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.3.tgz", + "integrity": "sha512-Eci2us9VTHm1eSyn5/eEpaC7eP/mp5n46gTRB3Aar3BgSvDQGJZuicyq6TsH4HngNBgVqC5sDYxOzTExSU+NjA==", + "optional": true + }, + "@rollup/rollup-linux-arm64-musl": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.3.tgz", + "integrity": "sha512-UrBoMLCq4E92/LCqlh+blpqMz5h1tJttPIniwUgOFJyjWI1qrtrDhhpHPuFxULlUmjFHfloWdixtDhSxJt5iKw==", + "optional": true + }, + "@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.3.tgz", + "integrity": "sha512-5aRjvsS8q1nWN8AoRfrq5+9IflC3P1leMoy4r2WjXyFqf3qcqsxRCfxtZIV58tCxd+Yv7WELPcO9mY9aeQyAmw==", + "optional": true + }, + "@rollup/rollup-linux-riscv64-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.3.tgz", + "integrity": "sha512-sk/Qh1j2/RJSX7FhEpJn8n0ndxy/uf0kI/9Zc4b1ELhqULVdTfN6HL31CDaTChiBAOgLcsJ1sgVZjWv8XNEsAQ==", + "optional": true + }, + "@rollup/rollup-linux-s390x-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.3.tgz", + "integrity": "sha512-jOO/PEaDitOmY9TgkxF/TQIjXySQe5KVYB57H/8LRP/ux0ZoO8cSHCX17asMSv3ruwslXW/TLBcxyaUzGRHcqg==", + "optional": true + }, + "@rollup/rollup-linux-x64-gnu": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.3.tgz", + "integrity": "sha512-8ybV4Xjy59xLMyWo3GCfEGqtKV5M5gCSrZlxkPGvEPCGDLNla7v48S662HSGwRd6/2cSneMQWiv+QzcttLrrOA==", + "optional": true + }, + "@rollup/rollup-linux-x64-musl": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.3.tgz", + "integrity": "sha512-s+xf1I46trOY10OqAtZ5Rm6lzHre/UiLA1J2uOhCFXWkbZrJRkYBPO6FhvGfHmdtQ3Bx793MNa7LvoWFAm93bg==", + "optional": true + }, + "@rollup/rollup-win32-arm64-msvc": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.3.tgz", + "integrity": "sha512-+4h2WrGOYsOumDQ5S2sYNyhVfrue+9tc9XcLWLh+Kw3UOxAvrfOrSMFon60KspcDdytkNDh7K2Vs6eMaYImAZg==", + "optional": true + }, + "@rollup/rollup-win32-ia32-msvc": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.3.tgz", + "integrity": "sha512-T1l7y/bCeL/kUwh9OD4PQT4aM7Bq43vX05htPJJ46RTI4r5KNt6qJRzAfNfM+OYMNEVBWQzR2Gyk+FXLZfogGw==", + "optional": true + }, + "@rollup/rollup-win32-x64-msvc": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.3.tgz", + "integrity": "sha512-/BypzV0H1y1HzgYpxqRaXGBRqfodgoBBCcsrujT6QRcakDQdfU+Lq9PENPh5jB4I44YWq+0C2eHsHya+nZY1sA==", + "optional": true + }, "@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", @@ -10410,11 +11446,21 @@ "optional": true }, "@swc/counter": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.2.tgz", - "integrity": "sha512-9F4ys4C74eSTEUNndnER3VJ15oru2NumfQxS8geE+f3eB5xvfxpWyqE5XlVnxb/R14uoXi6SLbBwwiDSkv+XEw==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", "dev": true }, + "@swc/helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz", + "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==", + "dev": true, + "requires": { + "@swc/counter": "^0.1.3", + "tslib": "^2.4.0" + } + }, "@swc/types": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.5.tgz", @@ -10479,6 +11525,16 @@ "@types/node": "*" } }, + "@types/cookiejar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", + "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==" + }, + "@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, "@types/express": { "version": "4.17.20", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.20.tgz", @@ -10521,6 +11577,11 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "@types/methods": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", + "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==" + }, "@types/mime": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.4.tgz", @@ -10544,11 +11605,16 @@ "version": "20.8.9", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.9.tgz", "integrity": "sha512-UzykFsT3FhHb1h7yD4CA4YhBHq545JC0YnEz41xkipN88eKQtL6rSgocL5tbAP6Ola9Izm/Aw4Ora8He4x0BHg==", - "dev": true, "requires": { "undici-types": "~5.26.4" } }, + "@types/prop-types": { + "version": "15.7.12", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", + "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", + "dev": true + }, "@types/qs": { "version": "6.9.9", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.9.tgz", @@ -10561,6 +11627,16 @@ "integrity": "sha512-+0autS93xyXizIYiyL02FCY8N+KkKPhILhcUSA276HxzreZ16kl+cmwvV2qAM/PuCCwPXzOXOWhiPcw20uSFcA==", "dev": true }, + "@types/react": { + "version": "18.2.79", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz", + "integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==", + "dev": true, + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, "@types/response-time": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/@types/response-time/-/response-time-2.3.7.tgz", @@ -10598,6 +11674,25 @@ "@types/node": "*" } }, + "@types/superagent": { + "version": "8.1.6", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.6.tgz", + "integrity": "sha512-yzBOv+6meEHSzV2NThYYOA6RtqvPr3Hbob9ZLp3i07SH27CrYVfm8CrF7ydTmidtelsFiKx2I4gZAiAOamGgvQ==", + "requires": { + "@types/cookiejar": "^2.1.5", + "@types/methods": "^1.1.4", + "@types/node": "*" + } + }, + "@types/supertest": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-6.0.2.tgz", + "integrity": "sha512-137ypx2lk/wTQbW6An6safu9hXmajAifU/s7szAHLN/FeIm5w7yR0Wkl9fdJMRSHwOn4HLAI0DaB2TOORuhPDg==", + "requires": { + "@types/methods": "^1.1.4", + "@types/superagent": "^8.1.0" + } + }, "@typescript-eslint/eslint-plugin": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", @@ -10956,7 +12051,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "requires": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -11129,14 +12223,12 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", - "dev": true + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" }, "bintrees": { "version": "1.0.2", @@ -11194,7 +12286,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, "requires": { "fill-range": "^7.0.1" } @@ -11255,6 +12346,15 @@ "load-tsconfig": "^0.2.3" } }, + "busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dev": true, + "requires": { + "streamsearch": "^1.1.0" + } + }, "bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -11285,9 +12385,9 @@ "dev": true }, "caniuse-lite": { - "version": "1.0.30001558", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001558.tgz", - "integrity": "sha512-/Et7DwLqpjS47JPEcz6VnxU9PwcIdVi0ciLXRWBQdj1XFye68pSQYpV0QtPTfUKWuOaEig+/Vez2l74eDc1tPQ==", + "version": "1.0.30001608", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001608.tgz", + "integrity": "sha512-cjUJTQkk9fQlJR2s4HMuPMvTiRggl0rAVMtthQuyOlDWuqHXqN8azLq+pi8B2TjwKJ32diHjUqRIKeFX4z1FoA==", "dev": true }, "chai": { @@ -11360,10 +12460,9 @@ } }, "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", - "dev": true, + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "requires": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -11379,13 +12478,18 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "requires": { "is-glob": "^4.0.1" } } } }, + "client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "dev": true + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -11416,6 +12520,11 @@ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==" + }, "component-emitter": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", @@ -11499,6 +12608,12 @@ "which": "^2.0.1" } }, + "csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true + }, "data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", @@ -12099,6 +13214,11 @@ "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", "dev": true }, + "estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==" + }, "esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -12276,7 +13396,6 @@ "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, "requires": { "to-regex-range": "^5.0.1" } @@ -12402,14 +13521,12 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "optional": true }, "function-bind": { @@ -12546,6 +13663,12 @@ "get-intrinsic": "^1.1.3" } }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, "graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -12663,7 +13786,6 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" @@ -12672,8 +13794,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "internal-slot": { "version": "1.0.6", @@ -12716,7 +13837,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "requires": { "binary-extensions": "^2.0.0" } @@ -12758,14 +13878,12 @@ "is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" }, "is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "requires": { "is-extglob": "^2.1.1" } @@ -12779,8 +13897,7 @@ "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, "is-number-object": { "version": "1.0.7", @@ -12803,6 +13920,14 @@ "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==", "dev": true }, + "is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "requires": { + "@types/estree": "*" + } + }, "is-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -13015,6 +14140,15 @@ "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==", "dev": true }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, "loupe": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", @@ -13037,7 +14171,6 @@ "version": "0.30.5", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz", "integrity": "sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==", - "dev": true, "requires": { "@jridgewell/sourcemap-codec": "^1.4.15" } @@ -13239,6 +14372,30 @@ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true }, + "next": { + "version": "14.2.2", + "resolved": "https://registry.npmjs.org/next/-/next-14.2.2.tgz", + "integrity": "sha512-oGwUaa2bCs47FbuxWMpOoXtBMPYpvTPgdZr3UAo+pu7Ns00z9otmYpoeV1HEiYL06AlRQQIA/ypK526KjJfaxg==", + "dev": true, + "requires": { + "@next/env": "14.2.2", + "@next/swc-darwin-arm64": "14.2.2", + "@next/swc-darwin-x64": "14.2.2", + "@next/swc-linux-arm64-gnu": "14.2.2", + "@next/swc-linux-arm64-musl": "14.2.2", + "@next/swc-linux-x64-gnu": "14.2.2", + "@next/swc-linux-x64-musl": "14.2.2", + "@next/swc-win32-arm64-msvc": "14.2.2", + "@next/swc-win32-ia32-msvc": "14.2.2", + "@next/swc-win32-x64-msvc": "14.2.2", + "@swc/helpers": "0.5.5", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "graceful-fs": "^4.2.11", + "postcss": "8.4.31", + "styled-jsx": "5.1.1" + } + }, "node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", @@ -13265,8 +14422,7 @@ "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, "npm-run-path": { "version": "4.0.1", @@ -13359,7 +14515,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, "requires": { "wrappy": "1" } @@ -13486,8 +14641,7 @@ "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" }, "pirates": { "version": "4.0.6", @@ -13558,6 +14712,15 @@ } } }, + "prisma": { + "version": "5.13.0", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-5.13.0.tgz", + "integrity": "sha512-kGtcJaElNRAdAGsCNykFSZ7dBKpL14Cbs+VaQ8cECxQlRPDjBlMHNFYeYt0SKovAVy2Y65JXQwB3A5+zIQwnTg==", + "devOptional": true, + "requires": { + "@prisma/engines": "5.13.0" + } + }, "prom-client": { "version": "14.2.0", "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-14.2.0.tgz", @@ -13621,6 +14784,26 @@ "unpipe": "1.0.0" } }, + "react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "dev": true, + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "dev": true, + "peer": true, + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + } + }, "react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -13631,7 +14814,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "requires": { "picomatch": "^2.2.1" } @@ -13770,11 +14952,27 @@ } }, "rollup": { - "version": "3.29.4", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", - "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", - "dev": true, - "requires": { + "version": "4.14.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.3.tgz", + "integrity": "sha512-ag5tTQKYsj1bhrFC9+OEWqb5O6VYgtQDO9hPDBMmIbePwhfSr+ExlcU741t8Dhw5DkPCQf6noz0jb36D6W9/hw==", + "requires": { + "@rollup/rollup-android-arm-eabi": "4.14.3", + "@rollup/rollup-android-arm64": "4.14.3", + "@rollup/rollup-darwin-arm64": "4.14.3", + "@rollup/rollup-darwin-x64": "4.14.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.14.3", + "@rollup/rollup-linux-arm-musleabihf": "4.14.3", + "@rollup/rollup-linux-arm64-gnu": "4.14.3", + "@rollup/rollup-linux-arm64-musl": "4.14.3", + "@rollup/rollup-linux-powerpc64le-gnu": "4.14.3", + "@rollup/rollup-linux-riscv64-gnu": "4.14.3", + "@rollup/rollup-linux-s390x-gnu": "4.14.3", + "@rollup/rollup-linux-x64-gnu": "4.14.3", + "@rollup/rollup-linux-x64-musl": "4.14.3", + "@rollup/rollup-win32-arm64-msvc": "4.14.3", + "@rollup/rollup-win32-ia32-msvc": "4.14.3", + "@rollup/rollup-win32-x64-msvc": "4.14.3", + "@types/estree": "1.0.5", "fsevents": "~2.3.2" } }, @@ -13832,6 +15030,16 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "dev": true, + "peer": true, + "requires": { + "loose-envify": "^1.1.0" + } + }, "semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -14031,6 +15239,12 @@ "integrity": "sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==", "dev": true }, + "streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "dev": true + }, "string.prototype.trim": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", @@ -14100,6 +15314,15 @@ "acorn": "^8.10.0" } }, + "styled-jsx": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", + "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "dev": true, + "requires": { + "client-only": "0.0.1" + } + }, "sucrase": { "version": "3.34.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz", @@ -14266,7 +15489,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "requires": { "is-number": "^7.0.0" } @@ -14389,6 +15611,15 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true + }, + "rollup": { + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "dev": true, + "requires": { + "fsevents": "~2.3.2" + } } } }, @@ -14531,8 +15762,7 @@ "undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", @@ -14620,6 +15850,17 @@ "fsevents": "~2.3.2", "postcss": "^8.4.27", "rollup": "^3.27.1" + }, + "dependencies": { + "rollup": { + "version": "3.29.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", + "integrity": "sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==", + "dev": true, + "requires": { + "fsevents": "~2.3.2" + } + } } }, "vite-node": { @@ -14752,8 +15993,7 @@ "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "yallist": { "version": "3.1.1", diff --git a/package.json b/package.json index 4c1606f..9a36ca2 100644 --- a/package.json +++ b/package.json @@ -1,14 +1,14 @@ { "name": "@last9/openapm", "description": "OpenAPM for Node.js", - "version": "0.7.0", + "version": "0.8.0", "main": "dist/index.js", "module": "dist/esm/index.js", "scripts": { "build": "tsup", "build:watch": "tsup --watch", - "test": "vitest", - "test:ui": "vitest --ui" + "vitest": "vitest", + "test": "chmod +x ./run-tests.sh && ./run-tests.sh" }, "license": "Apache-2.0", "author": "Last9 team ", @@ -33,6 +33,7 @@ "@types/express": "^4.17.17", "@types/mysql2": "github:types/mysql2", "@types/node": "^20.4.4", + "@types/react": "^18.2.79", "@types/response-time": "^2.3.5", "@typescript-eslint/eslint-plugin": "^5.61.0", "@typescript-eslint/parser": "^5.61.0", @@ -47,9 +48,12 @@ "eslint-plugin-promise": "^6.1.1", "express": "^4.18.2", "mysql2": "^3.6.0", + "next": "^14.2.2", "node-fetch": "^3.3.1", "parse-prometheus-text-format": "^1.1.1", "prettier": "2.8.8", + "prisma": "^5.13.0", + "react": "^18.2.0", "supertest": "^6.3.3", "ts-node": "^10.9.1", "tslib": "^2.6.0", @@ -58,9 +62,14 @@ "vitest": "^0.32.4" }, "dependencies": { + "@prisma/client": "^5.13.0", + "@rollup/plugin-commonjs": "^25.0.7", + "@types/supertest": "^6.0.2", "chalk": "^4.1.2", + "chokidar": "^3.6.0", "prom-client": "^14.2.0", "response-time": "^2.3.2", + "rollup": "^4.14.3", "undici": "^5.27.2", "url-value-parser": "^2.2.0" }, @@ -73,6 +82,12 @@ }, "express": { "optional": true + }, + "@prisma/client": { + "optional": true + }, + "next": { + "optional": true } }, "directories": { diff --git a/playground/app.js b/playground/app.js index 175d105..dd53975 100644 --- a/playground/app.js +++ b/playground/app.js @@ -4,9 +4,9 @@ * not testing or developing for the same * */ require('dotenv').config(); -var express = require('express'); -var { OpenAPM } = require('../dist/index.js'); -var mysql2 = require('mysql2'); +const express = require('express'); +const { OpenAPM } = require('../dist/index.js'); +const mysql2 = require('mysql2'); const openapm = new OpenAPM({ extractLabels: { @@ -17,10 +17,10 @@ const openapm = new OpenAPM({ } }, levitateConfig: { - orgSlug: process.env['LEVITATE_ORG_SLUG'], - dataSourceName: process.env['LEVITATE_DATASOURCE'], + orgSlug: process.env.LEVITATE_ORG_SLUG, + dataSourceName: process.env.LEVITATE_DATASOURCE, refreshTokens: { - write: process.env['LEVITATE_WRITE_REFRESH_TOKEN'] + write: process.env.LEVITATE_WRITE_REFRESH_TOKEN } }, customPathsToMask: [/\b\d+(?:,\d+)*\b/gm], @@ -33,7 +33,7 @@ openapm.instrument('mysql'); const app = express(); const pool = mysql2.createPool( - `mysql://express-app:password@127.0.0.1/express` // If this throws an error, Change the db url to the one you're running on your machine locally or the testing instance you might have hosted. + 'mysql://express-app:password@127.0.0.1/express' // If this throws an error, Change the db url to the one you're running on your machine locally or the testing instance you might have hosted. ); app.get('/result', (req, res) => { @@ -52,7 +52,7 @@ app.get('/result', (req, res) => { }); app.get('/organizations/:org/users', (req, res) => { - console.log(req.params['org']); + console.log(req.params.org); res.status(200).json({}); }); diff --git a/playground/next/app/app-apis/[id]/route.js b/playground/next/app/app-apis/[id]/route.js new file mode 100644 index 0000000..56b64f6 --- /dev/null +++ b/playground/next/app/app-apis/[id]/route.js @@ -0,0 +1,10 @@ +import { NextResponse } from 'next/server'; + +export async function GET(request) { + return NextResponse.json({ + status: 200, + body: { + message: 'GET method called' + } + }); +} diff --git a/playground/next/app/app-apis/route.js b/playground/next/app/app-apis/route.js new file mode 100644 index 0000000..56b64f6 --- /dev/null +++ b/playground/next/app/app-apis/route.js @@ -0,0 +1,10 @@ +import { NextResponse } from 'next/server'; + +export async function GET(request) { + return NextResponse.json({ + status: 200, + body: { + message: 'GET method called' + } + }); +} diff --git a/playground/next/app/layout.js b/playground/next/app/layout.js new file mode 100644 index 0000000..8525f5f --- /dev/null +++ b/playground/next/app/layout.js @@ -0,0 +1,12 @@ +export const metadata = { + title: 'Next.js', + description: 'Generated by Next.js', +} + +export default function RootLayout({ children }) { + return ( + + {children} + + ) +} diff --git a/playground/next/app/page.js b/playground/next/app/page.js new file mode 100644 index 0000000..e63b7c0 --- /dev/null +++ b/playground/next/app/page.js @@ -0,0 +1,3 @@ +export default function Page() { + return
Page
; +} diff --git a/playground/next/app/users/[id]/delete/page.js b/playground/next/app/users/[id]/delete/page.js new file mode 100644 index 0000000..bace1d5 --- /dev/null +++ b/playground/next/app/users/[id]/delete/page.js @@ -0,0 +1,3 @@ +export default function Page({ params: { id } }) { + return
Delete: {id}
; +} diff --git a/playground/next/app/users/[id]/page.js b/playground/next/app/users/[id]/page.js new file mode 100644 index 0000000..bace1d5 --- /dev/null +++ b/playground/next/app/users/[id]/page.js @@ -0,0 +1,3 @@ +export default function Page({ params: { id } }) { + return
Delete: {id}
; +} diff --git a/playground/next/app/users/page.js b/playground/next/app/users/page.js new file mode 100644 index 0000000..6818946 --- /dev/null +++ b/playground/next/app/users/page.js @@ -0,0 +1,3 @@ +export default function Page() { + return
User
; +} diff --git a/playground/next/next.js b/playground/next/next.js new file mode 100644 index 0000000..a1779b6 --- /dev/null +++ b/playground/next/next.js @@ -0,0 +1,55 @@ +const express = require('express'); +const http = require('http'); +const next = require('next'); +const { parse } = require('url'); +const { OpenAPM } = require('../../dist/index.js'); + +const openapm = new OpenAPM({ + metricsServerPort: 9098 +}); +openapm.instrument('nextjs'); + +async function main() { + const app = express(); + const server = http.createServer(app); + + // 'dev' is a boolean that indicates whether the app should run in development mode + const dev = process.env.NODE_ENV !== 'production'; + const port = 3002; + + // 'dir' is a string that specifies the directory where the app is located + const dir = './playground/next'; + const nextApp = next({ + dev, + dir, + customServer: true, + httpServer: server, + port + }); + // openapm.instrument('nextjs', nextApp); + const handle = nextApp.getRequestHandler(); + + app.get('/metrics', async (_, res) => { + const metrics = await openapm.getMetrics(); + res.setHeader('Content-Type', 'text/plain; version=0.0.4; charset=utf-8'); + res.end(metrics); + }); + + app.all('*', async (req, res) => { + const parsedUrl = parse(req.url, true); + await handle(req, res, parsedUrl); + }); + + // 'hostname' is a string that specifies the domain name of the server + // For local development, this is typically 'localhost' + const hostname = 'localhost'; + + await nextApp.prepare(); + server.listen(port, hostname); + server.on('error', async (err) => { + console.error(err); + }); + server.once('listening', async () => {}); +} + +main(); diff --git a/run-tests.sh b/run-tests.sh new file mode 100755 index 0000000..6eda13a --- /dev/null +++ b/run-tests.sh @@ -0,0 +1,43 @@ +setupNext() { + echo "Setting up Next.js" + npx next build ./tests/nextjs +} + +setupPrisma() { + echo "Setting up Prisma" + npx prisma generate --schema=./tests/prisma/schema.prisma + npx prisma migrate dev --schema=./tests/prisma/schema.prisma --name init +} + +# Run all tests +runAllTests() { + setupNext + setupPrisma + npm run vitest +} + +# Run Next.js tests +runNextJsTests() { + setupNext + npm run vitest -t ./tests/nextjs/nextjs.test.ts +} + +# Run Prisma tests +runPrismaTests() { + setupPrisma + npm run vitest -t ./tests/prisma/*.test.ts +} + +# Check if a variable is passed +if [ "$1" = "nextjs" ]; then + # Run Next.js tests without setting up + if [ "$2" = "--no-setup" ]; then + npm run vitest -t ./tests/nextjs/nextjs.test.ts + else + runNextJsTests + fi +elif [ "$1" = "prisma" ]; then + runPrismaTests +else + runAllTests +fi \ No newline at end of file diff --git a/src/OpenAPM.ts b/src/OpenAPM.ts index abd6076..7984905 100644 --- a/src/OpenAPM.ts +++ b/src/OpenAPM.ts @@ -17,6 +17,8 @@ import { getHostIpAddress, getPackageJson, getSanitizedPath } from './utils'; import { instrumentExpress } from './clients/express'; import { instrumentMySQL } from './clients/mysql2'; import { instrumentNestFactory } from './clients/nestjs'; +import { instrumentNextjs } from './clients/nextjs'; + import { LevitateConfig, LevitateEvents } from './levitate/events'; export type ExtractFromParams = { @@ -33,6 +35,15 @@ export type DefaultLabels = | 'host'; export interface OpenAPMOptions { + /** + * Enable the OpenAPM + */ + enabled?: boolean; + /** + * Enable the metrics server + * @default true + */ + enableMetricsServer?: boolean; /** Route where the metrics will be exposed * @default "/metrics" */ @@ -63,24 +74,28 @@ export interface OpenAPMOptions { levitateConfig?: LevitateConfig; } -export type SupportedModules = 'express' | 'mysql' | 'nestjs'; +export type SupportedModules = 'express' | 'mysql' | 'nestjs' | 'nextjs'; const moduleNames = { express: 'express', mysql: 'mysql2', - nestjs: '@nestjs/core' + nestjs: '@nestjs/core', + nextjs: 'next' }; const packageJson = getPackageJson(); export class OpenAPM extends LevitateEvents { + public simpleCache: Record = {}; private path: string; private metricsServerPort: number; + private enabled: boolean; + private enableMetricsServer: boolean; readonly environment: string; readonly program: string; private defaultLabels?: Record; - private requestsCounterConfig: CounterConfiguration; - private requestDurationHistogramConfig: HistogramConfiguration; + readonly requestsCounterConfig: CounterConfiguration; + readonly requestDurationHistogramConfig: HistogramConfiguration; private requestsCounter?: Counter; private requestsDurationHistogram?: Histogram; private extractLabels?: Record; @@ -92,8 +107,10 @@ export class OpenAPM extends LevitateEvents { constructor(options?: OpenAPMOptions) { super(options); // Initializing all the options + this.enabled = options?.enabled ?? true; this.path = options?.path ?? '/metrics'; this.metricsServerPort = options?.metricsServerPort ?? 9097; + this.enableMetricsServer = options?.enableMetricsServer ?? true; this.environment = options?.environment ?? 'production'; this.program = packageJson?.name ?? ''; this.defaultLabels = options?.defaultLabels; @@ -124,8 +141,10 @@ export class OpenAPM extends LevitateEvents { this.customPathsToMask = options?.customPathsToMask; this.excludeDefaultLabels = options?.excludeDefaultLabels; - this.initiateMetricsRoute(); - this.initiatePromClient(); + if (this.enabled) { + this.initiateMetricsRoute(); + this.initiatePromClient(); + } } private getDefaultLabels = () => { @@ -164,10 +183,13 @@ export class OpenAPM extends LevitateEvents { public shutdown = async () => { return new Promise((resolve, reject) => { - console.log('Shutting down metrics server gracefully.'); + if (!this.enabled) { + resolve(undefined); + } + if (this.enableMetricsServer) { + console.log('Shutting down metrics server gracefully.'); + } this.metricsServer?.close((err) => { - promClient.register.clear(); - if (err) { reject(err); return; @@ -176,17 +198,25 @@ export class OpenAPM extends LevitateEvents { resolve(undefined); console.log('Metrics server shut down gracefully.'); }); + + promClient.register.clear(); + resolve(undefined); }); }; private initiateMetricsRoute = () => { + // Enabling metrics server runs a separate process for the metrics server that a Prometheus agent can scrape. If it is not enabled, metrics are exposed in the same process as the web application. + if (!this.enableMetricsServer) { + return; + } // Creating native http server this.metricsServer = http.createServer(async (req, res) => { // Sanitize the path const path = getSanitizedPath(req.url ?? '/'); if (path === this.path && req.method === 'GET') { res.setHeader('Content-Type', promClient.register.contentType); - return res.end(await promClient.register.metrics()); + const metrics = await this.getMetrics(); + return res.end(metrics); } else { res.statusCode = 404; res.end('404 Not found'); @@ -259,6 +289,9 @@ export class OpenAPM extends LevitateEvents { res: ServerResponse, time: number ) => { + if (!this.enabled) { + return; + } const sanitizedPathname = getSanitizedPath(req.originalUrl ?? '/'); // Extract labels from the request params const { pathname, labels: parsedLabelsFromPathname } = @@ -274,8 +307,9 @@ export class OpenAPM extends LevitateEvents { // Make sure you copy baseURL in case of nested routes. const path = req.route ? req.baseUrl + req.route?.path : pathname; + const labels: Record = { - path: path, + path, status: res.statusCode.toString(), method: req.method as string, ...parsedLabelsFromPathname @@ -304,7 +338,42 @@ export class OpenAPM extends LevitateEvents { */ public REDMiddleware = this._REDMiddleware; - public instrument(moduleName: SupportedModules) { + public getMetrics = async (): Promise => { + let metrics = ''; + if (!this.enabled) { + return metrics; + } + if ( + typeof this.simpleCache['prisma:installed'] === 'undefined' || + this.simpleCache['prisma:installed'] + ) { + try { + // TODO: Make prisma implementation more generic so that it can be used with other ORMs, DBs and libraries + const { PrismaClient } = require('@prisma/client'); + const prisma = new PrismaClient(); + const prismaMetrics = prisma ? await prisma.$metrics.prometheus() : ''; + metrics += prisma ? prismaMetrics : ''; + + this.simpleCache['prisma:installed'] = true; + await prisma.$disconnect(); + } catch (error) { + this.simpleCache['prisma:installed'] = false; + } + } + + metrics += await promClient.register.metrics(); + + if (metrics.startsWith('"') && metrics.endsWith('"')) { + metrics = metrics.slice(1, -1); + } + + return metrics.trim(); + }; + + public instrument(moduleName: SupportedModules): boolean { + if (!this.enabled) { + return false; + } try { if (moduleName === 'express') { const express = require('express'); @@ -318,9 +387,21 @@ export class OpenAPM extends LevitateEvents { const { NestFactory } = require('@nestjs/core'); instrumentNestFactory(NestFactory, this._REDMiddleware); } + if (moduleName === 'nextjs') { + const nextServer = require('next/dist/server/next-server'); + instrumentNextjs( + nextServer.default, + { + counter: this.requestsCounter, + histogram: this.requestsDurationHistogram + }, + this + ); + } + + return true; } catch (error) { if (Object.keys(moduleNames).includes(moduleName)) { - console.log(error); throw new Error( `OpenAPM couldn't import the ${moduleNames[moduleName]} package, please install it.` ); diff --git a/src/clients/nextjs.ts b/src/clients/nextjs.ts new file mode 100644 index 0000000..1225551 --- /dev/null +++ b/src/clients/nextjs.ts @@ -0,0 +1,174 @@ +import type NextNodeServer from 'next/dist/server/next-server'; +import prom, { Counter, Histogram } from 'prom-client'; +import chokidar from 'chokidar'; +import { wrap } from '../shimmer'; +import { loadManifest } from 'next/dist/server/load-manifest'; +import { join } from 'path'; +import { getRouteRegex } from 'next/dist/shared/lib/router/utils/route-regex'; +import { getRouteMatcher } from 'next/dist/shared/lib/router/utils/route-matcher'; +import OpenAPM from '../OpenAPM'; + +const DOT_NEXT = join(process.cwd(), '.next'); + +const PAGES_MANIFEST = 'server/pages-manifest.json'; +const APP_PATHS_MANIFEST = 'server/app-paths-manifest.json'; + +const PATHS_CACHE = { + value: new Set<{ + route: string; + re: RegExp; + matcher: (pathname: string | null | undefined) => + | false + | { + [param: string]: any; + }; + }>(), + setValue: async () => { + try { + const pagesManifest = (await loadManifest( + join(DOT_NEXT, PAGES_MANIFEST), + false + )) as Record; + const appPathsManifest = (await loadManifest( + join(DOT_NEXT, APP_PATHS_MANIFEST), + false + )) as Record; + + const appPathsKeys = Object.keys(appPathsManifest); + for (let i = 0; i < appPathsKeys.length; i++) { + const key = appPathsKeys[i]; + const path = key.replace( + /\/(page|not-found|layout|loading|head|route)$/, + '' + ); + const reg = getRouteRegex(path); + const matcher = getRouteMatcher(reg); + + PATHS_CACHE.value.add({ + route: path, + matcher, + re: reg.re + }); + } + + const keys = Object.keys(pagesManifest); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const reg = getRouteRegex(key); + const matcher = getRouteMatcher(reg); + + PATHS_CACHE.value.add({ + route: key, + matcher, + re: reg.re + }); + } + } catch (e) {} + }, + keepUpdated: () => { + const watcher = chokidar.watch(DOT_NEXT, { + ignoreInitial: true + }); + + watcher.on('all', () => { + PATHS_CACHE.setValue(); + }); + return watcher; + } +}; + +const parsedPathname = (url: string) => { + return url.split('?')[0]; +}; + +const wrappedHandler = ( + handler: ReturnType, + ctx: { + getParameterizedRoute: (route: string) => string; + counter?: Counter; + histogram?: Histogram; + } +) => { + return async function ( + ...args: Parameters> + ) { + const [req, res] = args; + const start = process.hrtime.bigint(); + const result = handler(...args); + if (result instanceof Promise) { + await result; + } + const end = process.hrtime.bigint(); + const duration = Number(end - start) / 1e6; + const parsedPath = ctx.getParameterizedRoute( + parsedPathname(req.url ?? '/') + ); + + ctx.counter + ?.labels( + parsedPath !== '' ? parsedPath : '/', + req.method ?? 'GET', + res.statusCode?.toString() ?? '500' + ) + .inc(); + + ctx.histogram + ?.labels( + parsedPath !== '' ? parsedPath : '/', + req.method ?? 'GET', + res.statusCode?.toString() ?? '500' + ) + .observe(duration); + + return result; + }; +}; + +export const instrumentNextjs = ( + nextServer: typeof NextNodeServer, + { counter, histogram }: { counter?: Counter; histogram?: Histogram }, + openapm: OpenAPM +) => { + const ctx = { + counter, + histogram + }; + + if (typeof ctx.counter === 'undefined') { + ctx.counter = new prom.Counter(openapm.requestsCounterConfig); + } + + if (typeof ctx.histogram === 'undefined') { + ctx.histogram = new prom.Histogram(openapm.requestDurationHistogramConfig); + } + + PATHS_CACHE.setValue(); + PATHS_CACHE.keepUpdated(); + const getParameterizedRoute = (route: string) => { + const values = Array.from(PATHS_CACHE.value); + for (let i = 0; i < values.length; i++) { + const page = values[i]; + if (page.matcher(route) !== false) { + return page.route; + } + } + + return route; + }; + + wrap(nextServer.prototype, 'getRequestHandler', function (original) { + return function ( + this: NextNodeServer['getRequestHandler'], + ...args: Parameters + ) { + const handler = original.apply(this, args) as ReturnType< + NextNodeServer['getRequestHandler'] + >; + return wrappedHandler(handler, { + getParameterizedRoute, + counter, + histogram + }); + }; + }); +}; diff --git a/src/utils.ts b/src/utils.ts index 304d22e..1b5016c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,7 +1,6 @@ import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; -import UrlValueParser from 'url-value-parser'; export const getPackageJson = () => { const packageJsonPath = path.join(process.cwd(), 'package.json'); @@ -63,3 +62,7 @@ export const maskValuesInSQLQuery = (query: string) => { return match; }); }; + +export const isCJS = () => { + return typeof exports === 'object' && typeof module !== 'undefined'; +}; diff --git a/tests/enabled.test.ts b/tests/enabled.test.ts new file mode 100644 index 0000000..8c74979 --- /dev/null +++ b/tests/enabled.test.ts @@ -0,0 +1,21 @@ +import { test, describe, beforeAll, expect } from 'vitest'; +import { OpenAPM } from '../src/OpenAPM'; + +describe('Enabled Option', () => { + let openapm: OpenAPM; + beforeAll(async () => { + openapm = new OpenAPM({ + enabled: false + }); + }); + + test('metricsServer', async () => { + expect(openapm.metricsServer).toBeUndefined(); + }); + test('instrument', async () => { + expect(openapm.instrument('express')).toBe(false); + }); + test('getMetrics', async () => { + expect(await openapm.getMetrics()).toBe(''); + }); +}); diff --git a/tests/mysql2.test.ts b/tests/mysql2.test.ts index 0e0b169..5933fbd 100644 --- a/tests/mysql2.test.ts +++ b/tests/mysql2.test.ts @@ -1,9 +1,9 @@ -import { describe, beforeAll, expect, test, vi, afterAll } from 'vitest'; -import mysql2, { Connection, Pool, PoolCluster, PoolNamespace } from 'mysql2'; +import { describe, beforeAll, expect, test, afterAll } from 'vitest'; +import mysql2, { Connection, Pool, PoolCluster } from 'mysql2'; import { instrumentMySQL, symbols } from '../src/clients/mysql2'; import prom, { Histogram } from 'prom-client'; -const connectionUri = `mysql://root@localhost:3306/test_db`; +const connectionUri = 'mysql://root@localhost:3306/test_db'; const sendTestRequest = async (conn: Connection | Pool, query: string) => { return new Promise((resolve) => { diff --git a/tests/nextjs/.eslintrc.json b/tests/nextjs/.eslintrc.json new file mode 100644 index 0000000..15b1ed9 --- /dev/null +++ b/tests/nextjs/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next" +} diff --git a/tests/nextjs/app/app-apis/[id]/route.js b/tests/nextjs/app/app-apis/[id]/route.js new file mode 100644 index 0000000..56b64f6 --- /dev/null +++ b/tests/nextjs/app/app-apis/[id]/route.js @@ -0,0 +1,10 @@ +import { NextResponse } from 'next/server'; + +export async function GET(request) { + return NextResponse.json({ + status: 200, + body: { + message: 'GET method called' + } + }); +} diff --git a/tests/nextjs/app/app-apis/route.js b/tests/nextjs/app/app-apis/route.js new file mode 100644 index 0000000..56b64f6 --- /dev/null +++ b/tests/nextjs/app/app-apis/route.js @@ -0,0 +1,10 @@ +import { NextResponse } from 'next/server'; + +export async function GET(request) { + return NextResponse.json({ + status: 200, + body: { + message: 'GET method called' + } + }); +} diff --git a/tests/nextjs/app/layout.js b/tests/nextjs/app/layout.js new file mode 100644 index 0000000..833db93 --- /dev/null +++ b/tests/nextjs/app/layout.js @@ -0,0 +1,12 @@ +export const metadata = { + title: 'Next.js', + description: 'Generated by Next.js' +}; + +export default function RootLayout({ children }) { + return ( + + {children} + + ); +} diff --git a/tests/nextjs/app/page.js b/tests/nextjs/app/page.js new file mode 100644 index 0000000..e63b7c0 --- /dev/null +++ b/tests/nextjs/app/page.js @@ -0,0 +1,3 @@ +export default function Page() { + return
Page
; +} diff --git a/tests/nextjs/app/users/[id]/delete/page.js b/tests/nextjs/app/users/[id]/delete/page.js new file mode 100644 index 0000000..bace1d5 --- /dev/null +++ b/tests/nextjs/app/users/[id]/delete/page.js @@ -0,0 +1,3 @@ +export default function Page({ params: { id } }) { + return
Delete: {id}
; +} diff --git a/tests/nextjs/app/users/[id]/page.js b/tests/nextjs/app/users/[id]/page.js new file mode 100644 index 0000000..bace1d5 --- /dev/null +++ b/tests/nextjs/app/users/[id]/page.js @@ -0,0 +1,3 @@ +export default function Page({ params: { id } }) { + return
Delete: {id}
; +} diff --git a/tests/nextjs/app/users/page.js b/tests/nextjs/app/users/page.js new file mode 100644 index 0000000..6818946 --- /dev/null +++ b/tests/nextjs/app/users/page.js @@ -0,0 +1,3 @@ +export default function Page() { + return
User
; +} diff --git a/tests/nextjs/next-env.d.ts b/tests/nextjs/next-env.d.ts new file mode 100644 index 0000000..fd36f94 --- /dev/null +++ b/tests/nextjs/next-env.d.ts @@ -0,0 +1,6 @@ +/// +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/tests/nextjs/next.config.js b/tests/nextjs/next.config.js new file mode 100644 index 0000000..f053ebf --- /dev/null +++ b/tests/nextjs/next.config.js @@ -0,0 +1 @@ +module.exports = {}; diff --git a/tests/nextjs/nextjs.test.ts b/tests/nextjs/nextjs.test.ts new file mode 100644 index 0000000..a30058e --- /dev/null +++ b/tests/nextjs/nextjs.test.ts @@ -0,0 +1,75 @@ +import { Server } from 'http'; +import next from 'next'; +import express from 'express'; +import request from 'supertest'; +import { describe, afterAll, beforeAll, test, expect } from 'vitest'; +import OpenAPM from '../../src/OpenAPM'; +import parsePrometheusTextFormat from 'parse-prometheus-text-format'; +import { resolve } from 'path'; +import { sendTestRequestNextJS } from '../utils'; + +describe('Next.js', () => { + let openapm: OpenAPM; + let server: Server; + let parsedData: Array> = []; + + beforeAll(async () => { + openapm = new OpenAPM({ + enableMetricsServer: false + }); + + openapm.instrument('nextjs'); + + const expressApp = express(); + const app = next({ + dev: false, + customServer: false, + httpServer: server, + dir: resolve(__dirname), + conf: {} + }); + + expressApp.get('/metrics', async (_, res) => { + let metrics = await openapm.getMetrics(); + res.setHeader('Content-Type', 'text/plain; version=0.0.4; charset=utf-8'); + res.end(metrics); + }); + + expressApp.all('*', (req, res) => { + return app.getRequestHandler()(req, res); + }); + + await app.prepare(); + server = expressApp.listen(3003); + + await sendTestRequestNextJS(expressApp, 3); + const res = await request(expressApp).get('/metrics'); + parsedData = parsePrometheusTextFormat(res.text); + }); + + afterAll(async () => { + await openapm.shutdown(); + server?.close(); + }); + + test('Metrics are captured', async () => { + expect(parsedData).toBeDefined(); + }); + + test('Captures Counter Metrics', async () => { + const counterMetrics = parsedData?.find( + (m) => m.name === 'http_requests_total' + )?.metrics; + expect(counterMetrics.length > 0).toBe(true); + }); + + test('Captures Histogram Metrics', async () => { + expect( + Object.keys( + parsedData?.find( + (m) => m.name === 'http_requests_duration_milliseconds' + )?.metrics[0].buckets + ).length > 0 + ).toBe(true); + }); +}); diff --git a/tests/nextjs/pages/about.tsx b/tests/nextjs/pages/about.tsx new file mode 100644 index 0000000..37112f8 --- /dev/null +++ b/tests/nextjs/pages/about.tsx @@ -0,0 +1,9 @@ +const About = () => { + return ( +
+

About

+
+ ); +}; + +export default About; diff --git a/tests/nextjs/pages/api/auth/[...nextAuth].ts b/tests/nextjs/pages/api/auth/[...nextAuth].ts new file mode 100644 index 0000000..e24a352 --- /dev/null +++ b/tests/nextjs/pages/api/auth/[...nextAuth].ts @@ -0,0 +1,5 @@ +import type { NextApiRequest, NextApiResponse } from "next"; + +export default function handler(req: NextApiRequest, res: NextApiResponse) { + res.status(200).json({ text: "Hello" }); +} diff --git a/tests/nextjs/pages/api/hello.ts b/tests/nextjs/pages/api/hello.ts new file mode 100644 index 0000000..e24a352 --- /dev/null +++ b/tests/nextjs/pages/api/hello.ts @@ -0,0 +1,5 @@ +import type { NextApiRequest, NextApiResponse } from "next"; + +export default function handler(req: NextApiRequest, res: NextApiResponse) { + res.status(200).json({ text: "Hello" }); +} diff --git a/tests/nextjs/pages/blog/[id].tsx b/tests/nextjs/pages/blog/[id].tsx new file mode 100644 index 0000000..b200bad --- /dev/null +++ b/tests/nextjs/pages/blog/[id].tsx @@ -0,0 +1,13 @@ +import { useRouter } from "next/router"; + +const Blog = () => { + const router = useRouter(); + const { id } = router.query; + return ( +
+

Blog Post {id}

+
+ ); +}; + +export default Blog; diff --git a/tests/nextjs/tsconfig.json b/tests/nextjs/tsconfig.json new file mode 100644 index 0000000..f05a9de --- /dev/null +++ b/tests/nextjs/tsconfig.json @@ -0,0 +1,35 @@ +{ + "compilerOptions": { + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "strict": false, + "noEmit": true, + "incremental": true, + "module": "esnext", + "esModuleInterop": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "plugins": [ + { + "name": "next" + } + ], + "strictNullChecks": true + }, + "include": [ + "next-env.d.ts", + ".next/types/**/*.ts", + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules" + ] +} diff --git a/tests/prisma/prisma.test.ts b/tests/prisma/prisma.test.ts new file mode 100644 index 0000000..3587d9a --- /dev/null +++ b/tests/prisma/prisma.test.ts @@ -0,0 +1,97 @@ +import express, { Express } from 'express'; +import { test, expect, describe, beforeAll, afterAll, vi } from 'vitest'; + +import OpenAPM from '../../src/OpenAPM'; +import { addRoutes, makeRequest } from '../utils'; +import { Server } from 'http'; + +class OpenAPMExtended extends OpenAPM { + public simpleCache: Record; + + constructor() { + super({ + enableMetricsServer: false + }); + this.simpleCache = {}; + } + + clearSimpleCache() { + this.simpleCache = {}; + } +} + +async function mock(mockedUri, stub) { + const { Module } = await import('module'); + + // @ts-ignore + Module._load_original = Module._load; + // @ts-ignore + Module._load = (uri, parent) => { + if (uri === mockedUri) return stub; + // @ts-ignore + return Module._load_original(uri, parent); + }; +} + +async function unmock() { + const { Module } = await import('module'); + + // @ts-ignore + Module._load = Module._load_original; + + // @ts-ignore + delete Module._load_original; +} + +describe('Prisma', () => { + let openapm: OpenAPM; + let app: Express; + let server: Server; + + vi.hoisted(async () => { + await mock('@prisma/client', { + PrismaClient: class PrismaClient { + constructor() { + throw new Error('Cannot find module "@prisma/client"'); + } + } + }); + }); + + beforeAll(async () => { + openapm = new OpenAPMExtended(); + openapm.instrument('express'); + + app = express(); + + app.get('/metrics', async (req, res) => { + res.status(200).send(await openapm.getMetrics()); + }); + + addRoutes(app); + server = app.listen(3003); + }); + + afterAll(async () => { + await openapm.shutdown(); + server.close(); + }); + + test('prisma:installed - false', async () => { + await makeRequest(app, '/api/10'); + await makeRequest(app, '/metrics'); + + expect(openapm.simpleCache['prisma:installed']).toBe(false); + }); + + test('simpleCache', async () => { + expect(openapm.simpleCache['prisma:installed']).toBe(false); + }); + + test('metrics', async () => { + await unmock(); + (openapm as OpenAPMExtended).clearSimpleCache(); + const res = await makeRequest(app, '/metrics'); + expect(res.text).toContain('prisma_client_queries_total'); + }); +}); diff --git a/tests/prisma/schema.prisma b/tests/prisma/schema.prisma new file mode 100644 index 0000000..d1a2f8a --- /dev/null +++ b/tests/prisma/schema.prisma @@ -0,0 +1,15 @@ +datasource db { + provider = "mysql" + url = "mysql://root@localhost:3306/test_db" +} + +generator client { + provider = "prisma-client-js" + previewFeatures = ["metrics"] +} + +model Todo { + id Int @id @default(autoincrement()) + title String + completed Boolean @default(false) +} diff --git a/tests/utils.ts b/tests/utils.ts index 76e3b1c..38c3396 100644 --- a/tests/utils.ts +++ b/tests/utils.ts @@ -24,17 +24,48 @@ function getRandomId() { return String(Math.floor(Math.random() * (max - min + 1)) + min); } +export const makeRequest = async (app: Express, path: string) => { + // @ts-ignore + const res = await request(app).get(path); + return res; +}; + export const sendTestRequests = async (app: Express, num: number) => { for (let index = 0; index < num; index++) { const id = getRandomId(); try { - // @ts-ignore - const res = await request(app).get(`/api/${id}`); + await makeRequest(app, `/api/${id}`); } catch (err) { throw new Error(err); } } const id = getRandomId(); - // @ts-ignore - await request(app).get(`/api/router/${id}`); + try { + await makeRequest(app, `/api/router/${id}`); + } catch (err) { + throw new Error(err); + } +}; + +export const sendTestRequestNextJS = async (app: Express, num: number) => { + const endpoints = [ + '/', + '/users', + '/users/:id', + '/app-apis', + '/app-apis/:id', + '/api/hello', + '/api/auth/login', + '/api/auth/register' + ]; + + const randomIndex = Math.floor(Math.random() * endpoints.length); + let endpoint = endpoints[randomIndex]; + + if (endpoint.includes(':id')) { + const randomId = Math.floor(Math.random() * 100); + endpoint = endpoint.replace(':id', randomId.toString()); + } + + await makeRequest(app, endpoint); }; diff --git a/tsconfig.json b/tsconfig.json index 3acedb9..697cd62 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,6 +13,7 @@ "esModuleInterop": true, "allowSyntheticDefaultImports": true, "skipLibCheck": true, + "downlevelIteration": true, "types": ["node", "mysql2", "@nestjs/core"], "outDir": "dist" }, diff --git a/tsup.config.ts b/tsup.config.ts index 5e6fa1f..ab9e067 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -9,6 +9,6 @@ export default defineConfig({ minify: true, treeshake: true, shims: true, - external: ['mysql2', '@nestjs/core'], + external: ['mysql2', '@nestjs/core', '@prisma/client', 'express', 'next'], dts: true });