diff --git a/app/app.ts b/app/app.ts index 27250ad..4670d8d 100644 --- a/app/app.ts +++ b/app/app.ts @@ -1,4 +1,5 @@ import express from 'express' +import { toNodeHandler } from 'better-auth/node' import cookieParser from 'cookie-parser' import cors from 'cors' import helmet from 'helmet' @@ -6,6 +7,7 @@ import logger from 'morgan' import * as z from 'zod' import { env, zodConfig } from './config' +import { auth } from './lib' import { globalErrorHandler, notFoundHandler } from './middlewares' import { apiRouter } from './routes' @@ -13,8 +15,11 @@ export const app = express() z.config(zodConfig) -app.use(helmet()) app.use(cors({ origin: env.ALLOWED_ORIGINS, credentials: true })) + +app.all(`${env.API_PREFIX}/auth/*splat`, toNodeHandler(auth)) + +app.use(helmet()) app.use(logger(app.get('env') === 'development' ? 'dev' : 'combined')) app.use(cookieParser()) app.use(express.json()) diff --git a/app/config/env.config.ts b/app/config/env.config.ts index 8e9feae..01d7615 100644 --- a/app/config/env.config.ts +++ b/app/config/env.config.ts @@ -7,19 +7,15 @@ const envSchema = z.object({ CLOUDINARY_API_SECRET: z.string(), CLOUDINARY_CLOUD_NAME: z.string(), DATABASE_URL: z.string(), - FRONTEND_URL: z.url(), + BETTER_AUTH_URL: z.url(), GOOGLE_CLIENT_ID: z.string(), GOOGLE_CLIENT_SECRET: z.string(), - GOOGLE_REDIRECT_URI: z.url(), MICROSOFT_CLIENT_ID: z.string(), MICROSOFT_CLIENT_SECRET: z.string(), - MICROSOFT_REDIRECT_URI: z.url(), REDIS_USERNAME: z.string(), REDIS_PASSWORD: z.string(), REDIS_HOST: z.string(), REDIS_PORT: z.coerce.number().int().positive().min(1000).max(65535), - ACCESS_JWT_SECRET: z.string().transform(v => new TextEncoder().encode(v)), - REFRESH_JWT_SECRET: z.string().transform(v => new TextEncoder().encode(v)), PORT: z.coerce.number().int().positive().min(1000).max(65535), API_PREFIX: z.string(), NODE_ENV: z.enum(['development', 'production']).default('development'), @@ -28,11 +24,7 @@ const envSchema = z.object({ .transform(v => v.split(',')) .pipe(z.array(z.url())), RESEND_API_KEY: z.string(), - RESEND_RECEIVER: z.email(), - ACCESS_JWT_EXPIRES_IN: z.string(), - REFRESH_JWT_EXPIRES_IN: z.string(), - ACCESS_JWT_ALGORITHM: z.string(), - REFRESH_JWT_ALGORITHM: z.string() + RESEND_RECEIVER: z.email() }) // eslint-disable-next-line no-restricted-syntax diff --git a/app/controllers/auth.controller.ts b/app/controllers/auth.controller.ts deleted file mode 100644 index 2152f9c..0000000 --- a/app/controllers/auth.controller.ts +++ /dev/null @@ -1,333 +0,0 @@ -import { randomBytes } from 'crypto' -import type { SigninSchema, SignupSchema } from '@/schemas' -import type { JwtPayload, MicrosoftProfile, TypedRequestBody } from '@/types' -import type { CookieOptions, NextFunction, Request, Response } from 'express' - -import { prisma } from '@/prisma' -import { ConfidentialClientApplication } from '@azure/msal-node' -import { hash, verify } from 'argon2' -import { OAuth2Client } from 'google-auth-library' -import { Conflict, Forbidden, Unauthorized } from 'http-errors' -import { jwtVerify, SignJWT } from 'jose' -import { JWTExpired } from 'jose/errors' - -import cloudinary, { env, redisClient } from '@/config' - -const { - ACCESS_JWT_EXPIRES_IN, - REFRESH_JWT_EXPIRES_IN, - REFRESH_JWT_SECRET, - ACCESS_JWT_SECRET, - ACCESS_JWT_ALGORITHM, - REFRESH_JWT_ALGORITHM, - GOOGLE_CLIENT_ID, - GOOGLE_CLIENT_SECRET, - GOOGLE_REDIRECT_URI, - MICROSOFT_CLIENT_ID, - MICROSOFT_CLIENT_SECRET, - MICROSOFT_REDIRECT_URI, - FRONTEND_URL, - NODE_ENV -} = env - -class AuthController { - private googleClient = new OAuth2Client( - GOOGLE_CLIENT_ID, - GOOGLE_CLIENT_SECRET, - GOOGLE_REDIRECT_URI - ) - - private microsoftClient = new ConfidentialClientApplication({ - auth: { - clientId: MICROSOFT_CLIENT_ID, - clientSecret: MICROSOFT_CLIENT_SECRET, - authority: 'https://login.microsoftonline.com/consumers' - } - }) - - private readonly COOKIE_OPTIONS: CookieOptions = { - httpOnly: true, - secure: NODE_ENV === 'production', - sameSite: 'lax' - } - - private readonly ACCESS_TOKEN_NAME = 'accessToken' - private readonly REFRESH_TOKEN_NAME = 'refreshToken' - - private createSessionAndSetCookies = async ( - res: Response, - userId: string - ) => { - const session = await prisma.session.create({ data: { userId } }) - - const accessToken = await new SignJWT({ id: userId, sid: session.id }) - .setExpirationTime(ACCESS_JWT_EXPIRES_IN) - .setProtectedHeader({ alg: ACCESS_JWT_ALGORITHM }) - .sign(ACCESS_JWT_SECRET) - - const refreshToken = await new SignJWT({ id: userId, sid: session.id }) - .setExpirationTime(REFRESH_JWT_EXPIRES_IN) - .setProtectedHeader({ alg: REFRESH_JWT_ALGORITHM }) - .sign(REFRESH_JWT_SECRET) - - res.cookie(this.ACCESS_TOKEN_NAME, accessToken, { - ...this.COOKIE_OPTIONS, - maxAge: 1000 * 60 * 60 // 1 hour - }) - res.cookie(this.REFRESH_TOKEN_NAME, refreshToken, { - ...this.COOKIE_OPTIONS, - maxAge: 1000 * 60 * 60 * 24 * 7 // 7 days - }) - } - - signup = async ( - { body }: TypedRequestBody, - res: Response, - next: NextFunction - ) => { - const isUserExists = await prisma.user.findFirst({ - where: { email: body.email } - }) - - if (isUserExists) return next(Conflict('Email already exist')) - - const user = await prisma.user.create({ - data: { - ...body, - password: await hash(body.password) - } - }) - - await this.createSessionAndSetCookies(res, user.id) - - res.json({ user }) - } - - signin = async ( - { body }: TypedRequestBody, - res: Response, - next: NextFunction - ) => { - const user = await prisma.user.findFirst({ - where: { email: body.email }, - omit: { password: false } - }) - - if (!user) return next(Unauthorized('Email or password invalid')) - - const { password, ...userWithoutPassword } = user - - if (!password) return next(Unauthorized('Email or password invalid')) - - const isPasswordMatch = await verify(password, body.password) - - if (!isPasswordMatch) return next(Unauthorized('Email or password invalid')) - - await this.createSessionAndSetCookies(res, user.id) - - res.json({ user: userWithoutPassword }) - } - - googleInitiate = async (_: Request, res: Response) => { - const state = randomBytes(32).toString('hex') - - await redisClient.set(`google_oauth_state:${state}`, 'true', 'EX', 5 * 60) - - const url = this.googleClient.generateAuthUrl({ - state, - access_type: 'offline', - scope: ['profile', 'email'] - }) - - res.redirect(url) - } - - googleCallback = async (req: Request, res: Response) => { - try { - const { code, state: receivedState, error } = req.query - - if (!code || (error && error === 'access_denied')) { - return res.redirect(`${FRONTEND_URL}?error=access_denied`) - } - - const redisStateKey = `google_oauth_state:${receivedState}` - - const storedState = await redisClient.get(redisStateKey) - - if (!storedState) { - return res.redirect(`${FRONTEND_URL}?error=oauth_error`) - } - - await redisClient.del(redisStateKey) - - const { - tokens: { id_token } - } = await this.googleClient.getToken(code as string) - - if (!id_token) { - return res.redirect(`${FRONTEND_URL}?error=oauth_error`) - } - - const ticket = await this.googleClient.verifyIdToken({ - idToken: id_token, - audience: GOOGLE_CLIENT_ID - }) - - const payload = ticket.getPayload() - - if (!payload || !payload.email) { - return res.redirect(`${FRONTEND_URL}?error=oauth_error`) - } - - const { email, name = 'Guest', picture = null } = payload - - let user = await prisma.user.findFirst({ where: { email } }) - - if (!user) { - user = await prisma.user.create({ - data: { name, email, avatar: picture } - }) - } - - await this.createSessionAndSetCookies(res, user.id) - - res.redirect(FRONTEND_URL) - } catch { - res.redirect(`${FRONTEND_URL}?error=oauth_error`) - } - } - - microsoftInitiate = async (_: Request, res: Response) => { - const state = randomBytes(32).toString('hex') - - await redisClient.set( - `microsoft_oauth_state:${state}`, - 'true', - 'EX', - 5 * 60 - ) - - const url = await this.microsoftClient.getAuthCodeUrl({ - scopes: ['openid', 'profile', 'email', 'User.Read'], - redirectUri: MICROSOFT_REDIRECT_URI, - prompt: 'select_account', - state - }) - - res.redirect(url) - } - - microsoftCallback = async (req: Request, res: Response) => { - try { - const { code, state: receivedState, error } = req.query - - if (!code || (error && error === 'access_denied')) { - return res.redirect(`${FRONTEND_URL}?error=access_denied`) - } - - const redisStateKey = `microsoft_oauth_state:${receivedState}` - - const storedState = await redisClient.get(redisStateKey) - - if (!storedState) { - return res.redirect(`${FRONTEND_URL}?error=oauth_error`) - } - - await redisClient.del(redisStateKey) - - const { idTokenClaims, accessToken } = - await this.microsoftClient.acquireTokenByCode({ - code: code as string, - scopes: ['openid', 'profile', 'email', 'User.Read'], - redirectUri: MICROSOFT_REDIRECT_URI - }) - - if (!idTokenClaims) { - return res.redirect(`${FRONTEND_URL}?error=oauth_error`) - } - - const profile = idTokenClaims as MicrosoftProfile - - const name = profile.name || 'Guest' - const email = profile.email || profile.preferred_username || profile.upn - - let user = await prisma.user.findFirst({ where: { email } }) - - if (!user) { - let avatar - - const avatarResponse = await fetch( - 'https://graph.microsoft.com/v1.0/me/photo/$value', - { headers: { Authorization: `Bearer ${accessToken}` } } - ) - - if (avatarResponse.ok) { - const buffer = Buffer.from(await avatarResponse.arrayBuffer()) - const base64 = buffer.toString('base64') - - const uploadedAvatar = await cloudinary.uploader.upload( - `data:image/jpeg;base64,${base64}`, - { folder: 'TaskPro/user_avatars' } - ) - - avatar = uploadedAvatar.url - } else { - avatar = null - } - - user = await prisma.user.create({ - data: { name, email, avatar } - }) - } - - await this.createSessionAndSetCookies(res, user.id) - - res.redirect(FRONTEND_URL) - } catch { - res.redirect(`${FRONTEND_URL}?error=oauth_error`) - } - } - - refresh = async (req: Request, res: Response, next: NextFunction) => { - const refreshToken = req.cookies[this.REFRESH_TOKEN_NAME] - - if (!refreshToken) return next(Forbidden()) - - try { - const { - payload: { id, sid } - } = await jwtVerify(refreshToken, REFRESH_JWT_SECRET) - - const user = await prisma.user.findUnique({ where: { id } }) - - if (!user) return next(Forbidden()) - - const currentSession = await prisma.session.findUnique({ - where: { id: sid } - }) - - if (!currentSession) return next(Forbidden()) - - await prisma.session.delete({ where: { id: currentSession.id } }) - - await this.createSessionAndSetCookies(res, user.id) - - res.json({ message: 'Tokens refreshed successfully' }) - } catch (error) { - if (error instanceof JWTExpired) return next(Forbidden(error.code)) - - return next(Forbidden()) - } - } - - logout = async ({ session }: Request, res: Response) => { - await prisma.session.delete({ where: { id: session } }) - - res.clearCookie(this.ACCESS_TOKEN_NAME, this.COOKIE_OPTIONS) - res.clearCookie(this.REFRESH_TOKEN_NAME, this.COOKIE_OPTIONS) - - res.sendStatus(204) - } -} - -export const authController = new AuthController() diff --git a/app/controllers/index.ts b/app/controllers/index.ts index 70971b9..b30a1fc 100644 --- a/app/controllers/index.ts +++ b/app/controllers/index.ts @@ -1,4 +1,3 @@ -export { authController } from './auth.controller' export { boardController } from './board.controller' export { taskController } from './task.controller' export { labelController } from './label.controller' diff --git a/app/controllers/user.controller.ts b/app/controllers/user.controller.ts index fbc061b..65d4227 100644 --- a/app/controllers/user.controller.ts +++ b/app/controllers/user.controller.ts @@ -1,6 +1,5 @@ -import type { EditUserSchema, NeedHelpSchema } from '@/schemas' +import type { NeedHelpSchema } from '@/schemas' import type { TypedRequestBody } from '@/types' -import type { User } from '@prisma/client' import type { NextFunction, Request, Response } from 'express' import { @@ -8,10 +7,9 @@ import { supportRequestUserTemplate } from '@/emails/templates' import { prisma } from '@/prisma' -import { hash } from 'argon2' -import { Conflict, InternalServerError, NotAcceptable } from 'http-errors' +import { InternalServerError } from 'http-errors' -import cloudinary, { env, redisClient, resend } from '@/config' +import { env, redisClient, resend } from '@/config' class UserController { me = async (req: Request, res: Response) => { @@ -40,63 +38,63 @@ class UserController { } } - update = async ( - { user, body, file }: TypedRequestBody, - res: Response, - next: NextFunction - ) => { - const { id, avatarPublicId, email: userEmail } = user - const { email, password } = body - - const isEmailExists = - email && (await prisma.user.findFirst({ where: { email } })) - - if (email && email !== userEmail && isEmailExists) { - return next(Conflict('Email already exist')) - } - - const updateData: Partial = body - - if (password) { - updateData.password = await hash(password) - } - - if (file) { - const extArr = ['jpeg', 'png'] - const ext = file.mimetype.split('/').pop() - - if (!extArr.includes(ext!)) { - return next(NotAcceptable('File must have .jpeg or .png extension')) - } - - try { - const newAvatar = await cloudinary.uploader.upload(file.path, { - folder: 'TaskPro/user_avatars' - }) - - if (avatarPublicId) { - await cloudinary.uploader.destroy(avatarPublicId, { - type: 'upload', - resource_type: 'image' - }) - } - - updateData.avatar = newAvatar.url - updateData.avatarPublicId = newAvatar.public_id - } catch { - return next(InternalServerError('Uploading avatar error')) - } - } - - const updatedUser = await prisma.user.update({ - where: { id }, - data: updateData - }) - - await redisClient.del(`user:${updatedUser.id}`) - - res.json(updatedUser) - } + // update = async ( + // { user, body, file }: TypedRequestBody, + // res: Response, + // next: NextFunction + // ) => { + // const { id, avatarPublicId, email: userEmail } = user + // const { email, password } = body + + // const isEmailExists = + // email && (await prisma.user.findFirst({ where: { email } })) + + // if (email && email !== userEmail && isEmailExists) { + // return next(Conflict('Email already exist')) + // } + + // const updateData: Partial = body + + // if (password) { + // updateData.password = await hash(password) + // } + + // if (file) { + // const extArr = ['jpeg', 'png'] + // const ext = file.mimetype.split('/').pop() + + // if (!extArr.includes(ext!)) { + // return next(NotAcceptable('File must have .jpeg or .png extension')) + // } + + // try { + // const newAvatar = await cloudinary.uploader.upload(file.path, { + // folder: 'TaskPro/user_avatars' + // }) + + // if (avatarPublicId) { + // await cloudinary.uploader.destroy(avatarPublicId, { + // type: 'upload', + // resource_type: 'image' + // }) + // } + + // updateData.avatar = newAvatar.url + // updateData.avatarPublicId = newAvatar.public_id + // } catch { + // return next(InternalServerError('Uploading avatar error')) + // } + // } + + // const updatedUser = await prisma.user.update({ + // where: { id }, + // data: updateData + // }) + + // await redisClient.del(`user:${updatedUser.id}`) + + // res.json(updatedUser) + // } help = async ( { body }: TypedRequestBody, diff --git a/app/lib/auth.ts b/app/lib/auth.ts new file mode 100644 index 0000000..80f2439 --- /dev/null +++ b/app/lib/auth.ts @@ -0,0 +1,44 @@ +import { Theme } from '@prisma/client' +import { betterAuth } from 'better-auth' +import { prismaAdapter } from 'better-auth/adapters/prisma' +import { openAPI } from 'better-auth/plugins' + +import { env } from '../config' +import { prisma } from '../prisma' +import { uploadToCloudinary } from './upload-to-cloudinary' + +export const auth = betterAuth({ + appName: 'Task Pro', + baseURL: env.BETTER_AUTH_URL, + database: prismaAdapter(prisma, { provider: 'mongodb' }), + advanced: { + cookiePrefix: 'taskpro', + database: { generateId: false }, + disableOriginCheck: env.NODE_ENV !== 'production' + }, + account: { accountLinking: { trustedProviders: ['google', 'microsoft'] } }, + user: { + additionalFields: { + theme: { type: Object.values(Theme), input: false } + } + }, + emailAndPassword: { enabled: true, requireEmailVerification: false }, + socialProviders: { + google: { + clientId: env.GOOGLE_CLIENT_ID, + clientSecret: env.GOOGLE_CLIENT_SECRET + }, + microsoft: { + clientId: env.MICROSOFT_CLIENT_ID, + clientSecret: env.MICROSOFT_CLIENT_SECRET, + prompt: 'select_account', + async mapProfileToUser(profile) { + const imgUrl = await uploadToCloudinary({ file: profile.picture }) + + return { image: imgUrl } + } + } + }, + trustedOrigins: env.ALLOWED_ORIGINS, + plugins: [openAPI()] +}) diff --git a/app/lib/index.ts b/app/lib/index.ts new file mode 100644 index 0000000..2dcc036 --- /dev/null +++ b/app/lib/index.ts @@ -0,0 +1,2 @@ +export { uploadToCloudinary } from './upload-to-cloudinary' +export { auth } from './auth' diff --git a/app/lib/upload-to-cloudinary.ts b/app/lib/upload-to-cloudinary.ts new file mode 100644 index 0000000..d442d8b --- /dev/null +++ b/app/lib/upload-to-cloudinary.ts @@ -0,0 +1,29 @@ +import cloudinary from '@/config' + +type UploadInput = { + file: string + folder?: string + public_id?: string +} + +export async function uploadToCloudinary({ + file, + folder = 'uploads', + public_id +}: UploadInput) { + const result = await cloudinary.uploader.upload( + file, + { folder, public_id, resource_type: 'auto' }, + err => { + if (err) throw new Error('Cloudinary upload failed') + } + ) + + return { + url: result.secure_url, + public_id: result.public_id, + width: result.width, + height: result.height, + format: result.format + } +} diff --git a/app/middlewares/authenticate.ts b/app/middlewares/authenticate.ts deleted file mode 100644 index 77cbb58..0000000 --- a/app/middlewares/authenticate.ts +++ /dev/null @@ -1,43 +0,0 @@ -import type { JwtPayload } from '@/types' -import type { NextFunction, Request, Response } from 'express' - -import { prisma } from '@/prisma' -import { Unauthorized } from 'http-errors' -import { jwtVerify } from 'jose' -import { JWTExpired } from 'jose/errors' - -import { env } from '@/config' - -export const authenticate = async ( - req: Request, - _: Response, - next: NextFunction -) => { - const token: string = req.cookies.accessToken - - if (!token) return next(Unauthorized()) - - try { - const { - payload: { id, sid } - } = await jwtVerify(token, env.ACCESS_JWT_SECRET) - - const user = await prisma.user.findUnique({ - where: { id }, - omit: { password: false } - }) - - const session = await prisma.session.findUnique({ where: { id: sid } }) - - if (!user || !session) return next(Unauthorized()) - - req.user = user - req.session = session.id - - next() - } catch (e) { - if (e instanceof JWTExpired) return next(Unauthorized(e.code)) - - return next(Unauthorized()) - } -} diff --git a/app/middlewares/index.ts b/app/middlewares/index.ts index afb6da8..9b58ba6 100644 --- a/app/middlewares/index.ts +++ b/app/middlewares/index.ts @@ -1,4 +1,4 @@ -export { authenticate } from './authenticate' +export { requireAuth } from './require-auth' export { upload } from './multer' export { validateRequest } from './validate-request' export { notFoundHandler, globalErrorHandler } from './errorHandler' diff --git a/app/middlewares/require-auth.ts b/app/middlewares/require-auth.ts new file mode 100644 index 0000000..cbd38d3 --- /dev/null +++ b/app/middlewares/require-auth.ts @@ -0,0 +1,26 @@ +import type { NextFunction, Request, Response } from 'express' + +import { auth } from '@/lib' +import { fromNodeHeaders } from 'better-auth/node' +import { Unauthorized } from 'http-errors' + +export const requireAuth = async ( + req: Request, + _: Response, + next: NextFunction +) => { + try { + const session = await auth.api.getSession({ + headers: fromNodeHeaders(req.headers) + }) + + if (!session) return next(Unauthorized()) + + req.user = session.user + req.session = session.session + + next() + } catch { + return next(Unauthorized()) + } +} diff --git a/app/prisma/prisma.client.ts b/app/prisma/prisma.client.ts index 392b7d8..4542ddb 100644 --- a/app/prisma/prisma.client.ts +++ b/app/prisma/prisma.client.ts @@ -5,6 +5,6 @@ import { updateIgnoreNotFoundExtension } from './extensions' -export const prisma = new PrismaClient({ omit: { user: { password: true } } }) +export const prisma = new PrismaClient() .$extends(updateIgnoreNotFoundExtension) .$extends(deleteIgnoreNotFoundExtension) diff --git a/app/routes/api/auth.ts b/app/routes/api/auth.ts deleted file mode 100644 index 9d1d271..0000000 --- a/app/routes/api/auth.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Router } from 'express' - -import { authController } from '@/controllers' - -import { authenticate, validateRequest } from '@/middlewares' - -import { SigninSchema, SignupSchema } from '@/schemas' - -export const authRouter = Router() - -authRouter.post( - '/signup', - validateRequest({ body: SignupSchema }), - authController.signup -) - -authRouter.post( - '/signin', - validateRequest({ body: SigninSchema }), - authController.signin -) - -authRouter.get('/google/initiate', authController.googleInitiate) - -authRouter.get('/google/callback', authController.googleCallback) - -authRouter.get('/microsoft/initiate', authController.microsoftInitiate) - -authRouter.get('/microsoft/callback', authController.microsoftCallback) - -authRouter.post('/refresh', authController.refresh) - -authRouter.post('/logout', authenticate, authController.logout) diff --git a/app/routes/api/board.ts b/app/routes/api/board.ts index 6172ed3..b232ad5 100644 --- a/app/routes/api/board.ts +++ b/app/routes/api/board.ts @@ -2,13 +2,13 @@ import { Router } from 'express' import { boardController } from '@/controllers' -import { authenticate, validateRequest } from '@/middlewares' +import { requireAuth, validateRequest } from '@/middlewares' import { AddBoardSchema, BoardParamsSchema, EditBoardSchema } from '@/schemas' export const boardRouter = Router() -boardRouter.use(authenticate) +boardRouter.use(requireAuth) boardRouter.get('/', boardController.getAll) diff --git a/app/routes/api/column.ts b/app/routes/api/column.ts index c8cdf5d..e235db3 100644 --- a/app/routes/api/column.ts +++ b/app/routes/api/column.ts @@ -2,7 +2,7 @@ import { Router } from 'express' import { columnController } from '@/controllers' -import { authenticate, validateRequest } from '@/middlewares' +import { requireAuth, validateRequest } from '@/middlewares' import { AddColumnSchema, @@ -14,7 +14,7 @@ import { export const columnRouter = Router() -columnRouter.use(authenticate) +columnRouter.use(requireAuth) columnRouter.post( '/:boardId', diff --git a/app/routes/api/label.ts b/app/routes/api/label.ts index 271cbc1..19973e4 100644 --- a/app/routes/api/label.ts +++ b/app/routes/api/label.ts @@ -2,13 +2,13 @@ import { Router } from 'express' import { labelController } from '@/controllers' -import { authenticate, validateRequest } from '@/middlewares' +import { requireAuth, validateRequest } from '@/middlewares' import { CreateLabelSchema } from '@/schemas' export const labelRouter = Router() -labelRouter.use(authenticate) +labelRouter.use(requireAuth) labelRouter.get('/', labelController.getAll) diff --git a/app/routes/api/task.ts b/app/routes/api/task.ts index c9f904d..05c9907 100644 --- a/app/routes/api/task.ts +++ b/app/routes/api/task.ts @@ -2,7 +2,7 @@ import { Router } from 'express' import { taskController } from '@/controllers' -import { authenticate, validateRequest } from '@/middlewares' +import { requireAuth, validateRequest } from '@/middlewares' import { AddTaskSchema, @@ -14,7 +14,7 @@ import { export const taskRouter = Router() -taskRouter.use(authenticate) +taskRouter.use(requireAuth) taskRouter.post( '/:columnId', diff --git a/app/routes/api/user.ts b/app/routes/api/user.ts index abc9dfb..05a1b2f 100644 --- a/app/routes/api/user.ts +++ b/app/routes/api/user.ts @@ -2,22 +2,22 @@ import { Router } from 'express' import { userController } from '@/controllers' -import { authenticate, upload, validateRequest } from '@/middlewares' +import { requireAuth, validateRequest } from '@/middlewares' -import { EditUserSchema, NeedHelpSchema } from '@/schemas' +import { NeedHelpSchema } from '@/schemas' export const userRouter = Router() -userRouter.use(authenticate) +userRouter.use(requireAuth) userRouter.get('/me', userController.me) -userRouter.patch( - '/', - upload.single('avatar'), - validateRequest({ body: EditUserSchema }), - userController.update -) +// userRouter.patch( +// '/', +// upload.single('avatar'), +// validateRequest({ body: EditUserSchema }), +// userController.update +// ) userRouter.post( '/help', diff --git a/app/routes/index.ts b/app/routes/index.ts index f7faa13..2187114 100644 --- a/app/routes/index.ts +++ b/app/routes/index.ts @@ -1,10 +1,10 @@ import fs from 'fs' import path from 'path' +import type { SwaggerUiOptions } from 'swagger-ui-express' import { Router } from 'express' import swaggerUi from 'swagger-ui-express' -import { authRouter } from './api/auth' import { boardRouter } from './api/board' import { columnRouter } from './api/column' import { labelRouter } from './api/label' @@ -16,13 +16,28 @@ export const apiRouter = Router() const swaggerPath = path.join(process.cwd(), 'swagger.json') const swagger = JSON.parse(fs.readFileSync(swaggerPath, 'utf-8')) +apiRouter.get('/swagger.json', (_, res) => { + res.json(swagger) +}) + +const swaggerOptions: SwaggerUiOptions = { + customSiteTitle: 'TaskPro API Docs', + explorer: true, + swaggerOptions: { + urls: [ + { url: '/api/swagger.json', name: 'Main API' }, + { url: '/api/auth/open-api/generate-schema', name: 'Auth' } + ], + 'urls.primaryName': 'Main API' + } +} + apiRouter.use( '/docs', swaggerUi.serve, - swaggerUi.setup(swagger, { customSiteTitle: 'TaskPro API Docs' }) + swaggerUi.setup(swagger, swaggerOptions) ) -apiRouter.use('/auth', authRouter) apiRouter.use('/user', userRouter) apiRouter.use('/board', boardRouter) apiRouter.use('/column', columnRouter) diff --git a/app/types/express.d.ts b/app/types/express.d.ts index 5517165..74d5731 100644 --- a/app/types/express.d.ts +++ b/app/types/express.d.ts @@ -1,10 +1,10 @@ -import type { User } from '@prisma/client' +import type { Session, User } from 'better-auth' declare global { namespace Express { interface Request { user: User - session: string + session: Session } } } diff --git a/package.json b/package.json index aa8b710..747d35e 100644 --- a/package.json +++ b/package.json @@ -18,15 +18,14 @@ "schema": "./prisma" }, "dependencies": { - "@azure/msal-node": "^5.3.0", "@prisma/client": "^6.19.3", - "argon2": "^0.44.0", + "@scalar/express-api-reference": "^0.10.7", + "better-auth": "^1.6.23", "cloudinary": "^2.10.0", "cookie-parser": "^1.4.7", "cors": "2.8.6", "dotenv": "^17.4.2", "express": "^5.2.1", - "google-auth-library": "^10.9.0", "helmet": "^8.2.0", "http-errors": "^2.0.1", "ioredis": "^5.11.1", @@ -34,6 +33,7 @@ "morgan": "1.11.0", "multer": "^2.2.0", "resend": "^6.16.0", + "swagger-merger": "^1.5.4", "swagger-ui-express": "^5.0.1", "zod": "^4.4.3" }, diff --git a/prisma/models/account.prisma b/prisma/models/account.prisma new file mode 100644 index 0000000..52a4cae --- /dev/null +++ b/prisma/models/account.prisma @@ -0,0 +1,22 @@ +model Account { + id String @id @default(auto()) @map("_id") @db.ObjectId + accountId String @map("account_id") + providerId String @map("provider_id") + scope String? + password String? + + accessToken String? @map("access_token") + refreshToken String? @map("refresh_token") + idToken String? @map("id_token") + + userId String @map("user_id") + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + accessTokenExpiresAt DateTime? @map("access_token_expires_at") + refreshTokenExpiresAt DateTime? @map("refresh_token_expires_at") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + @@index([userId]) + @@map("accounts") +} diff --git a/prisma/models/passkey.prisma b/prisma/models/passkey.prisma new file mode 100644 index 0000000..8861333 --- /dev/null +++ b/prisma/models/passkey.prisma @@ -0,0 +1,20 @@ +model Passkey { + id String @id @default(auto()) @map("_id") @db.ObjectId + name String? + counter Int + transports String? + aaguid String? + deviceType String @map("device_type") + backedUp Boolean @map("backed_up") + publicKey String @map("public_key") + credentialID String @map("credential_id") + + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + createdAt DateTime? @map("created_at") + + @@index([userId]) + @@index([credentialID]) + @@map("passkeys") +} diff --git a/prisma/models/session.prisma b/prisma/models/session.prisma index 763f174..0e33abb 100644 --- a/prisma/models/session.prisma +++ b/prisma/models/session.prisma @@ -1,9 +1,18 @@ model Session { - id String @id @default(auto()) @map("_id") @db.ObjectId + id String @id @default(auto()) @map("_id") @db.ObjectId + token String + + ipAddress String? @map("ip_address") + userAgent String? @map("user_agent") + userId String @map("user_id") + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + expiresAt DateTime @map("expires_at") createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") + @@unique([token]) + @@index([userId]) @@map("sessions") } diff --git a/prisma/models/user.prisma b/prisma/models/user.prisma index c953bff..580a878 100644 --- a/prisma/models/user.prisma +++ b/prisma/models/user.prisma @@ -1,19 +1,21 @@ model User { - id String @id @default(auto()) @map("_id") @db.ObjectId - name String - email String @unique - password String? - theme Theme @default(light) + id String @id @default(auto()) @map("_id") @db.ObjectId + name String + email String + emailVerified Boolean @default(false) @map("email_verified") + image String? + imagePublicId String? @map("image_public_id") + theme Theme @default(light) - avatar String? - avatarPublicId String? @map("avatar_public_id") - - boards Board[] - labels Label[] + sessions Session[] + accounts Account[] + boards Board[] + labels Label[] createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime @updatedAt @map("updated_at") + @@unique([email]) @@map("users") } diff --git a/prisma/models/verification.prisma b/prisma/models/verification.prisma new file mode 100644 index 0000000..4edd654 --- /dev/null +++ b/prisma/models/verification.prisma @@ -0,0 +1,12 @@ +model Verification { + id String @id @default(auto()) @map("_id") @db.ObjectId + identifier String + value String + + expiresAt DateTime @map("expires_at") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + @@index([identifier]) + @@map("verifications") +} diff --git a/swagger.json b/swagger.json index 922af64..1cbdcc2 100644 --- a/swagger.json +++ b/swagger.json @@ -3,7 +3,7 @@ "info": { "version": "2.2.0", "title": "Task Pro API", - "description": "The Task Pro API provides endpoints to manage projects and user assignments, ensuring secure and efficient request handling.", + "description": "Task Pro API provides endpoints for managing projects, tasks, and user assignments with secure authentication and efficient workflows.", "contact": { "name": "TaskPro", "url": "https://www.taskpro.qzz.io" @@ -14,191 +14,11 @@ { "url": "https://api.taskpro.qzz.io" } ], "paths": { - "/auth/signup": { - "post": { - "tags": ["Auth"], - "summary": "Create a new user", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": ["name", "email", "password"], - "properties": { - "name": { - "type": "string", - "minLength": 2, - "example": "John Doe" - }, - "email": { - "type": "string", - "format": "email", - "example": "user@example.com" - }, - "password": { - "type": "string", - "minLength": 8, - "maxLength": 64, - "example": "qwerty123" - } - } - } - } - } - }, - "responses": { - "201": { "$ref": "#/components/responses/AuthResponse" }, - "400": { "$ref": "#/components/responses/BadRequestError" }, - "409": { "$ref": "#/components/responses/Conflict" } - } - } - }, - "/auth/signin": { - "post": { - "tags": ["Auth"], - "summary": "Signin a user", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "required": ["email", "password"], - "properties": { - "email": { - "type": "string", - "format": "email", - "example": "user@example.com" - }, - "password": { - "type": "string", - "example": "qwerty123" - } - } - } - } - } - }, - "responses": { - "201": { "$ref": "#/components/responses/AuthResponse" }, - "400": { "$ref": "#/components/responses/BadRequestError" }, - "401": { "$ref": "#/components/responses/Unauthorized" } - } - } - }, - "/auth/google/initiate": { - "get": { - "tags": ["Auth"], - "summary": "Redirect user to Google OAuth", - "responses": { - "302": { - "headers": { - "Location": { - "description": "Google OAuth URL", - "schema": { "type": "string" } - } - } - } - } - } - }, - "/auth/google/callback": { - "get": { - "tags": ["Auth"], - "summary": "Handles Google's OAuth redirect", - "responses": { - "302": { - "description": "Redirects to frontend with success or error", - "headers": { - "Location": { - "description": "Frontend redirect URL", - "schema": { - "type": "string", - "example": "https://taskpro.qzz.io?error=oauth_error" - } - } - } - } - } - } - }, - "/auth/microsoft/initiate": { - "get": { - "tags": ["Auth"], - "summary": "Redirect user to Microsoft OAuth", - "responses": { - "302": { - "headers": { - "Location": { - "description": "Microsoft OAuth URL", - "schema": { "type": "string" } - } - } - } - } - } - }, - "/auth/microsoft/callback": { - "get": { - "tags": ["Auth"], - "summary": "Handles Microsoft's OAuth redirect", - "responses": { - "302": { - "description": "Redirects to frontend with success or error", - "headers": { - "Location": { - "description": "Frontend redirect URL", - "schema": { - "type": "string", - "example": "https://taskpro.qzz.io?error=oauth_error" - } - } - } - } - } - } - }, - "/auth/refresh": { - "post": { - "tags": ["Auth"], - "summary": "Get new access and refresh tokens", - "security": [{ "CookieAuth": [] }], - "responses": { - "200": { "$ref": "#/components/responses/RefreshResponse" }, - "400": { "$ref": "#/components/responses/BadRequestError" }, - "401": { "$ref": "#/components/responses/Unauthorized" }, - "403": { "$ref": "#/components/responses/Forbidden" } - } - } - }, - "/auth/logout": { - "post": { - "tags": ["Auth"], - "summary": "Logout a user", - "security": [{ "CookieAuth": [] }], - "responses": { - "204": { "description": "The user was logged out successfully." }, - "401": { "$ref": "#/components/responses/Unauthorized" } - } - } - }, - "/user/me": { - "get": { - "tags": ["User"], - "summary": "Get current user", - "security": [{ "CookieAuth": [] }], - "responses": { - "200": { "$ref": "#/components/responses/UserResponse" }, - "401": { "$ref": "#/components/responses/Unauthorized" } - } - } - }, "/user/help": { "post": { "tags": ["User"], "summary": "Send email need help", - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "requestBody": { "required": true, "content": { @@ -242,65 +62,11 @@ } } }, - "/user": { - "patch": { - "tags": ["User"], - "summary": "Edit user profile", - "security": [{ "CookieAuth": [] }], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 2, - "example": "Jane Doe" - }, - "email": { - "type": "string", - "format": "email", - "example": "jane.doe@example.com" - }, - "password": { - "type": "string", - "minLength": 8, - "example": "newsecurepassword123" - }, - "theme": { - "type": "string", - "enum": ["light", "dark", "system"], - "example": "dark" - } - } - } - }, - "multipart/form-data": { - "schema": { - "type": "object", - "properties": { - "avatar": { "type": "string", "format": "binary" } - } - } - } - } - }, - "responses": { - "200": { "$ref": "#/components/responses/UserResponse" }, - "400": { "$ref": "#/components/responses/BadRequestError" }, - "406": { "$ref": "#/components/responses/NotAcceptable" }, - "401": { "$ref": "#/components/responses/Unauthorized" }, - "409": { "$ref": "#/components/responses/Conflict" } - } - } - }, "/board": { "get": { "tags": ["Board"], "summary": "Get all boards", - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "responses": { "200": { "$ref": "#/components/responses/AllBoardsResponse" }, "401": { "$ref": "#/components/responses/Unauthorized" } @@ -309,7 +75,7 @@ "post": { "tags": ["Board"], "summary": "Add new board", - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "requestBody": { "required": true, "content": { @@ -368,7 +134,7 @@ } } ], - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "responses": { "200": { "$ref": "#/components/responses/BoardByIdResponse" }, "401": { "$ref": "#/components/responses/Unauthorized" }, @@ -389,7 +155,7 @@ } } ], - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "requestBody": { "required": true, "content": { @@ -446,7 +212,7 @@ } } ], - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "responses": { "204": { "description": "The resource was deleted successfully." }, "401": { "$ref": "#/components/responses/Unauthorized" }, @@ -469,7 +235,7 @@ } } ], - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "requestBody": { "required": true, "content": { @@ -511,7 +277,7 @@ } } ], - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "requestBody": { "required": true, "content": { @@ -562,7 +328,7 @@ } } ], - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "requestBody": { "required": true, "content": { @@ -601,7 +367,7 @@ } } ], - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "responses": { "204": { "description": "The resource was deleted successfully." }, "401": { "$ref": "#/components/responses/Unauthorized" }, @@ -624,7 +390,7 @@ } } ], - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "requestBody": { "required": true, "content": { @@ -680,7 +446,7 @@ } } ], - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "requestBody": { "required": true, "content": { @@ -728,7 +494,7 @@ } } ], - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "requestBody": { "required": true, "content": { @@ -781,7 +547,7 @@ } } ], - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "responses": { "204": { "description": "The resource was deleted successfully." }, "401": { "$ref": "#/components/responses/Unauthorized" }, @@ -793,7 +559,7 @@ "get": { "tags": ["Label"], "summary": "Get all labels", - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "responses": { "200": { "$ref": "#/components/responses/AllLabelsResponse" }, "401": { "$ref": "#/components/responses/Unauthorized" } @@ -802,7 +568,7 @@ "post": { "tags": ["Label"], "summary": "Add new label", - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "requestBody": { "required": true, "content": { @@ -847,7 +613,7 @@ } } ], - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "requestBody": { "required": true, "content": { @@ -886,7 +652,7 @@ } } ], - "security": [{ "CookieAuth": [] }], + "security": [{ "bearerAuth": [] }], "responses": { "204": { "description": "The resource was deleted successfully." }, "401": { "$ref": "#/components/responses/Unauthorized" }, @@ -897,53 +663,6 @@ }, "components": { "schemas": { - "User": { - "type": "object", - "properties": { - "id": { - "type": "string", - "pattern": "^[0-9a-fA-F]{24}$", - "example": "674723719b47fd08e6a2d517" - }, - "name": { "type": "string", "example": "John Doe" }, - "email": { - "type": "string", - "format": "email", - "example": "user@example.com" - }, - "theme": { - "type": "string", - "enum": ["light", "dark", "system"], - "example": "light" - }, - "avatar": { - "type": "string", - "format": "url", - "example": "https://res.cloudinary.com/dmbnnewoy/image/upload/v1706958682/TaskPro/user_avatar_default/user_light.png" - }, - "avatarPublicId": { - "type": "string", - "nullable": true, - "example": null - }, - "createdAt": { - "type": "string", - "format": "date-time", - "example": "2026-06-29T14:30:00.000Z" - }, - "updatedAt": { - "type": "string", - "format": "date-time", - "example": "2026-06-29T14:30:00.000Z" - } - } - }, - "AuthResponseSchema": { - "type": "object", - "properties": { - "user": { "$ref": "#/components/schemas/User" } - } - }, "BoardBackground": { "type": "object", "properties": { @@ -1198,64 +917,6 @@ } } }, - "NotAcceptable": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ErrorResponse", - "example": { - "statusCode": 406, - "message": "File must have .jpeg or .png extension" - } - } - } - } - }, - "AuthResponse": { - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/AuthResponseSchema" } - } - }, - "headers": { - "Set-Cookie": { - "schema": { - "type": "string", - "example": "accessToken=eyJhbGci...; Path=/; HttpOnly; SameSite=Lax; Secure; refreshToken=eyJhbGci...; Path=/; HttpOnly; SameSite=Lax; Secure;" - } - } - } - }, - "RefreshResponse": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "message": { - "type": "string", - "example": "Tokens refreshed successfully" - } - } - } - } - }, - "headers": { - "Set-Cookie": { - "schema": { - "type": "string", - "example": "accessToken=eyJhbGci...; Path=/; HttpOnly; SameSite=Lax; Secure; refreshToken=eyJhbGci...; Path=/; HttpOnly; SameSite=Lax; Secure;" - } - } - } - }, - "UserResponse": { - "content": { - "application/json": { - "schema": { "$ref": "#/components/schemas/User" } - } - } - }, "UpdateOrderBadRequest": { "content": { "application/json": { @@ -1438,11 +1099,10 @@ } }, "securitySchemes": { - "CookieAuth": { - "type": "apiKey", - "in": "cookie", - "name": "accessToken", - "description": "Access token is sent as an HTTP-only cookie." + "bearerAuth": { + "type": "http", + "scheme": "bearer", + "bearerFormat": "JWT" } } } diff --git a/yarn.lock b/yarn.lock index a6a6ac5..2b50d5b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,19 +2,6 @@ # yarn lockfile v1 -"@azure/msal-common@16.10.0": - version "16.10.0" - resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-16.10.0.tgz#b270d164019e7c8d80692e9ceceecff003e6c1dc" - integrity sha512-iYtjpanlv6963Jprs0MvzIap07V+QhultjQctfbEDQCflsDAEeO3R7XnVA5gk30fhoBFLdgJT7VqO0TGsEsN9w== - -"@azure/msal-node@^5.3.0": - version "5.3.0" - resolved "https://registry.yarnpkg.com/@azure/msal-node/-/msal-node-5.3.0.tgz#538b1fe105828dd31b35c8d6c817d5ff96464b2c" - integrity sha512-fXtJX811pX8y8QlrQqBSH6+plvWyKZDI0IxkheAcyAw9OtcpXyFivmTC7eGUqutLWaDlKXuQ3yOESD4zAmkjHg== - dependencies: - "@azure/msal-common" "16.10.0" - jsonwebtoken "^9.0.0" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.29.7": version "7.29.7" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.29.7.tgz#f2fbbfea87c44a21590ec515b778b2c26d8866e7" @@ -87,6 +74,57 @@ "@babel/helper-string-parser" "^7.29.7" "@babel/helper-validator-identifier" "^7.29.7" +"@better-auth/core@1.6.23": + version "1.6.23" + resolved "https://registry.yarnpkg.com/@better-auth/core/-/core-1.6.23.tgz#1a6213ec81a3f7f084aae945736475d2b04c1b7a" + integrity sha512-beEhOs0uVeOxYOZKUfIEBd/nQV2Bd4/6wyLxZ0OFkn6CMTK2Vi+hXuZLnyPBeB6RdHpebEoJWiHqwHxBIxgPDQ== + dependencies: + "@opentelemetry/semantic-conventions" "^1.39.0" + "@standard-schema/spec" "^1.1.0" + zod "^4.3.6" + +"@better-auth/drizzle-adapter@1.6.23": + version "1.6.23" + resolved "https://registry.yarnpkg.com/@better-auth/drizzle-adapter/-/drizzle-adapter-1.6.23.tgz#d9f9f1c974e0e9a1e5fdc6685fad4a267c9d467c" + integrity sha512-2+/PTVfIP9E7iz6af8TB3lhnowHUj9ljC66kECmHaFEdUqPgzHoWux9epotKwO7XDg2ui4ttWQ8CMeNFLvQeKQ== + +"@better-auth/kysely-adapter@1.6.23": + version "1.6.23" + resolved "https://registry.yarnpkg.com/@better-auth/kysely-adapter/-/kysely-adapter-1.6.23.tgz#845a5de0d8e9927e15011908a838951abbfad427" + integrity sha512-zbNJsMbG09exfkGyvFqBLLqWoMPAUWjxCuUnEK5AsjbYoZeIjj/QGZgdf4CapVWryKxjA9Q6Jlr6fbiPpC3VAg== + +"@better-auth/memory-adapter@1.6.23": + version "1.6.23" + resolved "https://registry.yarnpkg.com/@better-auth/memory-adapter/-/memory-adapter-1.6.23.tgz#1295d3c8091cfa2a9beb2e7f9b29e44663dd09e5" + integrity sha512-krIiR0pIVkaKlAzm690n5bcMW4NGbqeMg0HQSD9fz/KcQF/eWLqcq9gG/BhHTj2i/y96qH+W5JWPmaSOS5iTgQ== + +"@better-auth/mongo-adapter@1.6.23": + version "1.6.23" + resolved "https://registry.yarnpkg.com/@better-auth/mongo-adapter/-/mongo-adapter-1.6.23.tgz#21637c529c8bc1b5ff27de8b96059d781acd151f" + integrity sha512-7+QdevitGlKBbP6JbiSk5SBnzPsKV/mDrQBGBn8hwByQLeJwqpqbuBPw7ZI8vzUlFfAAnyFiqwP3Eb8mxnp7pA== + +"@better-auth/prisma-adapter@1.6.23": + version "1.6.23" + resolved "https://registry.yarnpkg.com/@better-auth/prisma-adapter/-/prisma-adapter-1.6.23.tgz#d141a4cc74785392bb0490d37002bd6d75b3b661" + integrity sha512-2qSdzidq4tkb1eS5TTqb4Nzg0mdZWm3Qky9SYeXeb8PpVQbC2sxqJhEM5mK7y12uU6I8hc64wO9f7AFVNL+6UQ== + +"@better-auth/telemetry@1.6.23": + version "1.6.23" + resolved "https://registry.yarnpkg.com/@better-auth/telemetry/-/telemetry-1.6.23.tgz#a232e560c2e7f618c4ad19e01565c0ea8b84740e" + integrity sha512-/R2Kb+z2BpDOOWwVHqOk+c0VNpuwfCv4Hp5Yr9003WIZPax/zyNraGLB9CFE8qF2gZW8Dsz419k4I8CPrGzpDA== + +"@better-auth/utils@0.4.2", "@better-auth/utils@^0.4.0": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@better-auth/utils/-/utils-0.4.2.tgz#265a37e2ceef035e6968a851b13400d1b044e070" + integrity sha512-AUxrvu+HaaODsUyzDxFgwd/8RZ1yZaYo42LXKSrU2oGgR38pS1ij8nqQKNgtTWoYGpNevNXtCfgTy6loHveW9A== + dependencies: + "@noble/hashes" "^2.0.1" + +"@better-fetch/fetch@1.3.1", "@better-fetch/fetch@^1.1.21": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@better-fetch/fetch/-/fetch-1.3.1.tgz#04a0d289de818a0b436bfda48e70b2b19227cc19" + integrity sha512-ABkD1WhyfPZprKRQI3bhATjeiFuNWC9PXhfGWqL+sg/gKrM977oFrYkdb4msM3hgUGonr7KlOsOFT5TU2rht9g== + "@borewit/text-codec@^0.2.1": version "0.2.2" resolved "https://registry.yarnpkg.com/@borewit/text-codec/-/text-codec-0.2.2.tgz#75025f735c0983b3a871668804a57387e3649375" @@ -250,11 +288,6 @@ "@simple-libs/stream-utils" "^1.2.0" semver "^7.5.2" -"@epic-web/invariant@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@epic-web/invariant/-/invariant-1.0.0.tgz#1073e5dee6dd540410784990eb73e4acd25c9813" - integrity sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA== - "@esbuild/aix-ppc64@0.28.0": version "0.28.0" resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz#7a289c158e29cbf59ea0afc83cc80f06d1c89402" @@ -624,10 +657,20 @@ "@napi-rs/nice-win32-ia32-msvc" "1.1.1" "@napi-rs/nice-win32-x64-msvc" "1.1.1" -"@phc/format@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@phc/format/-/format-1.0.0.tgz#b5627003b3216dc4362125b13f48a4daa76680e4" - integrity sha512-m7X9U6BG2+J+R1lSOdCiITLLrxm+cWlNI3HUFA92oLO77ObGNzaKdh8pMLqdZcshtkKuV84olNNXDfMc4FezBQ== +"@noble/ciphers@^2.1.1": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@noble/ciphers/-/ciphers-2.2.0.tgz#84fb45ac9332925d643b80f89ceb0ea2f21dba95" + integrity sha512-Z6pjIZ/8IJcCGzb2S/0Px5J81yij85xASuk1teLNeg75bfT07MV3a/O2Mtn1I2se43k3lkVEcFaR10N4cgQcZA== + +"@noble/hashes@^2.0.1": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-2.2.0.tgz#22da1d16a469954fce877055d559900a6c73b63b" + integrity sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg== + +"@opentelemetry/semantic-conventions@^1.39.0": + version "1.41.1" + resolved "https://registry.yarnpkg.com/@opentelemetry/semantic-conventions/-/semantic-conventions-1.41.1.tgz#b04e7151c5913a7a006d4f465479da75efb98a7a" + integrity sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA== "@prisma/client@^6.19.3": version "6.19.3" @@ -680,6 +723,50 @@ dependencies: "@prisma/debug" "6.19.3" +"@scalar/client-side-rendering@0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@scalar/client-side-rendering/-/client-side-rendering-0.3.0.tgz#1c5abd495e5cb77524cc2a2e583fe7cda24e6297" + integrity sha512-FFpLokNEyKvpKAJesa+eDh+2e+4qdhoexAJp6alhhxUA6SGqhDfTIV4P+0nZBMdMD6WPu4YeVJHa1hRrGKUVMA== + dependencies: + "@scalar/schemas" "0.7.0" + "@scalar/types" "0.16.0" + "@scalar/validation" "0.6.0" + +"@scalar/express-api-reference@^0.10.7": + version "0.10.7" + resolved "https://registry.yarnpkg.com/@scalar/express-api-reference/-/express-api-reference-0.10.7.tgz#5791a04c6de984ec95b7187775e2a54c36189d7e" + integrity sha512-RJ335AAA25g/Byq9voBMS98WtiRgIb6MkZurINmKo5Y9RdYWL/uJl4bBzYPFyxKuRVZ6+Hp/PK0nntzKo86p+w== + dependencies: + "@scalar/client-side-rendering" "0.3.0" + +"@scalar/helpers@0.9.0": + version "0.9.0" + resolved "https://registry.yarnpkg.com/@scalar/helpers/-/helpers-0.9.0.tgz#b3db11361209b4b927333487523e1e84c4ffd40f" + integrity sha512-M34CLRCttqC1bXthI/QSzQj0s5C6nrU2PFWf/vOT3RpycbiGDGQbqR+5RfFzpOIQvRqbHfNdcRbeiZBw+vCbkQ== + +"@scalar/schemas@0.7.0": + version "0.7.0" + resolved "https://registry.yarnpkg.com/@scalar/schemas/-/schemas-0.7.0.tgz#e01f1a0fd7deedcf913d6dc9c554459177441244" + integrity sha512-Roj0e7S29yTbGojwdx/b8C1H5arVN4h/VSPrtQ5vb9NT2ZAbrfPQVmpQYrF845rKOWX7kSzIvyBwrKNEMIAkxA== + dependencies: + "@scalar/helpers" "0.9.0" + "@scalar/validation" "0.6.0" + +"@scalar/types@0.16.0": + version "0.16.0" + resolved "https://registry.yarnpkg.com/@scalar/types/-/types-0.16.0.tgz#6dfae29d2f0fe6e6329510b0e7955dd95a8e209d" + integrity sha512-26OSrvZjKWZ7F236wWmJajBGDVFJuvXFJqKPFqbt/PxlgZKXQXfJsZorASRQmdNogT576nxYalQ7oaYWEnQwfw== + dependencies: + "@scalar/helpers" "0.9.0" + nanoid "^5.1.6" + type-fest "^5.3.1" + zod "^4.3.5" + +"@scalar/validation@0.6.0": + version "0.6.0" + resolved "https://registry.yarnpkg.com/@scalar/validation/-/validation-0.6.0.tgz#70e8b8a7eec6b86fd76f569b614e22bb41209f06" + integrity sha512-tpmmG+/xRE2Kn9RpflU3AIyZv08v10+E1ZrJCx7z6+/91zHVxy0M73kC1LT4/8PbYNt85ywyC8+n+D99JdMcGA== + "@scarf/scarf@=1.4.0": version "1.4.0" resolved "https://registry.yarnpkg.com/@scarf/scarf/-/scarf-1.4.0.tgz#3bbb984085dbd6d982494538b523be1ce6562972" @@ -773,7 +860,7 @@ resolved "https://registry.yarnpkg.com/@stablelib/base64/-/base64-1.0.1.tgz#bdfc1c6d3a62d7a3b7bbc65b6cce1bb4561641be" integrity sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ== -"@standard-schema/spec@^1.0.0": +"@standard-schema/spec@^1.0.0", "@standard-schema/spec@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.1.0.tgz#a79b55dbaf8604812f52d140b2c9ab41bc150bb8" integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w== @@ -1277,11 +1364,6 @@ acorn@^8.16.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.16.0.tgz#4ce79c89be40afe7afe8f3adb902a1f1ce9ac08a" integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== -agent-base@^7.1.2: - version "7.1.4" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.4.tgz#e3cd76d4c548ee895d3c3fd8dc1f6c5b9032e7a8" - integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== - ajv@^6.14.0: version "6.15.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.15.0.tgz#07e982c74626167aa7a2495c53817892d7139492" @@ -1331,16 +1413,6 @@ append-field@^1.0.0: resolved "https://registry.yarnpkg.com/append-field/-/append-field-1.0.0.tgz#1e3440e915f0b1203d23748e78edd7b9b5b43e56" integrity sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw== -argon2@^0.44.0: - version "0.44.0" - resolved "https://registry.yarnpkg.com/argon2/-/argon2-0.44.0.tgz#65a5ba662bba66af41407aa0457decf4af101742" - integrity sha512-zHPGN3S55sihSQo0dBbK0A5qpi2R31z7HZDZnry3ifOyj8bZZnpZND2gpmhnRGO1V/d555RwBqIK5W4Mrmv3ig== - dependencies: - "@phc/format" "^1.0.0" - cross-env "^10.0.0" - node-addon-api "^8.5.0" - node-gyp-build "^4.8.4" - argparse@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" @@ -1351,6 +1423,11 @@ array-ify@^1.0.0: resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== +async@^3.2.3: + version "3.2.6" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== + b4a@^1.6.4: version "1.8.1" resolved "https://registry.yarnpkg.com/b4a/-/b4a-1.8.1.tgz#7f16334ca80127aeb26064a28841acbf174840a4" @@ -1371,7 +1448,7 @@ bare-events@^2.7.0: resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.8.3.tgz#ed26c87a24ece41c69dd4d2d0891c2c04a949e13" integrity sha512-HdUm8EMQBLaJvGUdidNNbqpA1kYkwNcb+MYxkxCLAPJGQzlv9J0C24h8V65Z4c5GLd/JEALDvpFCQgpLJqc0zw== -base64-js@^1.3.0, base64-js@^1.3.1: +base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -1383,10 +1460,38 @@ basic-auth@~2.0.1: dependencies: safe-buffer "5.1.2" -bignumber.js@^9.0.0: - version "9.3.1" - resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.3.1.tgz#759c5aaddf2ffdc4f154f7b493e1c8770f88c4d7" - integrity sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ== +better-auth@^1.6.23: + version "1.6.23" + resolved "https://registry.yarnpkg.com/better-auth/-/better-auth-1.6.23.tgz#1af5228d94be69fb2e63b5bfd57d843c849cc28a" + integrity sha512-4vOaRd9UiKGKm9R+ej0jjU1es3MiJIiNc9Qq3VCnYqOZ4/nb5272QqTxWYoDxyUXl5x6A2x2we5KZKQO9teTQQ== + dependencies: + "@better-auth/core" "1.6.23" + "@better-auth/drizzle-adapter" "1.6.23" + "@better-auth/kysely-adapter" "1.6.23" + "@better-auth/memory-adapter" "1.6.23" + "@better-auth/mongo-adapter" "1.6.23" + "@better-auth/prisma-adapter" "1.6.23" + "@better-auth/telemetry" "1.6.23" + "@better-auth/utils" "0.4.2" + "@better-fetch/fetch" "1.3.1" + "@noble/ciphers" "^2.1.1" + "@noble/hashes" "^2.0.1" + better-call "1.3.7" + defu "^6.1.4" + jose "^6.1.3" + kysely "^0.28.17 || ^0.29.0" + nanostores "^1.1.1" + zod "^4.3.6" + +better-call@1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/better-call/-/better-call-1.3.7.tgz#2889806d909c1efb636b7e9009e7deba8927106f" + integrity sha512-Al51/hjp2SSp6CRTa3F2ptcx4yQVS1xWKoY6jcVXqNYOap6mHFP2jUBn5EwIL4iIed1/Sq4hlQ+Umm6EflZG+w== + dependencies: + "@better-auth/utils" "^0.4.0" + "@better-fetch/fetch" "^1.1.21" + rou3 "^0.7.12" + set-cookie-parser "^3.0.1" binary-version-check@^6.1.0: version "6.1.0" @@ -1434,11 +1539,6 @@ brace-expansion@^5.0.5: dependencies: balanced-match "^4.0.2" -buffer-equal-constant-time@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" - integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== - buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" @@ -1601,6 +1701,11 @@ cluster-key-slot@1.1.1: resolved "https://registry.yarnpkg.com/cluster-key-slot/-/cluster-key-slot-1.1.1.tgz#10ccb9ded0729464b6d2e7d714b100a2d1259d43" integrity sha512-rwHwUfXL40Chm1r08yrhU3qpUvdVlgkKNeyeGPOxnW8/SyVDvgRaed/Uz54AqWNaTCAThlj6QAs3TZcKI0xDEw== +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== + comma-separated-tokens@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz#4e89c9458acb61bc8fef19f4529973b2392839ee" @@ -1616,6 +1721,11 @@ commander@^8.3.0: resolved "https://registry.yarnpkg.com/commander/-/commander-8.3.0.tgz#4837ea1b2da67b9c616a67afbb0fafee567bca66" integrity sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww== +commander@^9.0.0: + version "9.5.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" + integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== + compare-func@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" @@ -1739,14 +1849,6 @@ cosmiconfig@^9.0.1: js-yaml "^4.1.0" parse-json "^5.2.0" -cross-env@^10.0.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-10.1.0.tgz#cfd2a6200df9ed75bfb9cb3d7ce609c13ea21783" - integrity sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw== - dependencies: - "@epic-web/invariant" "^1.0.0" - cross-spawn "^7.0.6" - cross-spawn@^7.0.3, cross-spawn@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" @@ -1756,11 +1858,6 @@ cross-spawn@^7.0.3, cross-spawn@^7.0.6: shebang-command "^2.0.0" which "^2.0.1" -data-uri-to-buffer@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz#d8feb2b2881e6a4f58c2e08acfd0e2834e26222e" - integrity sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A== - debug@2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -1768,7 +1865,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4, debug@4.4.3, debug@^4.3.1, debug@^4.3.2, debug@^4.4.0, debug@^4.4.3: +debug@4.4.3, debug@^4.3.1, debug@^4.3.2, debug@^4.4.0, debug@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== @@ -1850,13 +1947,6 @@ dunder-proto@^1.0.1: es-errors "^1.3.0" gopd "^1.2.0" -ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" - integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== - dependencies: - safe-buffer "^5.0.1" - ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -2189,11 +2279,6 @@ ext-name@^5.0.0: ext-list "^2.0.0" sort-keys-length "^1.0.0" -extend@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - fast-check@^3.23.1: version "3.23.2" resolved "https://registry.yarnpkg.com/fast-check/-/fast-check-3.23.2.tgz#0129f1eb7e4f500f58e8290edc83c670e4a574a2" @@ -2236,14 +2321,6 @@ fdir@^6.5.0: resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.5.0.tgz#ed2ab967a331ade62f18d077dae192684d50d350" integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== -fetch-blob@^3.1.2, fetch-blob@^3.1.4: - version "3.2.0" - resolved "https://registry.yarnpkg.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9" - integrity sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ== - dependencies: - node-domexception "^1.0.0" - web-streams-polyfill "^3.0.3" - figures@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/figures/-/figures-6.1.0.tgz#935479f51865fa7479f6fa94fc6fc7ac14e62c4a" @@ -2321,18 +2398,19 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.4.2.tgz#f5c23c107f0f37de8dbdf24f13722b3b98d52726" integrity sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA== +fmtconv@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/fmtconv/-/fmtconv-1.1.0.tgz#9ec73916f89773c130f988d282cbe791f63a8d7a" + integrity sha512-UV++K2xqeZBNRsVrhNjepbrHfMw3zfLArbciTs+eQmMPl635xbWaxnoOAZsvvsssAsfUnuL2S8Z7b+rC8obICw== + dependencies: + commander "^9.0.0" + js-yaml "^4.1.0" + form-data-encoder@^4.0.2: version "4.1.0" resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-4.1.0.tgz#497cedc94810bd5d53b99b5d4f6c152d5cbc9db2" integrity sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw== -formdata-polyfill@^4.0.10: - version "4.0.10" - resolved "https://registry.yarnpkg.com/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz#24807c31c9d402e002ab3d8c720144ceb8848423" - integrity sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g== - dependencies: - fetch-blob "^3.1.2" - forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -2358,24 +2436,6 @@ function-timeout@^1.0.1: resolved "https://registry.yarnpkg.com/function-timeout/-/function-timeout-1.0.2.tgz#e5a7b6ffa523756ff20e1231bbe37b5f373aadd5" integrity sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA== -gaxios@^7.0.0, gaxios@^7.1.4: - version "7.1.4" - resolved "https://registry.yarnpkg.com/gaxios/-/gaxios-7.1.4.tgz#33a5b78e2c5c01cf5a5d17f58dd188839867fc9c" - integrity sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA== - dependencies: - extend "^3.0.2" - https-proxy-agent "^7.0.1" - node-fetch "^3.3.2" - -gcp-metadata@8.1.2: - version "8.1.2" - resolved "https://registry.yarnpkg.com/gcp-metadata/-/gcp-metadata-8.1.2.tgz#e62e3373ddf41fc727ccc31c55c687b798bee898" - integrity sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg== - dependencies: - gaxios "^7.0.0" - google-logging-utils "^1.0.0" - json-bigint "^1.0.0" - get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -2462,23 +2522,6 @@ globals@^17.7.0: resolved "https://registry.yarnpkg.com/globals/-/globals-17.7.0.tgz#553d55090b4dde8209ec2da42580d6e7e7d8b10d" integrity sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg== -google-auth-library@^10.9.0: - version "10.9.0" - resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-10.9.0.tgz#61e742af957818f572c3a5bce634c68355241d64" - integrity sha512-xtvUqvINPhTaBm7nXqlYPcrMHJPm1lCNdSovxnKKhTm+4JsvQ+KGVYJViLoH9Yxu8w+T0Qv5HubzYT9BLrppJg== - dependencies: - base64-js "^1.3.0" - ecdsa-sig-formatter "^1.0.11" - gaxios "^7.1.4" - gcp-metadata "8.1.2" - google-logging-utils "1.1.3" - jws "^4.0.0" - -google-logging-utils@1.1.3, google-logging-utils@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/google-logging-utils/-/google-logging-utils-1.1.3.tgz#17b71f1f95d266d2ddd356b8f00178433f041b17" - integrity sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA== - gopd@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" @@ -2590,14 +2633,6 @@ http2-wrapper@^2.2.1: quick-lru "^5.1.1" resolve-alpn "^1.2.0" -https-proxy-agent@^7.0.1: - version "7.0.6" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" - integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== - dependencies: - agent-base "^7.1.2" - debug "4" - human-signals@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-5.0.0.tgz#42665a284f9ae0dade3ba41ebc37eb4b852f3a28" @@ -2767,7 +2802,7 @@ jiti@^2.4.2, jiti@^2.7.0: resolved "https://registry.yarnpkg.com/jiti/-/jiti-2.7.0.tgz#974228f2f4ca2bc21885a1797b45fea68e950c64" integrity sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ== -jose@^6.2.3: +jose@^6.1.3, jose@^6.2.3: version "6.2.3" resolved "https://registry.yarnpkg.com/jose/-/jose-6.2.3.tgz#0975197ad973251221c658a3cddc4b951a250c2d" integrity sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw== @@ -2789,13 +2824,6 @@ jsesc@^3.0.2: resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.1.0.tgz#74d335a234f67ed19907fdadfac7ccf9d409825d" integrity sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA== -json-bigint@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" - integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== - dependencies: - bignumber.js "^9.0.0" - json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" @@ -2821,39 +2849,6 @@ json-stable-stringify-without-jsonify@^1.0.1: resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== -jsonwebtoken@^9.0.0: - version "9.0.3" - resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz#6cd57ab01e9b0ac07cb847d53d3c9b6ee31f7ae2" - integrity sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g== - dependencies: - jws "^4.0.1" - 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" - -jwa@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/jwa/-/jwa-2.0.1.tgz#bf8176d1ad0cd72e0f3f58338595a13e110bc804" - integrity sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg== - dependencies: - buffer-equal-constant-time "^1.0.1" - ecdsa-sig-formatter "1.0.11" - safe-buffer "^5.0.1" - -jws@^4.0.0, jws@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/jws/-/jws-4.0.1.tgz#07edc1be8fac20e677b283ece261498bd38f0690" - integrity sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA== - dependencies: - jwa "^2.0.1" - safe-buffer "^5.0.1" - keyv@^4.5.4: version "4.5.4" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" @@ -2873,6 +2868,11 @@ kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +"kysely@^0.28.17 || ^0.29.0": + version "0.29.2" + resolved "https://registry.yarnpkg.com/kysely/-/kysely-0.29.2.tgz#4a07da18db3bba2911693c2b3bd4af415104a85f" + integrity sha512-s6WVJyEZrbm6jhBpiKHsGHyePMrVQKJ85wZCFCr9W4QHv6WTjWIrdvTmO9hDEA3bNK0xkrE2DqrHsXMLWuZpQg== + levn@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -2916,41 +2916,6 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash.includes@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" - integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== - -lodash.isboolean@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" - integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== - -lodash.isinteger@^4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" - integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== - -lodash.isnumber@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" - integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== - -lodash.isplainobject@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== - -lodash.isstring@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" - integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== - -lodash.once@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" - integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== - lodash@^4.17.23: version "4.18.1" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.18.1.tgz#ff2b66c1f6326d59513de2407bf881439812771c" @@ -3127,7 +3092,7 @@ ms@2.0.0: resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== -ms@^2.1.1, ms@^2.1.3: +ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== @@ -3142,6 +3107,16 @@ multer@^2.2.0: concat-stream "^2.0.0" type-is "^1.6.18" +nanoid@^5.1.6: + version "5.1.16" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-5.1.16.tgz#fe345c0a1f9007c32fbb5c139e1208bfd3f41ef7" + integrity sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ== + +nanostores@^1.1.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/nanostores/-/nanostores-1.4.0.tgz#9acc8a6026533dc0410c37cb3b076db00163d277" + integrity sha512-i0tloweeudshAEuddpDxcg9Ik6pkPfVsHIgKyf143JrgG7/MOh0+q7BypdLXZPoOP7fOYt1eTcwGkyiVmhJFkA== + natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -3152,35 +3127,11 @@ negotiator@^1.0.0: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-1.0.0.tgz#b6c91bb47172d69f93cfd7c357bbb529019b5f6a" integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== -node-addon-api@^8.5.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-8.8.0.tgz#b742be05007b37ebc1741bbaf370d22bf25d208a" - integrity sha512-c5Ko1fZJIJmzhFIkhRN76WTq+fC6tWnGy9CXA0fA+XygsWZmEwG8vmbkNqxMyoaa0Tin4djul49NzdVcJJcjeA== - -node-domexception@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" - integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== - node-fetch-native@^1.6.6: version "1.6.7" resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-1.6.7.tgz#9d09ca63066cc48423211ed4caf5d70075d76a71" integrity sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q== -node-fetch@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.2.tgz#d1e889bacdf733b4ff3b2b243eb7a12866a0b78b" - integrity sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA== - dependencies: - data-uri-to-buffer "^4.0.0" - fetch-blob "^3.1.4" - formdata-polyfill "^4.0.10" - -node-gyp-build@^4.8.4: - version "4.8.4" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.4.tgz#8a70ee85464ae52327772a90d66c6077a900cfc8" - integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ== - normalize-url@^8.1.1: version "8.1.1" resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-8.1.1.tgz#751a20c8520e5725404c06015fea21d7567f25ef" @@ -3587,6 +3538,11 @@ rfdc@^1.4.1: resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== +rou3@^0.7.12: + version "0.7.12" + resolved "https://registry.yarnpkg.com/rou3/-/rou3-0.7.12.tgz#cac17425c04abddba854a42385cabfe0b971a179" + integrity sha512-iFE4hLDuloSWcD7mjdCDhx2bKcIsYbtOTpfH5MHHLSKMOUyjqQXTeZVa289uuwEGEKFoE/BAPbhaU4B774nceg== + router@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/router/-/router-2.2.0.tgz#019be620b711c87641167cc79b99090f00b146ef" @@ -3603,7 +3559,7 @@ safe-buffer@5.1.2: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@^5.0.1, safe-buffer@~5.2.0: +safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -3632,7 +3588,7 @@ semver-truncate@^3.0.0: dependencies: semver "^7.3.5" -semver@^7.3.5, semver@^7.3.8, semver@^7.5.2, semver@^7.5.4, semver@^7.6.0, semver@^7.7.3: +semver@^7.3.5, semver@^7.3.8, semver@^7.5.2, semver@^7.6.0, semver@^7.7.3: version "7.8.1" resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.1.tgz#bf4970b5e70fda0686363cc18bfe8805d5ed957e" integrity sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg== @@ -3664,6 +3620,11 @@ serve-static@^2.2.0: parseurl "^1.3.3" send "^1.2.0" +set-cookie-parser@^3.0.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-3.1.1.tgz#5d80652208e37545aba30ea613d4f797ea405af8" + integrity sha512-vM9SUhjsUYs6UeJUmygc5Ofm5eQGe85riob5ju6XCgFGJI5PLV4nrDAQpQjd+LkFBpAkADn5BQQpZ9EUNkyLuA== + setprototypeof@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -3910,10 +3871,20 @@ supports-hyperlinks@^2.2.0: has-flag "^4.0.0" supports-color "^7.0.0" +swagger-merger@^1.5.4: + version "1.5.4" + resolved "https://registry.yarnpkg.com/swagger-merger/-/swagger-merger-1.5.4.tgz#c1c3356a9cbc824cbffd04222eed694a53b2cd92" + integrity sha512-hdNDtg3mecF1e4/3q5P9CquHrez+rQdklwN8itQ8gVVWiugmzlRwoLu/kgNytSlkIYQ4ajjiA7L86XqRfW/QQQ== + dependencies: + async "^3.2.3" + co "^4.6.0" + commander "^9.0.0" + fmtconv "^1.1.0" + swagger-ui-dist@>=5.0.0: - version "5.32.6" - resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.32.6.tgz#4a6a56786915ea4bfea43f00ff17890df8124d4c" - integrity sha512-75ttZNaYCLoFPnozPZcTUU6mS3wKT8l7WLjU5zJSHFeJa23i5vtnze6IiCl4jDMPeQTXVXIgovq4M11NNfQvSA== + version "5.32.8" + resolved "https://registry.yarnpkg.com/swagger-ui-dist/-/swagger-ui-dist-5.32.8.tgz#14a5e6ba016875dfb31de26cff5c3bea725a3c89" + integrity sha512-dgMdWXIgnI4zX4OPhKEdWnlDODbgm8W3AX0Ivn/BBqcUh6xZsBxhZMnvk6DJyRz1BTrj8dPxtarmEGgkz30oyA== dependencies: "@scarf/scarf" "=1.4.0" @@ -3929,6 +3900,11 @@ system-architecture@^1.0.0: resolved "https://registry.yarnpkg.com/system-architecture/-/system-architecture-1.0.0.tgz#0e71d7678d3d3b2fabfac0e00ea0306b14f8a532" integrity sha512-0OJWD12D7XX3KUg1DYkMaTTjSTo2k/mhIYI3HlBlceXSMcJhW/1qO735fPKS5prcyjvn57Ub151vvASYXpQrEw== +tagged-tag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/tagged-tag/-/tagged-tag-1.0.0.tgz#a0b5917c2864cba54841495abfa3f6b13edcf4d6" + integrity sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng== + tar-stream@3.1.7: version "3.1.7" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-3.1.7.tgz#24b3fb5eabada19fe7338ed6d26e5f7c482e792b" @@ -4041,6 +4017,13 @@ type-fest@^4.26.1, type-fest@^4.6.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-4.41.0.tgz#6ae1c8e5731273c2bf1f58ad39cbae2c91a46c58" integrity sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA== +type-fest@^5.3.1: + version "5.7.0" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-5.7.0.tgz#bae586d3b7c2596bd9c7e62195f33c7fcada1c91" + integrity sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg== + dependencies: + tagged-tag "^1.0.0" + type-is@^1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" @@ -4177,11 +4160,6 @@ vfile@^6.0.0: "@types/unist" "^3.0.0" vfile-message "^4.0.0" -web-streams-polyfill@^3.0.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b" - integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw== - web-worker@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/web-worker/-/web-worker-1.5.0.tgz#71b2b0fbcc4293e8f0aa4f6b8a3ffebff733dcc5" @@ -4266,7 +4244,7 @@ yoctocolors@^2.1.1: resolved "https://registry.yarnpkg.com/yoctocolors/-/yoctocolors-2.1.2.tgz#d795f54d173494e7d8db93150cec0ed7f678c83a" integrity sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug== -zod@^4.4.3: +zod@^4.3.5, zod@^4.3.6, zod@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/zod/-/zod-4.4.3.tgz#b680f172885d18bbebf21a834ea25e55a1bbf356" integrity sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==