diff --git a/.env.example b/.env.example index 4950dcf..e18a6b5 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,4 @@ NODE_ENV=development PORT=5000 -MONGO_URI=YOUR_MONGO_URI \ No newline at end of file +MONGO_URI=YOUR_MONGO_URI +JWT_SECRET=YOUR_JWT_SECRET \ No newline at end of file diff --git a/apps/api/config/db.ts b/apps/api/config/db.ts index 83e1a11..c1c4a55 100644 --- a/apps/api/config/db.ts +++ b/apps/api/config/db.ts @@ -3,8 +3,7 @@ import { log } from '@repo/logger' export const connect_db = async () => { try { - const conn = await mongoose.connect(process.env.MONGO_URI as string) - log(`MongoDB Connected here: ${conn.connection.name}`) + await mongoose.connect(process.env.MONGO_URI as string) } catch (error) { log(`Error: ${error}`) process.exit(1) diff --git a/apps/api/package.json b/apps/api/package.json index b671e85..3811cbe 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -16,20 +16,27 @@ }, "dependencies": { "@repo/logger": "*", + "bcrypt": "^5.1.1", "body-parser": "^1.20.2", + "cookie-parser": "^1.4.6", "cors": "^2.8.5", "express": "^4.18.2", + "jsonwebtoken": "^9.0.2", "mongoose": "^8.1.1", - "morgan": "^1.10.0" + "morgan": "^1.10.0", + "zod": "^3.22.4" }, "devDependencies": { "@repo/eslint-config": "*", "@repo/jest-presets": "*", "@repo/typescript-config": "*", + "@types/bcrypt": "^5.0.2", "@types/body-parser": "^1.19.5", + "@types/cookie-parser": "^1.4.7", "@types/cors": "^2.8.17", "@types/express": "^4.17.21", "@types/jest": "^29.5.11", + "@types/jsonwebtoken": "^9.0.5", "@types/morgan": "^1.9.9", "@types/node": "^20.10.6", "@types/supertest": "^6.0.2", diff --git a/apps/api/src/controllers/job_controller.ts b/apps/api/src/controllers/job_controller.ts index a67a928..6b93224 100644 --- a/apps/api/src/controllers/job_controller.ts +++ b/apps/api/src/controllers/job_controller.ts @@ -1,6 +1,7 @@ import { Request, Response } from 'express' -import { JOB_SCHEMA } from '@models/job_model' +import { JOB_SCHEMA } from '@/models/job_model' import { isValidObjectId } from 'mongoose' +import { log } from 'console' //* @desc Get all jobs //* route GET /api/jobs @@ -11,7 +12,7 @@ export async function get_jobs(_: Request, res: Response): Promise { const jobs = await JOB_SCHEMA.find({}).sort({ createdAt: -1 }) res.status(200).json({ jobs }) } catch (error) { - console.error('Error fetching jobs:', error) + log('Error fetching jobs:', error) res.status(500).json({ error: 'Internal server error' }) } } @@ -35,7 +36,7 @@ export async function get_job(req: Request, res: Response): Promise { } res.status(200).json(job) } catch (error) { - console.error('Error fetching job:', error) + log('Error fetching job:', error) res.status(500).json({ error: 'Internal server error' }) } } @@ -45,12 +46,13 @@ export async function get_job(req: Request, res: Response): Promise { //! @access Private export async function post_job(req: Request, res: Response): Promise { try { + // validate job data + const job_data = req.body //* post job - const jobData = req.body - const newJob = await JOB_SCHEMA.create(jobData) - res.status(201).json(newJob) + const new_job = await JOB_SCHEMA.create(job_data) + res.status(201).json(new_job) } catch (error) { - console.error('Error posting job:', error) + log('Error posting job:', error) res.status(500).json({ error: 'Internal server error' }) } } @@ -73,10 +75,10 @@ export async function delete_job(req: Request, res: Response): Promise { return } //* delete job - const deleted_job = await JOB_SCHEMA.findByIdAndDelete(id) + await JOB_SCHEMA.findByIdAndDelete(id) res.status(200).json({ message: 'Job deleted successfully' }) } catch (error) { - console.error('Error deleting job:', error) + log('Error deleting job:', error) res.status(500).json({ error: 'Internal server error' }) } } @@ -104,7 +106,7 @@ export async function update_job(req: Request, res: Response): Promise { }) res.status(200).json(updated_job) } catch (error) { - console.error('Error updating job:', error) + log('Error updating job:', error) res.status(500).json({ error: 'Internal server error' }) } } diff --git a/apps/api/src/controllers/user_controller.ts b/apps/api/src/controllers/user_controller.ts new file mode 100644 index 0000000..3d908fc --- /dev/null +++ b/apps/api/src/controllers/user_controller.ts @@ -0,0 +1,120 @@ +import { Request, Response } from 'express' +import { USER_SCHEMA } from '@/models/user_model' +import { isValidObjectId } from 'mongoose' +import { log } from 'console' +import { generate_token } from 'utils/generate_token' + +//* @desc Register user +//* route POST /api/user +//? @access Public +export async function register_user( + req: Request, + res: Response +): Promise { + try { + const user_data = req.body + // check if user email already exists + const { email } = req.body + const existing_user = await USER_SCHEMA.findOne({ email }) + if (existing_user) { + res.status(409).json({ error: 'User email already exists' }) + return + } + + //* post user + const user = await USER_SCHEMA.create(user_data) + generate_token(res, user._id.toString()) + res.status(201).json(user) + } catch (error) { + log('Error posting user:', error) + res.status(500).json({ error: 'Internal server error' }) + } +} + +//* @desc Login user +//* route GET /api/user +//! @access Public +export async function login_user(req: Request, res: Response): Promise { + try { + //* get user by email and password + const { email, password } = req.body + const user = await USER_SCHEMA.findOne({ email }) + + // check if user exists and password is correct + if (!user || !(await user.match_password(password))) { + res.status(401).json({ error: 'Incorrect email or password' }) + return + } + generate_token(res, user._id.toString()) + res.status(200).json(user) + } catch (error) { + log('Error fetching user:', error) + res.status(500).json({ error: 'Internal server error' }) + } +} + +//* @desc Logout user +//* route POST /api/user/logout +// ? @access Public +export async function logout_user(_: Request, res: Response): Promise { + try { + res.clearCookie('token').status(200).json({ message: 'User logged out' }) + } catch (error) { + log('Error logging out user:', error) + res.status(500).json({ error: 'Internal server error' }) + } +} + +//* @desc Update user +//* route PATCH /api/user/:id +//! @access Private +export async function update_user(req: Request, res: Response): Promise { + try { + const { id } = req.params + //* check if id is valid + if (!isValidObjectId(id)) { + res.status(400).json({ error: 'Invalid id' }) + return + } + //* check if user exists + const user = await USER_SCHEMA.findById(id) + if (!user) { + res.status(404).json({ error: 'user not found' }) + return + } + + //* check if user email already exists + const { email } = req.body + const existing_user = await USER_SCHEMA.findOne({ email }) + if (existing_user && existing_user._id.toString() !== id) { + res.status(409).json({ error: 'Email already exists' }) + return + } + + //* update user + const updated_user = await USER_SCHEMA.findByIdAndUpdate(id, req.body, { + new: true, + }) + res.status(200).json(updated_user) + } catch (error) { + log('Error updating user:', error) + res.status(500).json({ error: 'Internal server error' }) + } +} + +//* @desc Get User +//* route GET /api/user/profile +//! @access Private +export async function get_user(req: Request, res: Response): Promise { + try { + const user = { + name: req.user?.name, + email: req.user?.email, + _id: req.user?._id, + } + res.status(200).json(user) + } catch (error) { + log('Error fetching user:', error) + res.status(500).json({ error: 'Internal server error' }) + } +} diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index 95ae3af..762ceb0 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -1,12 +1,20 @@ import { log } from '@repo/logger' import { createServer } from './server' -import job_route from '@routes/job_route' +import { job_route, user_route } from '@/routes' +import { connect_db } from 'config/db' const port = process.env.PORT || 5001 const server = createServer() server.use('/api/v1/jobs', job_route) +server.use('/api/v1/user', user_route) -server.listen(port, () => { - log(`api running on ${port}`) -}) +connect_db() + .then(() => { + server.listen(port, () => { + log(`db connected & api running on ${port}`) + }) + }) + .catch((error) => { + log('Error connecting to db:', error) + }) diff --git a/apps/api/src/middleware/auth_middleware.ts b/apps/api/src/middleware/auth_middleware.ts new file mode 100644 index 0000000..9d8c9c3 --- /dev/null +++ b/apps/api/src/middleware/auth_middleware.ts @@ -0,0 +1,41 @@ +import { Request as ExpressRequest, Response, NextFunction } from 'express' +import jwt, { JwtPayload } from 'jsonwebtoken' +import { USER_SCHEMA } from '@/models/user_model' +import { log } from 'console' + +interface User { + name?: string + email: string + password: string +} + +interface Request extends ExpressRequest { + user?: User +} + +export async function protect_route( + req: Request, + res: Response, + next: NextFunction +) { + try { + const token = req.cookies.token + if (!token) { + res.status(401).json({ error: 'Unauthorized' }) + return + } + const secret = process.env.JWT_SECRET + if (!secret) { + res.status(500).json({ error: 'JWT secret is undefined' }) + return + } + const decoded = jwt.verify(token, secret) as JwtPayload + req.user = (await USER_SCHEMA.findById(decoded.user_id).select( + '-password' + )) as User + + next() + } catch (error) { + res.status(500).json({ error: 'Internal server error' }) + } +} diff --git a/apps/api/src/middleware/validation_middleware.ts b/apps/api/src/middleware/validation_middleware.ts new file mode 100644 index 0000000..e82651f --- /dev/null +++ b/apps/api/src/middleware/validation_middleware.ts @@ -0,0 +1,18 @@ +import { Request, Response, NextFunction } from 'express' +import { z } from 'zod' + +export function validate_schema(schema: z.AnyZodObject) { + return (req: Request, res: Response, next: NextFunction) => { + try { + schema.parse(req.body) + next() + } catch (error) { + if (error instanceof z.ZodError) { + const error_messages = error.errors.map((issue) => ({ + message: `${issue.path.join('.')} - ${issue.message}`, + })) + res.status(400).json({ error: 'Invalid data', error_messages }) + } + } + } +} diff --git a/apps/api/src/models/user_model.ts b/apps/api/src/models/user_model.ts new file mode 100644 index 0000000..b65a5ae --- /dev/null +++ b/apps/api/src/models/user_model.ts @@ -0,0 +1,37 @@ +import mongoose, { Document, Schema } from 'mongoose' +import bcrypt from 'bcrypt' + +interface IUserSchema extends Document { + name?: string + email: string + password: string + match_password: (password: string) => Promise +} + +const user_schema = new Schema( + { + name: { type: String }, + email: { type: String, required: true, unique: true }, + password: { type: String, required: true }, + }, + { + timestamps: true, + } +) + +user_schema.pre('save', async function (next) { + const user = this + if (!user.isModified('password')) { + next() + return + } + const salt = await bcrypt.genSalt(10) + this.password = await bcrypt.hash(this.password, salt) + next() +}) + +user_schema.methods.match_password = async function (entered_password: string) { + return await bcrypt.compare(entered_password, this.password) +} + +export const USER_SCHEMA = mongoose.model('User', user_schema) diff --git a/apps/api/src/routes/index.ts b/apps/api/src/routes/index.ts new file mode 100644 index 0000000..3d9245c --- /dev/null +++ b/apps/api/src/routes/index.ts @@ -0,0 +1,2 @@ +export { default as job_route } from './job_route' +export { default as user_route } from './user_route' diff --git a/apps/api/src/routes/job_route.ts b/apps/api/src/routes/job_route.ts index 7295271..607fafe 100644 --- a/apps/api/src/routes/job_route.ts +++ b/apps/api/src/routes/job_route.ts @@ -1,13 +1,14 @@ import express from 'express' +const router = express.Router() +import { validate_schema } from '@/middleware/validation_middleware' +import { JOB_VALIDATION_SCHEMA } from '@/validations/job_validation' import { delete_job, get_job, get_jobs, post_job, update_job, -} from '@controllers/job_controller' - -const router = express.Router() +} from '@/controllers/job_controller' //* @desc Get all jobs //? @access Public @@ -19,7 +20,7 @@ router.get('/:id', get_job) //* @desc Post job //! @access Private -router.post('/', post_job) +router.post('/', validate_schema(JOB_VALIDATION_SCHEMA), post_job) //* @desc Delete job //! @access Private @@ -27,6 +28,6 @@ router.delete('/:id', delete_job) //* @desc Update job //! @access Private -router.patch('/:id', update_job) +router.patch('/:id', validate_schema(JOB_VALIDATION_SCHEMA), update_job) export default router diff --git a/apps/api/src/routes/user_route.ts b/apps/api/src/routes/user_route.ts new file mode 100644 index 0000000..130ad73 --- /dev/null +++ b/apps/api/src/routes/user_route.ts @@ -0,0 +1,39 @@ +import express from 'express' +const router = express.Router() +import { validate_schema } from '@/middleware/validation_middleware' +import { USER_VALIDATION_SCHEMA } from '@/validations/user_validation' +import { + login_user, + logout_user, + register_user, + update_user, + get_user, +} from '@/controllers/user_controller' +import { protect_route } from '@/middleware/auth_middleware' + +//* @desc Create user +//? @access Public +router.post('/', validate_schema(USER_VALIDATION_SCHEMA), register_user) + +//* @desc Login user +//? @access Public +router.get('/login', validate_schema(USER_VALIDATION_SCHEMA), login_user) + +//* @desc Logout user +//? @access Public +router.post('/logout', logout_user) + +//* @desc Get User +//! @access Private +router.get('/profile', protect_route, get_user) + +//* @desc Update user +//! @access Private +router.patch( + '/:id', + protect_route, + validate_schema(USER_VALIDATION_SCHEMA), + update_user +) + +export default router diff --git a/apps/api/src/server.ts b/apps/api/src/server.ts index 62bd43b..d9bdb22 100644 --- a/apps/api/src/server.ts +++ b/apps/api/src/server.ts @@ -3,10 +3,9 @@ import express, { type Express } from 'express' import morgan from 'morgan' import cors from 'cors' import { config } from 'dotenv' -import { connect_db } from '@config/db' +import cookieParser from 'cookie-parser' config() -connect_db() export const createServer = (): Express => { const app = express() @@ -16,6 +15,7 @@ export const createServer = (): Express => { .use(urlencoded({ extended: true })) .use(json()) .use(cors()) + .use(cookieParser()) .get('/health', (_, res) => { return res.json({ ok: true }) }) diff --git a/apps/api/src/types/express/index.d.ts b/apps/api/src/types/express/index.d.ts new file mode 100644 index 0000000..0d78638 --- /dev/null +++ b/apps/api/src/types/express/index.d.ts @@ -0,0 +1,24 @@ +export {} + +declare global { + namespace Express { + export interface Request { + job?: { + id: string + title: string + description: string + company: string + location: string + salary: number + created_at: string + updated_at: string + } + user?: { + _id?: string + name?: string + email: string + password: string + } + } + } +} diff --git a/apps/api/src/validations/job_validation.ts b/apps/api/src/validations/job_validation.ts new file mode 100644 index 0000000..a5b12cb --- /dev/null +++ b/apps/api/src/validations/job_validation.ts @@ -0,0 +1,13 @@ +import { z } from 'zod' + +export const JOB_VALIDATION_SCHEMA = z.object({ + company: z.string(), + logo: z.string().optional(), + position: z.string(), + role: z.string(), + level: z.string(), + contract: z.string(), + location: z.string(), + languages: z.array(z.string()), + tools: z.array(z.string()), +}) diff --git a/apps/api/src/validations/user_validation.ts b/apps/api/src/validations/user_validation.ts new file mode 100644 index 0000000..3efbbd1 --- /dev/null +++ b/apps/api/src/validations/user_validation.ts @@ -0,0 +1,7 @@ +import { z } from 'zod' + +export const USER_VALIDATION_SCHEMA = z.object({ + name: z.string().optional(), + email: z.string().email(), + password: z.string().min(6), +}) diff --git a/apps/api/tsconfig.json b/apps/api/tsconfig.json index 84e4f60..ef3422b 100644 --- a/apps/api/tsconfig.json +++ b/apps/api/tsconfig.json @@ -7,11 +7,6 @@ "baseUrl": ".", "paths": { "@/*": ["src/*"], - "@lib/*": ["src/lib/*"], - "@controllers/*": ["src/controllers/*"], - "@models/*": ["src/models/*"], - "@config/*": ["config/*"], - "@routes/*": ["src/routes/*"], }, }, "exclude": ["node_modules"], diff --git a/apps/api/types/index.ts b/apps/api/types/index.ts deleted file mode 100644 index 6515841..0000000 --- a/apps/api/types/index.ts +++ /dev/null @@ -1,12 +0,0 @@ -export type Job = { - company: string - logo?: string - featured?: boolean - position: string - role: string - level: string - contract: string - location: string - languages: string[] - tools: string[] -} diff --git a/apps/api/types/package.json b/apps/api/types/package.json deleted file mode 100644 index ae7e391..0000000 --- a/apps/api/types/package.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "@repo/types", - "version": "1.0.0", - "description": "", - "main": "./src/index.ts", - "types": "./src/index.ts", - "exports": { - ".": "./src/index.ts" - }, - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "keywords": [], - "author": "", - "license": "MIT" -} diff --git a/apps/api/utils/generate_token.ts b/apps/api/utils/generate_token.ts new file mode 100644 index 0000000..1779022 --- /dev/null +++ b/apps/api/utils/generate_token.ts @@ -0,0 +1,21 @@ +import jwt from 'jsonwebtoken' +import { Response } from 'express' +import { log } from 'console' + +export function generate_token(res: Response, user_id: string) { + const secret = process.env.JWT_SECRET + + if (!secret) { + log('JWT_SECRET is not defined') + return + } + const token = jwt.sign({ user_id }, secret, { + expiresIn: '30m', + }) + res.cookie('token', token, { + httpOnly: true, + secure: process.env.NODE_ENV === 'production', + sameSite: 'strict', + maxAge: 30 * 60 * 1000, + }) +} diff --git a/package-lock.json b/package-lock.json index dce13f8..16317de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,20 +39,27 @@ "version": "0.0.0", "dependencies": { "@repo/logger": "*", + "bcrypt": "^5.1.1", "body-parser": "^1.20.2", + "cookie-parser": "^1.4.6", "cors": "^2.8.5", "express": "^4.18.2", + "jsonwebtoken": "^9.0.2", "mongoose": "^8.1.1", - "morgan": "^1.10.0" + "morgan": "^1.10.0", + "zod": "^3.22.4" }, "devDependencies": { "@repo/eslint-config": "*", "@repo/jest-presets": "*", "@repo/typescript-config": "*", + "@types/bcrypt": "^5.0.2", "@types/body-parser": "^1.19.5", + "@types/cookie-parser": "^1.4.7", "@types/cors": "^2.8.17", "@types/express": "^4.17.21", "@types/jest": "^29.5.11", + "@types/jsonwebtoken": "^9.0.5", "@types/morgan": "^1.9.9", "@types/node": "^20.10.6", "@types/supertest": "^6.0.2", @@ -1884,6 +1891,66 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", + "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@mapbox/node-pre-gyp/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/@microsoft/tsdoc": { "version": "0.14.2", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", @@ -2473,6 +2540,15 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/bcrypt": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@types/bcrypt/-/bcrypt-5.0.2.tgz", + "integrity": "sha512-6atioO8Y75fNcbmj0G7UjI9lXN2pQ/IGJ2FWT4a/btd0Lk9lQalHLKhkgKVZ3r+spnmWUKfbMi1GEe9wyHQfNQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/body-parser": { "version": "1.19.5", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", @@ -2501,6 +2577,15 @@ "@types/node": "*" } }, + "node_modules/@types/cookie-parser": { + "version": "1.4.7", + "resolved": "https://registry.npmjs.org/@types/cookie-parser/-/cookie-parser-1.4.7.tgz", + "integrity": "sha512-Fvuyi354Z+uayxzIGCwYTayFKocfV7TuDYZClCdIP9ckhvAu/ixDtCB6qx2TT0FKjPLf1f3P/J1rgf6lPs64mw==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, "node_modules/@types/cookiejar": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", @@ -2648,6 +2733,15 @@ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", "dev": true }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", + "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/mdast": { "version": "3.0.15", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.15.tgz", @@ -3170,7 +3264,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, "dependencies": { "debug": "4" }, @@ -3253,6 +3346,23 @@ "resolved": "apps/api", "link": true }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -3603,6 +3713,19 @@ "node": ">= 0.8" } }, + "node_modules/bcrypt": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", + "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", + "hasInstallScript": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.11", + "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -3727,6 +3850,11 @@ "node": ">=16.20.1" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -3938,6 +4066,14 @@ "fsevents": "~2.3.2" } }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } + }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", @@ -4067,6 +4203,14 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -4117,6 +4261,11 @@ "typedarray": "^0.0.6" } }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -4160,6 +4309,31 @@ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" }, + "node_modules/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-parser": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz", + "integrity": "sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==", + "dependencies": { + "cookie": "0.4.1", + "cookie-signature": "1.0.6" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, "node_modules/cookiejar": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", @@ -4380,6 +4554,11 @@ "node": ">=0.4.0" } }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" + }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", @@ -4415,6 +4594,14 @@ "node": ">=12.20" } }, + "node_modules/detect-libc": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -4531,6 +4718,14 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -6186,11 +6381,6 @@ "node": ">= 0.6" } }, - "node_modules/express/node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" - }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -6502,6 +6692,33 @@ "resolved": "apps/frontend", "link": true }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -6555,6 +6772,43 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gauge/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/gauge/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -6819,6 +7073,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" + }, "node_modules/hasown": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", @@ -6890,7 +7149,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, "dependencies": { "agent-base": "6", "debug": "4" @@ -8435,6 +8693,27 @@ "node": ">=6" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -8450,6 +8729,25 @@ "node": ">=4.0" } }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/kareem": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz", @@ -8577,6 +8875,36 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -8589,6 +8917,11 @@ "dev": true, "peer": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -9900,6 +10233,45 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/mongodb": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz", @@ -10188,6 +10560,49 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/node-addon-api": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", + "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-fetch/node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/node-fetch/node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" + }, + "node_modules/node-fetch/node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", @@ -10242,6 +10657,17 @@ "node": ">=8" } }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, "node_modules/nwsapi": { "version": "2.2.7", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.7.tgz", @@ -10557,7 +10983,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -11160,7 +11585,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -11474,8 +11898,6 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -11490,8 +11912,6 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -11501,8 +11921,6 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "dev": true, - "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -11522,8 +11940,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -11748,6 +12164,11 @@ "node": ">= 0.8.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, "node_modules/set-function-length": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.0.tgz", @@ -12010,7 +12431,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -12019,7 +12439,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -12410,6 +12829,35 @@ "node": ">=6" } }, + "node_modules/tar": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", + "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", @@ -13724,8 +14172,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/utils-merge": { "version": "1.0.1", @@ -14041,6 +14488,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wide-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -14249,6 +14722,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",