From 7136304c63b16e0ac26017eeab8cd3d3815dd6a9 Mon Sep 17 00:00:00 2001 From: Jackson Chen <90215880+Sma1lboy@users.noreply.github.com> Date: Mon, 3 Mar 2025 14:28:09 -0600 Subject: [PATCH 01/13] feat: add build and start scripts for frontend and backend (#143) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Added dedicated commands for building and starting both the frontend and backend, offering improved operational clarity and separation between environments. - **Chores** - Refined task dependencies and command configurations to enhance performance, streamline development workflows, and simplify maintenance. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- backend/package.json | 2 ++ codefox-common/package.json | 2 ++ frontend/package.json | 3 ++- llm-server/package.json | 6 ++++-- package.json | 6 +++++- turbo.json | 16 ++++++++++++++++ 6 files changed, 31 insertions(+), 4 deletions(-) diff --git a/backend/package.json b/backend/package.json index aaa90edb..a74cf3ff 100644 --- a/backend/package.json +++ b/backend/package.json @@ -8,10 +8,12 @@ "packageManager": "pnpm@9.1.0", "scripts": { "build": "nest build", + "build:backend": "pnpm build", "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "lint": "ts-prune \"{src,apps,libs,test}/**/*.ts\" && eslint \"{src,apps,libs,test}/**/*.ts\" --fix ", "start": "nest start", "start:dev": "NODE_OPTIONS=\"--experimental-specifier-resolution=node\" nest start --watch", + "start:backend": "pnpm start", "dev": "pnpm start:dev", "dev:backend": "pnpm start:dev", "start:debug": "nest start --debug --watch", diff --git a/codefox-common/package.json b/codefox-common/package.json index 8123a03b..d67eef64 100644 --- a/codefox-common/package.json +++ b/codefox-common/package.json @@ -19,6 +19,8 @@ ], "scripts": { "build": "pnpm run build:cjs && pnpm run build:esm && pnpm run build:types", + "build:frontend": "pnpm run build", + "build:backend": "pnpm run build", "build:cjs": "tsc -p tsconfig.cjs.json", "build:esm": "tsc -p tsconfig.esm.json", "build:types": "tsc -p tsconfig.types.json", diff --git a/frontend/package.json b/frontend/package.json index e7c58a36..09b55b32 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,8 +4,9 @@ "private": true, "scripts": { "build": "next build", + "build:frontend": "next build", "dev": "next dev", - "start:dev": "next dev", + "start:frontend": "next start", "start:dev-watch": "tmuxinator start -p .tmuxinator/build.yml", "dev:watch": "tmuxinator start -p .tmuxinator/dev.yml", "start": "next start", diff --git a/llm-server/package.json b/llm-server/package.json index dc217305..2acccfee 100644 --- a/llm-server/package.json +++ b/llm-server/package.json @@ -5,11 +5,13 @@ "type": "module", "scripts": { "start": "NODE_OPTIONS='--enable-source-maps' nodemon --watch \"src/**/*.ts\" --exec \"tsx\" src/main.ts", + "start:backend": "pnpm start", "dev": "NODE_OPTIONS='--enable-source-maps' nodemon --watch \"src/**/*.ts\" --exec \"tsx\" src/main.ts", "dev:backend": "pnpm dev", - "build": " tsc", + "build": "tsc", + "build:backend": "pnpm build", "serve": "node --enable-source-maps dist/main.js", - "format": "prettier --write \"src/**/*.ts\" ", + "format": "prettier --write \"src/**/*.ts\"", "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", "test": "NODE_OPTIONS=--experimental-vm-modules jest --config jest.config.js", "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --config jest.config.js --watch", diff --git a/package.json b/package.json index 2ae4201b..982461ca 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,8 @@ "scripts": { "build": "turbo build", "build:common": "pnpm --filter codefox-common run build", + "build:frontend": "turbo build:frontend", + "build:backend": "turbo build:backend", "dev:turbo": "turbo dev", "dev": "tmuxinator start -p .tmuxinator/dev.yml", "lint": "eslint . --ext .js,.ts,.tsx", @@ -14,7 +16,9 @@ "dev:backend": "turbo dev:backend", "test": "turbo test", "fix": "eslint . --ext .js,.ts,.tsx --fix", - "start": "turbo start" + "start": "turbo start", + "start:frontend": "turbo start:frontend", + "start:backend": "turbo start:backend" }, "postinstall": "pnpm --filter codefox-common run build", "keywords": [], diff --git a/turbo.json b/turbo.json index dd42c2db..54bc2361 100644 --- a/turbo.json +++ b/turbo.json @@ -21,6 +21,22 @@ "start": { "dependsOn": ["^build"], "cache": false + }, + "start:frontend": { + "dependsOn": ["^build"], + "cache": false + }, + "start:backend": { + "dependsOn": ["^build"], + "cache": false + }, + "build:frontend": { + "dependsOn": ["^build:frontend"], + "cache": true + }, + "build:backend": { + "dependsOn": ["^build:backend"], + "cache": true } } } From b6162854e06aaea88a630764718285439e8ee943 Mon Sep 17 00:00:00 2001 From: ZHallen122 <106571949+ZHallen122@users.noreply.github.com> Date: Tue, 4 Mar 2025 18:22:31 -0500 Subject: [PATCH 02/13] Project create limit (#148) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a daily limit for project creation, capping the number of new projects a user can create per day. - Added a new query endpoint that lets users check how many projects they can still create today. - Enhanced error notifications to clearly inform users when their daily project creation limit has been reached. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --- backend/src/project/project-limits.ts | 26 ++++++++++ backend/src/project/project.resolver.ts | 9 ++++ backend/src/project/project.service.ts | 65 ++++++++++++++++++++++++- 3 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 backend/src/project/project-limits.ts diff --git a/backend/src/project/project-limits.ts b/backend/src/project/project-limits.ts new file mode 100644 index 00000000..018c1b88 --- /dev/null +++ b/backend/src/project/project-limits.ts @@ -0,0 +1,26 @@ +import { ForbiddenException, HttpStatus } from '@nestjs/common'; +import { GraphQLError } from 'graphql'; + +export const PROJECT_DAILY_LIMIT = 3; // Maximum number of projects a user can create per day + +export enum ProjectErrorCode { + DAILY_LIMIT_EXCEEDED = 'DAILY_LIMIT_EXCEEDED', +} + +export class ProjectRateLimitException extends ForbiddenException { + constructor(limit: number) { + super( + `Daily project creation limit of ${limit} reached. Please try again tomorrow.`, + ); + } + + getGraphQLError(): GraphQLError { + return new GraphQLError(this.message, { + extensions: { + code: ProjectErrorCode.DAILY_LIMIT_EXCEEDED, + limit: PROJECT_DAILY_LIMIT, + status: HttpStatus.TOO_MANY_REQUESTS, + }, + }); + } +} diff --git a/backend/src/project/project.resolver.ts b/backend/src/project/project.resolver.ts index 944d8174..69686ee5 100644 --- a/backend/src/project/project.resolver.ts +++ b/backend/src/project/project.resolver.ts @@ -7,6 +7,7 @@ import { ResolveField, Parent, ID, + Int, } from '@nestjs/graphql'; import { ProjectService } from './project.service'; import { Project } from './project.model'; @@ -147,4 +148,12 @@ export class ProjectsResolver { ): Promise<Project[]> { return this.projectService.fetchPublicProjects(input); } + + // In ProjectsResolver: + @Query(() => Int) + async getRemainingProjectLimit( + @GetUserIdFromToken() userId: string, + ): Promise<number> { + return this.projectService.getRemainingProjectLimit(userId); + } } diff --git a/backend/src/project/project.service.ts b/backend/src/project/project.service.ts index c30fa75e..61d92f51 100644 --- a/backend/src/project/project.service.ts +++ b/backend/src/project/project.service.ts @@ -7,7 +7,7 @@ import { ForbiddenException, } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; -import { In, Not, Repository } from 'typeorm'; +import { Between, In, Not, Repository } from 'typeorm'; import { Project } from './project.model'; import { ProjectPackages } from './project-packages.model'; import { @@ -26,6 +26,11 @@ import { BuilderContext } from 'src/build-system/context'; import { ChatService } from 'src/chat/chat.service'; import { Chat } from 'src/chat/chat.model'; import { v4 as uuidv4 } from 'uuid'; +import { + PROJECT_DAILY_LIMIT, + ProjectRateLimitException, +} from './project-limits'; + @Injectable() export class ProjectService { private readonly model: OpenAIModelProvider = @@ -119,12 +124,41 @@ export class ProjectService { } } + /** + * Checks if a user has exceeded their daily project creation limit + * @param userId The user ID to check + * @returns A boolean indicating whether the user can create more projects today + */ + async canCreateProject(userId: string): Promise<boolean> { + const today = new Date(); + today.setHours(0, 0, 0, 0); // Start of today + + const tomorrow = new Date(today); + tomorrow.setDate(tomorrow.getDate() + 1); // Start of tomorrow + + // Count projects created by user today + const todayProjectCount = await this.projectsRepository.count({ + where: { + userId: userId, + createdAt: Between(today, tomorrow), + }, + }); + + return todayProjectCount < PROJECT_DAILY_LIMIT; + } + async createProject( input: CreateProjectInput, userId: string, ): Promise<Chat> { try { - // First, handle project name generation if needed (this is the only sync operation we need) + //First check if user have reach the create project limit + const canCreate = await this.canCreateProject(userId); + if (!canCreate) { + throw new ProjectRateLimitException(PROJECT_DAILY_LIMIT); + } + + // handle project name generation if needed (this is the only sync operation we need) let projectName = input.projectName; if (!projectName || projectName === '') { this.logger.debug( @@ -164,6 +198,10 @@ export class ProjectService { // Return chat immediately so user can start interacting return defaultChat; } catch (error) { + if (error instanceof ProjectRateLimitException) { + throw error.getGraphQLError(); // Throw as a GraphQL error for the client + } + this.logger.error( `Error in createProject: ${error.message}`, error.stack, @@ -602,4 +640,27 @@ export class ProjectService { return []; } + + /** + * Gets the number of projects a user can still create today + * @param userId The user ID to check + * @returns The number of remaining projects the user can create today + */ + async getRemainingProjectLimit(userId: string): Promise<number> { + const today = new Date(); + today.setHours(0, 0, 0, 0); // Start of today + + const tomorrow = new Date(today); + tomorrow.setDate(tomorrow.getDate() + 1); // Start of tomorrow + + // Count projects created by this user today + const todayProjectCount = await this.projectsRepository.count({ + where: { + userId: userId, + createdAt: Between(today, tomorrow), + }, + }); + + return Math.max(0, PROJECT_DAILY_LIMIT - todayProjectCount); + } } From 1173bef6d5ceea52a540727280bbb448c9a2f4ab Mon Sep 17 00:00:00 2001 From: Jackson Chen <90215880+Sma1lboy@users.noreply.github.com> Date: Tue, 4 Mar 2025 18:39:26 -0600 Subject: [PATCH 03/13] feat(backend): adding s3 supports, and also expose uploading project images (#154) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - **Enhanced Project Photo Upload:** Users can now update project photos directly through file uploads for a smoother, more reliable experience. - **Expanded Image Flexibility:** The application now supports loading images from any domain, broadening your content sourcing options. - **Improved Upload Performance:** Upgraded file handling ensures consistent and efficient processing for a better overall experience. - **New Configuration Options:** A new example configuration file has been added to guide users on setting up environment variables. - **Bug Fixes** - **Updated Project Photo Mutation:** The mutation for updating project photos has been streamlined, enhancing functionality and usability. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: NarwhalChen <125920907+NarwhalChen@users.noreply.github.com> --- .gitignore | 4 +- backend/.env | 5 - backend/.env.development | 5 - backend/.env.example | 23 + backend/.gitignore | 3 +- backend/package.json | 3 + backend/src/config/config.module.ts | 34 + backend/src/config/config.service.ts | 71 ++ backend/src/config/env.validation.ts | 51 + backend/src/guard/project.guard.ts | 3 + backend/src/main.ts | 7 + backend/src/project/dto/project.input.ts | 12 + backend/src/project/project.module.ts | 6 +- backend/src/project/project.resolver.ts | 28 +- backend/src/project/project.service.ts | 30 +- backend/src/upload/upload.module.ts | 10 + backend/src/upload/upload.service.ts | 193 +++ frontend/next.config.mjs | 2 +- frontend/package.json | 4 +- frontend/src/graphql/request.ts | 11 - frontend/src/graphql/schema.gql | 16 +- frontend/src/graphql/type.tsx | 28 +- frontend/src/lib/client.ts | 22 +- llm-server/.env | 9 - pnpm-lock.yaml | 1471 +++++++++++++++++++--- 25 files changed, 1826 insertions(+), 225 deletions(-) delete mode 100644 backend/.env delete mode 100644 backend/.env.development create mode 100644 backend/.env.example create mode 100644 backend/src/config/config.module.ts create mode 100644 backend/src/config/config.service.ts create mode 100644 backend/src/config/env.validation.ts create mode 100644 backend/src/upload/upload.module.ts create mode 100644 backend/src/upload/upload.service.ts delete mode 100644 llm-server/.env diff --git a/.gitignore b/.gitignore index f5c6219c..7e3c534e 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,6 @@ models/ */**/database.sqlite ./backend/src/database.sqlite -.codefox \ No newline at end of file +.codefox + +.env \ No newline at end of file diff --git a/backend/.env b/backend/.env deleted file mode 100644 index bec5018a..00000000 --- a/backend/.env +++ /dev/null @@ -1,5 +0,0 @@ -PORT=8080 -JWT_SECRET="JACKSONCHENNAHEULALLENPENGYU" -JWT_REFRESH_SECRET="JACKSONCHENNAHEULALLENPENGYUREFRESH" -SALT_ROUNDS=123 -NODE_ENV="DEV" \ No newline at end of file diff --git a/backend/.env.development b/backend/.env.development deleted file mode 100644 index d375f0f8..00000000 --- a/backend/.env.development +++ /dev/null @@ -1,5 +0,0 @@ -PORT=8080 -JWT_SECRET="JACKSONCHENNAHEULALLENPENGYU" -JWT_REFRESH="JACKSONCHENNAHEULALLENPENGYUREFRESH" -SALT_ROUNDS=123 -OPENAI_BASE_URI="http://localhost:3001" diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 00000000..7f8afe80 --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,23 @@ +# Server Configuration +PORT=8080 + +# DEV PROD OR TEST +NODE_ENV="DEV" +# JWT Configuration +JWT_SECRET="your_jwt_secret_here" +JWT_REFRESH="your_jwt_refresh_secret_here" +SALT_ROUNDS=10 + +# OpenAI Configuration +OPENAI_BASE_URI="http://localhost:3001" + +# S3/Cloudflare R2 Configuration (Optional) +# If not provided, local file storage will be used +S3_ACCESS_KEY_ID="your_s3_access_key_id" # Must be 32 characters for Cloudflare R2 +S3_SECRET_ACCESS_KEY="your_s3_secret_access_key" +S3_REGION="auto" # Use 'auto' for Cloudflare R2 +S3_BUCKET_NAME="your_bucket_name" +S3_ENDPOINT="https://<account_id>.r2.cloudflarestorage.com" # Cloudflare R2 endpoint +S3_ACCOUNT_ID="your_cloudflare_account_id" # Your Cloudflare account ID +S3_PUBLIC_URL="https://pub-xxx.r2.dev" # Your R2 public bucket URL + diff --git a/backend/.gitignore b/backend/.gitignore index c493aef4..3ff469a1 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -55,4 +55,5 @@ log-*/ # Backend -/backend/package-lock.json \ No newline at end of file +/backend/package-lock.json +.env \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index a74cf3ff..d084f34d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -28,6 +28,7 @@ }, "dependencies": { "@apollo/server": "^4.11.0", + "@aws-sdk/client-s3": "^3.758.0", "@huggingface/hub": "latest", "@huggingface/transformers": "latest", "@nestjs/apollo": "^12.2.0", @@ -45,6 +46,7 @@ "@types/toposort": "^2.0.7", "axios": "^1.7.7", "bcrypt": "^5.1.1", + "class-transformer": "^0.5.1", "class-validator": "^0.14.1", "dotenv": "^16.4.7", "eslint-plugin-unused-imports": "^4.1.4", @@ -53,6 +55,7 @@ "gpt-3-encoder": "^1.1.4", "graphql": "^16.9.0", "graphql-subscriptions": "^2.0.0", + "graphql-upload-minimal": "^1.6.1", "graphql-ws": "^5.16.0", "lodash": "^4.17.21", "markdown-to-txt": "^2.0.1", diff --git a/backend/src/config/config.module.ts b/backend/src/config/config.module.ts new file mode 100644 index 00000000..37864ce0 --- /dev/null +++ b/backend/src/config/config.module.ts @@ -0,0 +1,34 @@ +import { Module } from '@nestjs/common'; +import { ConfigModule as NestConfigModule } from '@nestjs/config'; +import { AppConfigService } from './config.service'; +import { EnvironmentVariables } from './env.validation'; +import { plainToInstance } from 'class-transformer'; +import { validateSync } from 'class-validator'; + +const validate = (config: Record<string, unknown>) => { + const validatedConfig = plainToInstance(EnvironmentVariables, config, { + enableImplicitConversion: true, + }); + + const errors = validateSync(validatedConfig, { + skipMissingProperties: false, + }); + + if (errors.length > 0) { + throw new Error(errors.toString()); + } + + return validatedConfig; +}; + +@Module({ + imports: [ + NestConfigModule.forRoot({ + validate, + isGlobal: true, + }), + ], + providers: [AppConfigService], + exports: [AppConfigService], +}) +export class AppConfigModule {} diff --git a/backend/src/config/config.service.ts b/backend/src/config/config.service.ts new file mode 100644 index 00000000..686450f0 --- /dev/null +++ b/backend/src/config/config.service.ts @@ -0,0 +1,71 @@ +import { Injectable } from '@nestjs/common'; +import { ConfigService as NestConfigService } from '@nestjs/config'; +import { EnvironmentVariables } from './env.validation'; + +@Injectable() +export class AppConfigService { + constructor(private configService: NestConfigService<EnvironmentVariables>) {} + + /** + * Get server port from environment + */ + get port(): number { + return this.configService.get('PORT'); + } + + /** + * Get JWT secret key for token generation + */ + get jwtSecret(): string { + return this.configService.get('JWT_SECRET'); + } + + /** + * Get JWT refresh token secret + */ + get jwtRefresh(): string { + return this.configService.get('JWT_REFRESH'); + } + + /** + * Get password hashing salt rounds + */ + get saltRounds(): number { + return this.configService.get('SALT_ROUNDS'); + } + + /** + * Get OpenAI API base URI + */ + get openaiBaseUri(): string { + return this.configService.get('OPENAI_BASE_URI'); + } + + /** + * Get S3/Cloudflare R2 configuration object + */ + get s3Config() { + return { + accessKeyId: this.configService.get('S3_ACCESS_KEY_ID'), + secretAccessKey: this.configService.get('S3_SECRET_ACCESS_KEY'), + region: this.configService.get('S3_REGION'), + bucketName: this.configService.get('S3_BUCKET_NAME'), + endpoint: this.configService.get('S3_ENDPOINT'), + accountId: this.configService.get('S3_ACCOUNT_ID'), + publicUrl: this.configService.get('S3_PUBLIC_URL'), + }; + } + + /** + * Check if S3 storage is properly configured + */ + get hasS3Configured(): boolean { + const config = this.s3Config; + return !!( + config.accessKeyId && + config.secretAccessKey && + config.region && + (config.endpoint || config.accountId) + ); + } +} diff --git a/backend/src/config/env.validation.ts b/backend/src/config/env.validation.ts new file mode 100644 index 00000000..0e81f5f3 --- /dev/null +++ b/backend/src/config/env.validation.ts @@ -0,0 +1,51 @@ +import { IsOptional, IsString, IsNumber, IsIn } from 'class-validator'; + +export class EnvironmentVariables { + @IsNumber() + PORT: number; + + @IsString() + @IsIn(['DEV', 'PROD', 'TEST']) + NODE_ENV: string; + + @IsString() + JWT_SECRET: string; + + @IsString() + JWT_REFRESH: string; + + @IsNumber() + SALT_ROUNDS: number; + + @IsString() + OPENAI_BASE_URI: string; + + // S3/Cloudflare R2 Configuration - all optional + @IsOptional() + @IsString() + S3_ACCESS_KEY_ID?: string; + + @IsOptional() + @IsString() + S3_SECRET_ACCESS_KEY?: string; + + @IsOptional() + @IsString() + S3_REGION?: string; + + @IsOptional() + @IsString() + S3_BUCKET_NAME?: string; + + @IsOptional() + @IsString() + S3_ENDPOINT?: string; + + @IsOptional() + @IsString() + S3_ACCOUNT_ID?: string; + + @IsOptional() + @IsString() + S3_PUBLIC_URL?: string; +} diff --git a/backend/src/guard/project.guard.ts b/backend/src/guard/project.guard.ts index a6dac748..9d3ee0d5 100644 --- a/backend/src/guard/project.guard.ts +++ b/backend/src/guard/project.guard.ts @@ -10,6 +10,9 @@ import { JwtService } from '@nestjs/jwt'; import { ProjectService } from '../project/project.service'; +/** + * This guard checks if the user is authorized to access a project. + */ @Injectable() export class ProjectGuard implements CanActivate { private readonly logger = new Logger('ProjectGuard'); diff --git a/backend/src/main.ts b/backend/src/main.ts index f36dff02..b084baaa 100644 --- a/backend/src/main.ts +++ b/backend/src/main.ts @@ -3,6 +3,7 @@ import { AppModule } from './app.module'; import 'reflect-metadata'; import * as dotenv from 'dotenv'; import { Logger } from '@nestjs/common'; +import { graphqlUploadExpress } from 'graphql-upload-minimal'; async function bootstrap() { const logger = new Logger('Bootstrap'); @@ -18,10 +19,16 @@ async function bootstrap() { 'Authorization', 'Access-Control-Allow-Origin', 'Access-Control-Allow-Credentials', + 'Apollo-Require-Preflight', 'x-refresh-token', ], }); + app.use( + '/graphql', + graphqlUploadExpress({ maxFileSize: 50000000, maxFiles: 10 }), + ); + console.log('process.env.PORT:', process.env.PORT); const server = await app.listen(process.env.PORT ?? 8080); logger.log(`Application is running on port ${process.env.PORT ?? 8080}`); diff --git a/backend/src/project/dto/project.input.ts b/backend/src/project/dto/project.input.ts index 26c05551..f6715316 100644 --- a/backend/src/project/dto/project.input.ts +++ b/backend/src/project/dto/project.input.ts @@ -2,6 +2,7 @@ import { InputType, Field, ID, ObjectType } from '@nestjs/graphql'; import { IsNotEmpty, IsString, IsUUID, IsOptional } from 'class-validator'; import { Project } from '../project.model'; +import { FileUpload, GraphQLUpload } from 'graphql-upload-minimal'; /** * @deprecated We don't need project upsert @@ -118,3 +119,14 @@ export class FetchPublicProjectsInputs { @Field() size: number; } + +@InputType() +export class UpdateProjectPhotoInput { + @IsString() + @Field(() => ID) + projectId: string; + + @IsOptional() + @Field(() => GraphQLUpload) + file: FileUpload; +} diff --git a/backend/src/project/project.module.ts b/backend/src/project/project.module.ts index c0be9389..9145f7d2 100644 --- a/backend/src/project/project.module.ts +++ b/backend/src/project/project.module.ts @@ -9,11 +9,15 @@ import { ProjectGuard } from '../guard/project.guard'; import { ChatService } from 'src/chat/chat.service'; import { User } from 'src/user/user.model'; import { Chat } from 'src/chat/chat.model'; +import { AppConfigModule } from 'src/config/config.module'; +import { UploadModule } from 'src/upload/upload.module'; @Module({ imports: [ TypeOrmModule.forFeature([Project, Chat, User, ProjectPackages]), - AuthModule, // Import AuthModule to provide JwtService to the ProjectGuard + AuthModule, + AppConfigModule, + UploadModule, ], providers: [ChatService, ProjectService, ProjectsResolver, ProjectGuard], exports: [ProjectService, ProjectGuard], diff --git a/backend/src/project/project.resolver.ts b/backend/src/project/project.resolver.ts index 69686ee5..ea1ccd34 100644 --- a/backend/src/project/project.resolver.ts +++ b/backend/src/project/project.resolver.ts @@ -15,6 +15,7 @@ import { CreateProjectInput, FetchPublicProjectsInputs, IsValidProjectInput, + UpdateProjectPhotoInput, } from './dto/project.input'; import { Logger, UseGuards } from '@nestjs/common'; import { ProjectGuard } from '../guard/project.guard'; @@ -87,22 +88,33 @@ export class ProjectsResolver { return this.projectService.subscribeToProject(userId, projectId); } + @UseGuards(ProjectGuard) @Mutation(() => Project) - async updateProjectPhotoUrl( + async updateProjectPhoto( @GetUserIdFromToken() userId: string, - @Args('projectId', { type: () => ID }) projectId: string, - @Args('photoUrl') photoUrl: string, + @Args('input') input: UpdateProjectPhotoInput, ): Promise<Project> { - this.logger.log( - `User ${userId} updating photo URL for project ${projectId}`, - ); + const { projectId, file } = input; + this.logger.log(`User ${userId} uploading photo for project ${projectId}`); + + // Extract the file data + const { createReadStream, mimetype } = await file; + + // Buffer the file content + const chunks = []; + for await (const chunk of createReadStream()) { + chunks.push(chunk); + } + const buffer = Buffer.concat(chunks); + + // Call the service with the extracted buffer and mimetype return this.projectService.updateProjectPhotoUrl( userId, projectId, - photoUrl, + buffer, + mimetype, ); } - @Mutation(() => Project) async updateProjectPublicStatus( @GetUserIdFromToken() userId: string, diff --git a/backend/src/project/project.service.ts b/backend/src/project/project.service.ts index 61d92f51..5a057bbe 100644 --- a/backend/src/project/project.service.ts +++ b/backend/src/project/project.service.ts @@ -26,16 +26,17 @@ import { BuilderContext } from 'src/build-system/context'; import { ChatService } from 'src/chat/chat.service'; import { Chat } from 'src/chat/chat.model'; import { v4 as uuidv4 } from 'uuid'; +import { UploadService } from 'src/upload/upload.service'; import { PROJECT_DAILY_LIMIT, ProjectRateLimitException, } from './project-limits'; - @Injectable() export class ProjectService { private readonly model: OpenAIModelProvider = OpenAIModelProvider.getInstance(); private readonly logger = new Logger('ProjectService'); + constructor( @InjectRepository(Project) private projectsRepository: Repository<Project>, @@ -44,6 +45,7 @@ export class ProjectService { @InjectRepository(ProjectPackages) private projectPackagesRepository: Repository<ProjectPackages>, private chatService: ChatService, + private uploadService: UploadService, ) {} async getProjectsByUser(userId: string): Promise<Project[]> { @@ -468,17 +470,35 @@ export class ProjectService { async updateProjectPhotoUrl( userId: string, projectId: string, - photoUrl: string, + file: Buffer, + mimeType: string, ): Promise<Project> { const project = await this.getProjectById(projectId); // Check ownership permission this.checkProjectOwnership(project, userId); - // Update photo URL - project.photoUrl = photoUrl; + try { + // Use the upload service to handle the file upload + const subdirectory = `projects/${projectId}/images`; + const uploadResult = await this.uploadService.upload( + file, + mimeType, + subdirectory, + ); - return this.projectsRepository.save(project); + // Update the project with the new URL + project.photoUrl = uploadResult.url; + + this.logger.debug( + `Updated photo URL for project ${projectId} to ${uploadResult.url}`, + ); + + return this.projectsRepository.save(project); + } catch (error) { + this.logger.error('Error uploading image:', error); + throw new InternalServerErrorException('Failed to upload image:', error); + } } /** diff --git a/backend/src/upload/upload.module.ts b/backend/src/upload/upload.module.ts new file mode 100644 index 00000000..555013bd --- /dev/null +++ b/backend/src/upload/upload.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { UploadService } from './upload.service'; +import { AppConfigModule } from '../config/config.module'; + +@Module({ + imports: [AppConfigModule], + providers: [UploadService], + exports: [UploadService], +}) +export class UploadModule {} diff --git a/backend/src/upload/upload.service.ts b/backend/src/upload/upload.service.ts new file mode 100644 index 00000000..b78e89b7 --- /dev/null +++ b/backend/src/upload/upload.service.ts @@ -0,0 +1,193 @@ +import { Injectable } from '@nestjs/common'; +import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'; +import { v4 as uuidv4 } from 'uuid'; +import path from 'path'; +import { existsSync, mkdirSync, createWriteStream } from 'fs-extra'; +import { finished } from 'stream/promises'; +import { getRootDir } from 'codefox-common'; +import { FileUpload } from 'graphql-upload-minimal'; +import * as fs from 'fs'; +import { AppConfigService } from 'src/config/config.service'; +export interface UploadResult { + url: string; + key: string; +} + +@Injectable() +export class UploadService { + private s3Client: S3Client | null = null; + private readonly mediaDir: string; + + constructor(private configService: AppConfigService) { + // Initialize S3 client if configurations are available + if (this.configService.hasS3Configured) { + const s3Config = this.configService.s3Config; + this.s3Client = new S3Client({ + region: s3Config.region, + endpoint: this.getEndpoint(), + credentials: { + accessKeyId: s3Config.accessKeyId, + secretAccessKey: s3Config.secretAccessKey, + }, + }); + } + + // Initialize media directory for local storage + this.mediaDir = path.join(getRootDir(), 'media'); + if (!existsSync(this.mediaDir)) { + mkdirSync(this.mediaDir, { recursive: true }); + } + } + + /** + * Get the S3 endpoint URL based on configuration + * Constructs the endpoint from account ID if not explicitly provided + * @returns The S3 endpoint URL string + */ + private getEndpoint(): string { + const s3Config = this.configService.s3Config; + + // Use explicit endpoint if provided + if (s3Config.endpoint) { + return s3Config.endpoint; + } + + // Construct endpoint from account ID for Cloudflare R2 + if (s3Config.accountId) { + return `https://${s3Config.accountId}.r2.cloudflarestorage.com`; + } + + // Fallback to default Cloudflare endpoint (should not reach here if hasS3Configured is properly checked) + return 'https://r2.cloudflarestorage.com'; + } + + /** + * Upload a file to either S3/Cloudflare R2 or local storage based on configuration + * @param file File to upload (can be a GraphQL FileUpload or a Buffer) + * @param mimetype File mimetype (required when uploading a Buffer) + * @param subdirectory Directory path to store the file in + * @returns Promise with the upload result + */ + async upload( + file: FileUpload | Buffer, + mimetype?: string, + subdirectory: string = 'uploads', + ): Promise<UploadResult> { + // Generate filename + const fileExtension = mimetype?.split('/')[1] || 'jpg'; + const filename = `${uuidv4()}.${fileExtension}`; + const key = `${subdirectory}/${filename}`; + + // Handle different file input types + if (Buffer.isBuffer(file)) { + // Direct buffer upload + if (!mimetype) { + throw new Error('Mimetype is required when uploading a buffer'); + } + + if (this.s3Client) { + // Upload to S3/Cloudflare R2 + await this.s3Client.send( + new PutObjectCommand({ + Bucket: this.configService.s3Config.bucketName, + Key: key, + Body: file, + ContentType: mimetype, + }), + ); + + // Get the appropriate URL for the uploaded file + const bucketUrl = this.getBucketUrl(); + + return { url: `${bucketUrl}/${key}`, key }; + } else { + // Upload to local storage from buffer + const directory = path.join(this.mediaDir, subdirectory); + if (!existsSync(directory)) { + mkdirSync(directory, { recursive: true }); + } + + const filePath = path.join(directory, filename); + + try { + await fs.promises.writeFile(filePath, file); + return { url: `/media/${key}`, key }; + } catch (error) { + throw new Error(`Failed to upload file: ${error.message}`); + } + } + } else { + // GraphQL FileUpload + const { createReadStream, mimetype: fileMimetype } = await file; + + if (this.s3Client) { + // Convert stream to buffer and upload to S3/Cloudflare R2 + const buffer = await this.streamToBuffer(createReadStream()); + + await this.s3Client.send( + new PutObjectCommand({ + Bucket: this.configService.s3Config.bucketName, + Key: key, + Body: buffer, + ContentType: fileMimetype, + }), + ); + + // Get the appropriate URL for the uploaded file + const bucketUrl = this.getBucketUrl(); + + return { url: `${bucketUrl}/${key}`, key }; + } else { + // Upload to local storage using stream + const directory = path.join(this.mediaDir, subdirectory); + if (!existsSync(directory)) { + mkdirSync(directory, { recursive: true }); + } + + const filePath = path.join(directory, filename); + const writeStream = createWriteStream(filePath); + + createReadStream().pipe(writeStream); + + try { + await finished(writeStream); + return { url: `/media/${key}`, key }; + } catch (error) { + throw new Error(`Failed to upload file: ${error.message}`); + } + } + } + } + + /** + * Convert a readable stream to a buffer + * @param stream Readable stream + * @returns Promise with buffer + */ + private async streamToBuffer(stream: NodeJS.ReadableStream): Promise<Buffer> { + const chunks: Buffer[] = []; + + return new Promise((resolve, reject) => { + stream.on('data', (chunk) => chunks.push(Buffer.from(chunk))); + stream.on('error', (err) => reject(err)); + stream.on('end', () => resolve(Buffer.concat(chunks))); + }); + } + + /** + * Get the bucket URL to use in the returned file URL + * @returns The bucket URL string + */ + private getBucketUrl(): string { + const s3Config = this.configService.s3Config; + + // If a public URL is configured, use it (e.g., CDN or custom domain) + if (s3Config.publicUrl) { + return s3Config.publicUrl; + } + + // Use the constructed endpoint + bucket name + const endpoint = this.getEndpoint(); + return `${endpoint}/${s3Config.bucketName}`; + } +} diff --git a/frontend/next.config.mjs b/frontend/next.config.mjs index 0374b496..c68c442e 100644 --- a/frontend/next.config.mjs +++ b/frontend/next.config.mjs @@ -28,7 +28,7 @@ const nextConfig = { remotePatterns: [ { protocol: 'https', - hostname: 'picsum.photos', + hostname: '*', pathname: '/**', }, ], diff --git a/frontend/package.json b/frontend/package.json index 09b55b32..ffb7364b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -16,7 +16,6 @@ "generate:watch": "graphql-codegen --watch" }, "dependencies": { - "codefox-common": "workspace:*", "@apollo/client": "^3.11.8", "@emoji-mart/data": "^1.2.1", "@emoji-mart/react": "^1.1.1", @@ -38,9 +37,12 @@ "@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.6", "@radix-ui/react-visually-hidden": "^1.1.1", + "@types/apollo-upload-client": "^18.0.0", "@types/dom-speech-recognition": "^0.0.4", + "apollo-upload-client": "^18.0.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "codefox-common": "workspace:*", "emoji-mart": "^5.6.0", "framer-motion": "^11.18.2", "graphql": "^16.9.0", diff --git a/frontend/src/graphql/request.ts b/frontend/src/graphql/request.ts index 7b099dd6..3a0d5fef 100644 --- a/frontend/src/graphql/request.ts +++ b/frontend/src/graphql/request.ts @@ -209,17 +209,6 @@ export const UPDATE_PROJECT_PUBLIC_STATUS = gql` } `; -// Mutation to update project photo URL -export const UPDATE_PROJECT_PHOTO_URL = gql` - mutation UpdateProjectPhotoUrl($projectId: ID!, $photoUrl: String!) { - updateProjectPhotoUrl(projectId: $projectId, photoUrl: $photoUrl) { - id - projectName - photoUrl - } - } -`; - // Query to get subscribed/forked projects export const GET_SUBSCRIBED_PROJECTS = gql` query GetSubscribedProjects { diff --git a/frontend/src/graphql/schema.gql b/frontend/src/graphql/schema.gql index 7a256c75..c637f81d 100644 --- a/frontend/src/graphql/schema.gql +++ b/frontend/src/graphql/schema.gql @@ -54,9 +54,7 @@ input CreateProjectInput { public: Boolean } -""" -Date custom scalar type -""" +"""Date custom scalar type""" scalar Date input FetchPublicProjectsInputs { @@ -115,7 +113,7 @@ type Mutation { subscribeToProject(projectId: ID!): Project! triggerChatStream(input: ChatInputType!): Boolean! updateChatTitle(updateChatTitleInput: UpdateChatTitleInput!): Chat - updateProjectPhotoUrl(photoUrl: String!, projectId: ID!): Project! + updateProjectPhoto(input: UpdateProjectPhotoInput!): Project! updateProjectPublicStatus(isPublic: Boolean!, projectId: ID!): Project! } @@ -210,6 +208,14 @@ input UpdateChatTitleInput { title: String } +input UpdateProjectPhotoInput { + file: Upload! + projectId: ID! +} + +"""The `Upload` scalar type represents a file upload.""" +scalar Upload + type User { chats: [Chat!]! createdAt: Date! @@ -221,4 +227,4 @@ type User { subscribedProjects: [Project!] @deprecated(reason: "Use projects with forkedFromId instead") updatedAt: Date! username: String! -} +} \ No newline at end of file diff --git a/frontend/src/graphql/type.tsx b/frontend/src/graphql/type.tsx index 554833ce..9f8b8b8f 100644 --- a/frontend/src/graphql/type.tsx +++ b/frontend/src/graphql/type.tsx @@ -36,6 +36,8 @@ export type Scalars = { Float: { input: number; output: number }; /** Date custom scalar type */ Date: { input: Date; output: Date }; + /** The `Upload` scalar type represents a file upload. */ + Upload: { input: any; output: any }; }; export type Chat = { @@ -154,7 +156,7 @@ export type Mutation = { subscribeToProject: Project; triggerChatStream: Scalars['Boolean']['output']; updateChatTitle?: Maybe<Chat>; - updateProjectPhotoUrl: Project; + updateProjectPhoto: Project; updateProjectPublicStatus: Project; }; @@ -210,9 +212,8 @@ export type MutationUpdateChatTitleArgs = { updateChatTitleInput: UpdateChatTitleInput; }; -export type MutationUpdateProjectPhotoUrlArgs = { - photoUrl: Scalars['String']['input']; - projectId: Scalars['ID']['input']; +export type MutationUpdateProjectPhotoArgs = { + input: UpdateProjectPhotoInput; }; export type MutationUpdateProjectPublicStatusArgs = { @@ -336,6 +337,11 @@ export type UpdateChatTitleInput = { title?: InputMaybe<Scalars['String']['input']>; }; +export type UpdateProjectPhotoInput = { + file: Scalars['Upload']['input']; + projectId: Scalars['ID']['input']; +}; + export type User = { __typename: 'User'; chats: Array<Chat>; @@ -491,6 +497,8 @@ export type ResolversTypes = ResolversObject<{ String: ResolverTypeWrapper<Scalars['String']['output']>; Subscription: ResolverTypeWrapper<{}>; UpdateChatTitleInput: UpdateChatTitleInput; + UpdateProjectPhotoInput: UpdateProjectPhotoInput; + Upload: ResolverTypeWrapper<Scalars['Upload']['output']>; User: ResolverTypeWrapper<User>; }>; @@ -524,6 +532,8 @@ export type ResolversParentTypes = ResolversObject<{ String: Scalars['String']['output']; Subscription: {}; UpdateChatTitleInput: UpdateChatTitleInput; + UpdateProjectPhotoInput: UpdateProjectPhotoInput; + Upload: Scalars['Upload']['output']; User: User; }>; @@ -730,11 +740,11 @@ export type MutationResolvers< ContextType, RequireFields<MutationUpdateChatTitleArgs, 'updateChatTitleInput'> >; - updateProjectPhotoUrl?: Resolver< + updateProjectPhoto?: Resolver< ResolversTypes['Project'], ParentType, ContextType, - RequireFields<MutationUpdateProjectPhotoUrlArgs, 'photoUrl' | 'projectId'> + RequireFields<MutationUpdateProjectPhotoArgs, 'input'> >; updateProjectPublicStatus?: Resolver< ResolversTypes['Project'], @@ -900,6 +910,11 @@ export type SubscriptionResolvers< >; }>; +export interface UploadScalarConfig + extends GraphQLScalarTypeConfig<ResolversTypes['Upload'], any> { + name: 'Upload'; +} + export type UserResolvers< ContextType = any, ParentType extends @@ -941,5 +956,6 @@ export type Resolvers<ContextType = any> = ResolversObject<{ Query?: QueryResolvers<ContextType>; RefreshTokenResponse?: RefreshTokenResponseResolvers<ContextType>; Subscription?: SubscriptionResolvers<ContextType>; + Upload?: GraphQLScalarType; User?: UserResolvers<ContextType>; }>; diff --git a/frontend/src/lib/client.ts b/frontend/src/lib/client.ts index d9fe6586..9f8aa7d2 100644 --- a/frontend/src/lib/client.ts +++ b/frontend/src/lib/client.ts @@ -3,7 +3,6 @@ import { ApolloClient, InMemoryCache, - HttpLink, ApolloLink, from, split, @@ -12,13 +11,14 @@ import { onError } from '@apollo/client/link/error'; import { GraphQLWsLink } from '@apollo/client/link/subscriptions'; import { createClient } from 'graphql-ws'; import { getMainDefinition } from '@apollo/client/utilities'; +import createUploadLink from 'apollo-upload-client/createUploadLink.mjs'; import { LocalStore } from '@/lib/storage'; -// HTTP Link -const httpLink = new HttpLink({ +// Create the upload link as the terminating link +const uploadLink = createUploadLink({ uri: process.env.NEXT_PUBLIC_GRAPHQL_URL || 'http://localhost:8080/graphql', headers: { - 'Content-Type': 'application/json', + 'Apollo-Require-Preflight': 'true', 'Access-Control-Allow-Credentials': 'true', 'Access-Control-Allow-Origin': '*', }, @@ -85,7 +85,15 @@ const errorLink = onError(({ graphQLErrors, networkError }) => { } }); -// Split traffic based on operation type +// Build the HTTP link chain +const httpLinkWithMiddleware = from([ + errorLink, + requestLoggingMiddleware, + authMiddleware, + uploadLink as unknown as ApolloLink, // Cast to ApolloLink to satisfy TypeScript +]); + +// Split traffic between WebSocket and HTTP const splitLink = wsLink ? split( ({ query }) => { @@ -96,9 +104,9 @@ const splitLink = wsLink ); }, wsLink, - from([errorLink, requestLoggingMiddleware, authMiddleware, httpLink]) + httpLinkWithMiddleware ) - : from([errorLink, requestLoggingMiddleware, authMiddleware, httpLink]); + : httpLinkWithMiddleware; // Create Apollo Client export const client = new ApolloClient({ diff --git a/llm-server/.env b/llm-server/.env deleted file mode 100644 index e30ddc6f..00000000 --- a/llm-server/.env +++ /dev/null @@ -1,9 +0,0 @@ -# Server Configuration -PORT=8001 - -# Model Configuration -MODEL_PATH=./models -MODEL_TYPE=llama - -# Other Configuration -NODE_ENV=development \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 553258ac..814f243e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,6 +36,9 @@ importers: '@apollo/server': specifier: ^4.11.0 version: 4.11.3(graphql@16.10.0) + '@aws-sdk/client-s3': + specifier: ^3.758.0 + version: 3.758.0 '@huggingface/hub': specifier: latest version: 1.0.1 @@ -50,7 +53,7 @@ importers: version: 3.1.3(@nestjs/common@10.4.15)(axios@1.8.1)(rxjs@7.8.2) '@nestjs/common': specifier: ^10.0.0 - version: 10.4.15(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + version: 10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/config': specifier: ^3.2.3 version: 3.3.0(@nestjs/common@10.4.15)(rxjs@7.8.2) @@ -59,7 +62,7 @@ importers: version: 10.4.15(@nestjs/common@10.4.15)(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/graphql': specifier: ^12.2.0 - version: 12.2.2(@nestjs/common@10.4.15)(@nestjs/core@10.4.15)(class-validator@0.14.1)(graphql@16.10.0)(reflect-metadata@0.2.2) + version: 12.2.2(@nestjs/common@10.4.15)(@nestjs/core@10.4.15)(class-transformer@0.5.1)(class-validator@0.14.1)(graphql@16.10.0)(reflect-metadata@0.2.2) '@nestjs/jwt': specifier: ^10.2.0 version: 10.2.0(@nestjs/common@10.4.15) @@ -87,6 +90,9 @@ importers: bcrypt: specifier: ^5.1.1 version: 5.1.1 + class-transformer: + specifier: ^0.5.1 + version: 0.5.1 class-validator: specifier: ^0.14.1 version: 0.14.1 @@ -111,6 +117,9 @@ importers: graphql-subscriptions: specifier: ^2.0.0 version: 2.0.0(graphql@16.10.0) + graphql-upload-minimal: + specifier: ^1.6.1 + version: 1.6.1(graphql@16.10.0) graphql-ws: specifier: ^5.16.0 version: 5.16.2(graphql@16.10.0) @@ -233,7 +242,7 @@ importers: devDependencies: '@nestjs/common': specifier: 10.4.15 - version: 10.4.15(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + version: 10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@types/fs-extra': specifier: ^11.0.4 version: 11.0.4 @@ -327,7 +336,7 @@ importers: version: 3.10.0(react-hook-form@7.54.2) '@langchain/community': specifier: ^0.3.1 - version: 0.3.33(@browserbasehq/stagehand@1.13.1)(@ibm-cloud/watsonx-ai@1.5.1)(@langchain/core@0.3.42)(axios@1.7.9)(ibm-cloud-sdk-core@5.1.3)(openai@4.86.1)(ws@8.18.1) + version: 0.3.34(@browserbasehq/stagehand@1.13.1)(@ibm-cloud/watsonx-ai@1.5.1)(@langchain/core@0.3.42)(axios@1.7.9)(ibm-cloud-sdk-core@5.1.3)(openai@4.86.1)(ws@8.18.1) '@langchain/core': specifier: ^0.3.3 version: 0.3.42(openai@4.86.1) @@ -336,7 +345,7 @@ importers: version: 4.7.0(monaco-editor@0.52.2)(react-dom@18.3.1)(react@18.3.1) '@nestjs/common': specifier: ^10.4.6 - version: 10.4.15(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + version: 10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@radix-ui/react-avatar': specifier: ^1.1.0 version: 1.1.3(@types/react-dom@18.3.5)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1) @@ -376,9 +385,15 @@ importers: '@radix-ui/react-visually-hidden': specifier: ^1.1.1 version: 1.1.2(@types/react-dom@18.3.5)(@types/react@18.3.18)(react-dom@18.3.1)(react@18.3.1) + '@types/apollo-upload-client': + specifier: ^18.0.0 + version: 18.0.0(@types/react@18.3.18)(graphql-ws@5.16.2)(react-dom@18.3.1)(react@18.3.1)(subscriptions-transport-ws@0.11.0) '@types/dom-speech-recognition': specifier: ^0.0.4 version: 0.0.4 + apollo-upload-client: + specifier: ^18.0.0 + version: 18.0.1(@apollo/client@3.13.1)(graphql@16.10.0) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -405,7 +420,7 @@ importers: version: 0.445.0(react@18.3.1) motion: specifier: ^12.4.7 - version: 12.4.9(react-dom@18.3.1)(react@18.3.1) + version: 12.4.10(react-dom@18.3.1)(react@18.3.1) next: specifier: ^14.2.13 version: 14.2.24(@babel/core@7.26.9)(@playwright/test@1.50.1)(react-dom@18.3.1)(react@18.3.1) @@ -417,7 +432,7 @@ importers: version: 18.3.1 react-activity-calendar: specifier: ^2.7.8 - version: 2.7.8(react@18.3.1) + version: 2.7.9(react@18.3.1) react-code-blocks: specifier: ^0.1.6 version: 0.1.6(react-dom@18.3.1)(react@18.3.1) @@ -556,7 +571,7 @@ importers: version: 3.3.3 '@nestjs/common': specifier: ^10.4.5 - version: 10.4.15(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + version: 10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@types/axios': specifier: ^0.14.4 version: 0.14.4 @@ -660,8 +675,8 @@ importers: packages: - /@0no-co/graphql.web@1.1.1(graphql@16.10.0): - resolution: {integrity: sha512-F2i3xdycesw78QCOBHmpTn7eaD2iNXGwB2gkfwxcOfBbeauYpr8RBSyJOkDrFtKtVRMclg8Sg3n1ip0ACyUuag==} + /@0no-co/graphql.web@1.1.2(graphql@16.10.0): + resolution: {integrity: sha512-N2NGsU5FLBhT8NZ+3l2YrzZSHITjNXNuDhC4iDiikv0IujaJ0Xc6xIxQZ/Ek3Cb+rgPjnLHYyJm11tInuJn+cw==} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 peerDependenciesMeta: @@ -1347,6 +1362,577 @@ packages: - encoding dev: true + /@aws-crypto/crc32@5.2.0: + resolution: {integrity: sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==} + engines: {node: '>=16.0.0'} + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.734.0 + tslib: 2.8.1 + dev: false + + /@aws-crypto/crc32c@5.2.0: + resolution: {integrity: sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==} + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.734.0 + tslib: 2.8.1 + dev: false + + /@aws-crypto/sha1-browser@5.2.0: + resolution: {integrity: sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==} + dependencies: + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.734.0 + '@aws-sdk/util-locate-window': 3.723.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + dev: false + + /@aws-crypto/sha256-browser@5.2.0: + resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} + dependencies: + '@aws-crypto/sha256-js': 5.2.0 + '@aws-crypto/supports-web-crypto': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.734.0 + '@aws-sdk/util-locate-window': 3.723.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + dev: false + + /@aws-crypto/sha256-js@5.2.0: + resolution: {integrity: sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==} + engines: {node: '>=16.0.0'} + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.734.0 + tslib: 2.8.1 + dev: false + + /@aws-crypto/supports-web-crypto@5.2.0: + resolution: {integrity: sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==} + dependencies: + tslib: 2.8.1 + dev: false + + /@aws-crypto/util@5.2.0: + resolution: {integrity: sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==} + dependencies: + '@aws-sdk/types': 3.734.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/client-s3@3.758.0: + resolution: {integrity: sha512-f8SlhU9/93OC/WEI6xVJf/x/GoQFj9a/xXK6QCtr5fvCjfSLgMVFmKTiIl/tgtDRzxUDc8YS6EGtbHjJ3Y/atg==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-crypto/sha1-browser': 5.2.0 + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.758.0 + '@aws-sdk/credential-provider-node': 3.758.0 + '@aws-sdk/middleware-bucket-endpoint': 3.734.0 + '@aws-sdk/middleware-expect-continue': 3.734.0 + '@aws-sdk/middleware-flexible-checksums': 3.758.0 + '@aws-sdk/middleware-host-header': 3.734.0 + '@aws-sdk/middleware-location-constraint': 3.734.0 + '@aws-sdk/middleware-logger': 3.734.0 + '@aws-sdk/middleware-recursion-detection': 3.734.0 + '@aws-sdk/middleware-sdk-s3': 3.758.0 + '@aws-sdk/middleware-ssec': 3.734.0 + '@aws-sdk/middleware-user-agent': 3.758.0 + '@aws-sdk/region-config-resolver': 3.734.0 + '@aws-sdk/signature-v4-multi-region': 3.758.0 + '@aws-sdk/types': 3.734.0 + '@aws-sdk/util-endpoints': 3.743.0 + '@aws-sdk/util-user-agent-browser': 3.734.0 + '@aws-sdk/util-user-agent-node': 3.758.0 + '@aws-sdk/xml-builder': 3.734.0 + '@smithy/config-resolver': 4.0.1 + '@smithy/core': 3.1.5 + '@smithy/eventstream-serde-browser': 4.0.1 + '@smithy/eventstream-serde-config-resolver': 4.0.1 + '@smithy/eventstream-serde-node': 4.0.1 + '@smithy/fetch-http-handler': 5.0.1 + '@smithy/hash-blob-browser': 4.0.1 + '@smithy/hash-node': 4.0.1 + '@smithy/hash-stream-node': 4.0.1 + '@smithy/invalid-dependency': 4.0.1 + '@smithy/md5-js': 4.0.1 + '@smithy/middleware-content-length': 4.0.1 + '@smithy/middleware-endpoint': 4.0.6 + '@smithy/middleware-retry': 4.0.7 + '@smithy/middleware-serde': 4.0.2 + '@smithy/middleware-stack': 4.0.1 + '@smithy/node-config-provider': 4.0.1 + '@smithy/node-http-handler': 4.0.3 + '@smithy/protocol-http': 5.0.1 + '@smithy/smithy-client': 4.1.6 + '@smithy/types': 4.1.0 + '@smithy/url-parser': 4.0.1 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.7 + '@smithy/util-defaults-mode-node': 4.0.7 + '@smithy/util-endpoints': 3.0.1 + '@smithy/util-middleware': 4.0.1 + '@smithy/util-retry': 4.0.1 + '@smithy/util-stream': 4.1.2 + '@smithy/util-utf8': 4.0.0 + '@smithy/util-waiter': 4.0.2 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + dev: false + + /@aws-sdk/client-sso@3.758.0: + resolution: {integrity: sha512-BoGO6IIWrLyLxQG6txJw6RT2urmbtlwfggapNCrNPyYjlXpzTSJhBYjndg7TpDATFd0SXL0zm8y/tXsUXNkdYQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.758.0 + '@aws-sdk/middleware-host-header': 3.734.0 + '@aws-sdk/middleware-logger': 3.734.0 + '@aws-sdk/middleware-recursion-detection': 3.734.0 + '@aws-sdk/middleware-user-agent': 3.758.0 + '@aws-sdk/region-config-resolver': 3.734.0 + '@aws-sdk/types': 3.734.0 + '@aws-sdk/util-endpoints': 3.743.0 + '@aws-sdk/util-user-agent-browser': 3.734.0 + '@aws-sdk/util-user-agent-node': 3.758.0 + '@smithy/config-resolver': 4.0.1 + '@smithy/core': 3.1.5 + '@smithy/fetch-http-handler': 5.0.1 + '@smithy/hash-node': 4.0.1 + '@smithy/invalid-dependency': 4.0.1 + '@smithy/middleware-content-length': 4.0.1 + '@smithy/middleware-endpoint': 4.0.6 + '@smithy/middleware-retry': 4.0.7 + '@smithy/middleware-serde': 4.0.2 + '@smithy/middleware-stack': 4.0.1 + '@smithy/node-config-provider': 4.0.1 + '@smithy/node-http-handler': 4.0.3 + '@smithy/protocol-http': 5.0.1 + '@smithy/smithy-client': 4.1.6 + '@smithy/types': 4.1.0 + '@smithy/url-parser': 4.0.1 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.7 + '@smithy/util-defaults-mode-node': 4.0.7 + '@smithy/util-endpoints': 3.0.1 + '@smithy/util-middleware': 4.0.1 + '@smithy/util-retry': 4.0.1 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + dev: false + + /@aws-sdk/core@3.758.0: + resolution: {integrity: sha512-0RswbdR9jt/XKemaLNuxi2gGr4xGlHyGxkTdhSQzCyUe9A9OPCoLl3rIESRguQEech+oJnbHk/wuiwHqTuP9sg==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.734.0 + '@smithy/core': 3.1.5 + '@smithy/node-config-provider': 4.0.1 + '@smithy/property-provider': 4.0.1 + '@smithy/protocol-http': 5.0.1 + '@smithy/signature-v4': 5.0.1 + '@smithy/smithy-client': 4.1.6 + '@smithy/types': 4.1.0 + '@smithy/util-middleware': 4.0.1 + fast-xml-parser: 4.4.1 + tslib: 2.8.1 + dev: false + + /@aws-sdk/credential-provider-env@3.758.0: + resolution: {integrity: sha512-N27eFoRrO6MeUNumtNHDW9WOiwfd59LPXPqDrIa3kWL/s+fOKFHb9xIcF++bAwtcZnAxKkgpDCUP+INNZskE+w==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/core': 3.758.0 + '@aws-sdk/types': 3.734.0 + '@smithy/property-provider': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/credential-provider-http@3.758.0: + resolution: {integrity: sha512-Xt9/U8qUCiw1hihztWkNeIR+arg6P+yda10OuCHX6kFVx3auTlU7+hCqs3UxqniGU4dguHuftf3mRpi5/GJ33Q==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/core': 3.758.0 + '@aws-sdk/types': 3.734.0 + '@smithy/fetch-http-handler': 5.0.1 + '@smithy/node-http-handler': 4.0.3 + '@smithy/property-provider': 4.0.1 + '@smithy/protocol-http': 5.0.1 + '@smithy/smithy-client': 4.1.6 + '@smithy/types': 4.1.0 + '@smithy/util-stream': 4.1.2 + tslib: 2.8.1 + dev: false + + /@aws-sdk/credential-provider-ini@3.758.0: + resolution: {integrity: sha512-cymSKMcP5d+OsgetoIZ5QCe1wnp2Q/tq+uIxVdh9MbfdBBEnl9Ecq6dH6VlYS89sp4QKuxHxkWXVnbXU3Q19Aw==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/core': 3.758.0 + '@aws-sdk/credential-provider-env': 3.758.0 + '@aws-sdk/credential-provider-http': 3.758.0 + '@aws-sdk/credential-provider-process': 3.758.0 + '@aws-sdk/credential-provider-sso': 3.758.0 + '@aws-sdk/credential-provider-web-identity': 3.758.0 + '@aws-sdk/nested-clients': 3.758.0 + '@aws-sdk/types': 3.734.0 + '@smithy/credential-provider-imds': 4.0.1 + '@smithy/property-provider': 4.0.1 + '@smithy/shared-ini-file-loader': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + dev: false + + /@aws-sdk/credential-provider-node@3.758.0: + resolution: {integrity: sha512-+DaMv63wiq7pJrhIQzZYMn4hSarKiizDoJRvyR7WGhnn0oQ/getX9Z0VNCV3i7lIFoLNTb7WMmQ9k7+z/uD5EQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/credential-provider-env': 3.758.0 + '@aws-sdk/credential-provider-http': 3.758.0 + '@aws-sdk/credential-provider-ini': 3.758.0 + '@aws-sdk/credential-provider-process': 3.758.0 + '@aws-sdk/credential-provider-sso': 3.758.0 + '@aws-sdk/credential-provider-web-identity': 3.758.0 + '@aws-sdk/types': 3.734.0 + '@smithy/credential-provider-imds': 4.0.1 + '@smithy/property-provider': 4.0.1 + '@smithy/shared-ini-file-loader': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + dev: false + + /@aws-sdk/credential-provider-process@3.758.0: + resolution: {integrity: sha512-AzcY74QTPqcbXWVgjpPZ3HOmxQZYPROIBz2YINF0OQk0MhezDWV/O7Xec+K1+MPGQO3qS6EDrUUlnPLjsqieHA==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/core': 3.758.0 + '@aws-sdk/types': 3.734.0 + '@smithy/property-provider': 4.0.1 + '@smithy/shared-ini-file-loader': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/credential-provider-sso@3.758.0: + resolution: {integrity: sha512-x0FYJqcOLUCv8GLLFDYMXRAQKGjoM+L0BG4BiHYZRDf24yQWFCAZsCQAYKo6XZYh2qznbsW6f//qpyJ5b0QVKQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/client-sso': 3.758.0 + '@aws-sdk/core': 3.758.0 + '@aws-sdk/token-providers': 3.758.0 + '@aws-sdk/types': 3.734.0 + '@smithy/property-provider': 4.0.1 + '@smithy/shared-ini-file-loader': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + dev: false + + /@aws-sdk/credential-provider-web-identity@3.758.0: + resolution: {integrity: sha512-XGguXhBqiCXMXRxcfCAVPlMbm3VyJTou79r/3mxWddHWF0XbhaQiBIbUz6vobVTD25YQRbWSmSch7VA8kI5Lrw==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/core': 3.758.0 + '@aws-sdk/nested-clients': 3.758.0 + '@aws-sdk/types': 3.734.0 + '@smithy/property-provider': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + dev: false + + /@aws-sdk/middleware-bucket-endpoint@3.734.0: + resolution: {integrity: sha512-etC7G18aF7KdZguW27GE/wpbrNmYLVT755EsFc8kXpZj8D6AFKxc7OuveinJmiy0bYXAMspJUWsF6CrGpOw6CQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.734.0 + '@aws-sdk/util-arn-parser': 3.723.0 + '@smithy/node-config-provider': 4.0.1 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 + '@smithy/util-config-provider': 4.0.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/middleware-expect-continue@3.734.0: + resolution: {integrity: sha512-P38/v1l6HjuB2aFUewt7ueAW5IvKkFcv5dalPtbMGRhLeyivBOHwbCyuRKgVs7z7ClTpu9EaViEGki2jEQqEsQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.734.0 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/middleware-flexible-checksums@3.758.0: + resolution: {integrity: sha512-o8Rk71S08YTKLoSobucjnbj97OCGaXgpEDNKXpXaavUM5xLNoHCLSUPRCiEN86Ivqxg1n17Y2nSRhfbsveOXXA==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@aws-crypto/crc32c': 5.2.0 + '@aws-crypto/util': 5.2.0 + '@aws-sdk/core': 3.758.0 + '@aws-sdk/types': 3.734.0 + '@smithy/is-array-buffer': 4.0.0 + '@smithy/node-config-provider': 4.0.1 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 + '@smithy/util-middleware': 4.0.1 + '@smithy/util-stream': 4.1.2 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/middleware-host-header@3.734.0: + resolution: {integrity: sha512-LW7RRgSOHHBzWZnigNsDIzu3AiwtjeI2X66v+Wn1P1u+eXssy1+up4ZY/h+t2sU4LU36UvEf+jrZti9c6vRnFw==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.734.0 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/middleware-location-constraint@3.734.0: + resolution: {integrity: sha512-EJEIXwCQhto/cBfHdm3ZOeLxd2NlJD+X2F+ZTOxzokuhBtY0IONfC/91hOo5tWQweerojwshSMHRCKzRv1tlwg==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.734.0 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/middleware-logger@3.734.0: + resolution: {integrity: sha512-mUMFITpJUW3LcKvFok176eI5zXAUomVtahb9IQBwLzkqFYOrMJvWAvoV4yuxrJ8TlQBG8gyEnkb9SnhZvjg67w==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.734.0 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/middleware-recursion-detection@3.734.0: + resolution: {integrity: sha512-CUat2d9ITsFc2XsmeiRQO96iWpxSKYFjxvj27Hc7vo87YUHRnfMfnc8jw1EpxEwMcvBD7LsRa6vDNky6AjcrFA==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.734.0 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/middleware-sdk-s3@3.758.0: + resolution: {integrity: sha512-6mJ2zyyHPYSV6bAcaFpsdoXZJeQlR1QgBnZZ6juY/+dcYiuyWCdyLUbGzSZSE7GTfx6i+9+QWFeoIMlWKgU63A==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/core': 3.758.0 + '@aws-sdk/types': 3.734.0 + '@aws-sdk/util-arn-parser': 3.723.0 + '@smithy/core': 3.1.5 + '@smithy/node-config-provider': 4.0.1 + '@smithy/protocol-http': 5.0.1 + '@smithy/signature-v4': 5.0.1 + '@smithy/smithy-client': 4.1.6 + '@smithy/types': 4.1.0 + '@smithy/util-config-provider': 4.0.0 + '@smithy/util-middleware': 4.0.1 + '@smithy/util-stream': 4.1.2 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/middleware-ssec@3.734.0: + resolution: {integrity: sha512-d4yd1RrPW/sspEXizq2NSOUivnheac6LPeLSLnaeTbBG9g1KqIqvCzP1TfXEqv2CrWfHEsWtJpX7oyjySSPvDQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.734.0 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/middleware-user-agent@3.758.0: + resolution: {integrity: sha512-iNyehQXtQlj69JCgfaOssgZD4HeYGOwxcaKeG6F+40cwBjTAi0+Ph1yfDwqk2qiBPIRWJ/9l2LodZbxiBqgrwg==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/core': 3.758.0 + '@aws-sdk/types': 3.734.0 + '@aws-sdk/util-endpoints': 3.743.0 + '@smithy/core': 3.1.5 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/nested-clients@3.758.0: + resolution: {integrity: sha512-YZ5s7PSvyF3Mt2h1EQulCG93uybprNGbBkPmVuy/HMMfbFTt4iL3SbKjxqvOZelm86epFfj7pvK7FliI2WOEcg==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-crypto/sha256-browser': 5.2.0 + '@aws-crypto/sha256-js': 5.2.0 + '@aws-sdk/core': 3.758.0 + '@aws-sdk/middleware-host-header': 3.734.0 + '@aws-sdk/middleware-logger': 3.734.0 + '@aws-sdk/middleware-recursion-detection': 3.734.0 + '@aws-sdk/middleware-user-agent': 3.758.0 + '@aws-sdk/region-config-resolver': 3.734.0 + '@aws-sdk/types': 3.734.0 + '@aws-sdk/util-endpoints': 3.743.0 + '@aws-sdk/util-user-agent-browser': 3.734.0 + '@aws-sdk/util-user-agent-node': 3.758.0 + '@smithy/config-resolver': 4.0.1 + '@smithy/core': 3.1.5 + '@smithy/fetch-http-handler': 5.0.1 + '@smithy/hash-node': 4.0.1 + '@smithy/invalid-dependency': 4.0.1 + '@smithy/middleware-content-length': 4.0.1 + '@smithy/middleware-endpoint': 4.0.6 + '@smithy/middleware-retry': 4.0.7 + '@smithy/middleware-serde': 4.0.2 + '@smithy/middleware-stack': 4.0.1 + '@smithy/node-config-provider': 4.0.1 + '@smithy/node-http-handler': 4.0.3 + '@smithy/protocol-http': 5.0.1 + '@smithy/smithy-client': 4.1.6 + '@smithy/types': 4.1.0 + '@smithy/url-parser': 4.0.1 + '@smithy/util-base64': 4.0.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-body-length-node': 4.0.0 + '@smithy/util-defaults-mode-browser': 4.0.7 + '@smithy/util-defaults-mode-node': 4.0.7 + '@smithy/util-endpoints': 3.0.1 + '@smithy/util-middleware': 4.0.1 + '@smithy/util-retry': 4.0.1 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + dev: false + + /@aws-sdk/region-config-resolver@3.734.0: + resolution: {integrity: sha512-Lvj1kPRC5IuJBr9DyJ9T9/plkh+EfKLy+12s/mykOy1JaKHDpvj+XGy2YO6YgYVOb8JFtaqloid+5COtje4JTQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.734.0 + '@smithy/node-config-provider': 4.0.1 + '@smithy/types': 4.1.0 + '@smithy/util-config-provider': 4.0.0 + '@smithy/util-middleware': 4.0.1 + tslib: 2.8.1 + dev: false + + /@aws-sdk/signature-v4-multi-region@3.758.0: + resolution: {integrity: sha512-0RPCo8fYJcrenJ6bRtiUbFOSgQ1CX/GpvwtLU2Fam1tS9h2klKK8d74caeV6A1mIUvBU7bhyQ0wMGlwMtn3EYw==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/middleware-sdk-s3': 3.758.0 + '@aws-sdk/types': 3.734.0 + '@smithy/protocol-http': 5.0.1 + '@smithy/signature-v4': 5.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/token-providers@3.758.0: + resolution: {integrity: sha512-ckptN1tNrIfQUaGWm/ayW1ddG+imbKN7HHhjFdS4VfItsP0QQOB0+Ov+tpgb4MoNR4JaUghMIVStjIeHN2ks1w==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/nested-clients': 3.758.0 + '@aws-sdk/types': 3.734.0 + '@smithy/property-provider': 4.0.1 + '@smithy/shared-ini-file-loader': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + transitivePeerDependencies: + - aws-crt + dev: false + + /@aws-sdk/types@3.734.0: + resolution: {integrity: sha512-o11tSPTT70nAkGV1fN9wm/hAIiLPyWX6SuGf+9JyTp7S/rC2cFWhR26MvA69nplcjNaXVzB0f+QFrLXXjOqCrg==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/util-arn-parser@3.723.0: + resolution: {integrity: sha512-ZhEfvUwNliOQROcAk34WJWVYTlTa4694kSVhDSjW6lE1bMataPnIN8A0ycukEzBXmd8ZSoBcQLn6lKGl7XIJ5w==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + + /@aws-sdk/util-endpoints@3.743.0: + resolution: {integrity: sha512-sN1l559zrixeh5x+pttrnd0A3+r34r0tmPkJ/eaaMaAzXqsmKU/xYre9K3FNnsSS1J1k4PEfk/nHDTVUgFYjnw==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-sdk/types': 3.734.0 + '@smithy/types': 4.1.0 + '@smithy/util-endpoints': 3.0.1 + tslib: 2.8.1 + dev: false + + /@aws-sdk/util-locate-window@3.723.0: + resolution: {integrity: sha512-Yf2CS10BqK688DRsrKI/EO6B8ff5J86NXe4C+VCysK7UOgN0l1zOTeTukZ3H8Q9tYYX3oaF1961o8vRkFm7Nmw==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + + /@aws-sdk/util-user-agent-browser@3.734.0: + resolution: {integrity: sha512-xQTCus6Q9LwUuALW+S76OL0jcWtMOVu14q+GoLnWPUM7QeUw963oQcLhF7oq0CtaLLKyl4GOUfcwc773Zmwwng==} + dependencies: + '@aws-sdk/types': 3.734.0 + '@smithy/types': 4.1.0 + bowser: 2.11.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/util-user-agent-node@3.758.0: + resolution: {integrity: sha512-A5EZw85V6WhoKMV2hbuFRvb9NPlxEErb4HPO6/SPXYY4QrjprIzScHxikqcWv1w4J3apB1wto9LPU3IMsYtfrw==} + engines: {node: '>=18.0.0'} + peerDependencies: + aws-crt: '>=1.0.0' + peerDependenciesMeta: + aws-crt: + optional: true + dependencies: + '@aws-sdk/middleware-user-agent': 3.758.0 + '@aws-sdk/types': 3.734.0 + '@smithy/node-config-provider': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@aws-sdk/xml-builder@3.734.0: + resolution: {integrity: sha512-Zrjxi5qwGEcUsJ0ru7fRtW74WcTS0rbLcehoFB+rN1GRi2hbLcFaYs4PwVA5diLeAJH0gszv3x4Hr/S87MfbKQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + /@babel/code-frame@7.26.2: resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} @@ -3961,7 +4547,7 @@ packages: '@docusaurus/utils': 3.6.3(acorn@8.14.0)(react-dom@18.3.1)(react@18.3.1)(typescript@5.6.3) '@docusaurus/utils-validation': 3.6.3(acorn@8.14.0)(react-dom@18.3.1)(react@18.3.1)(typescript@5.6.3) algoliasearch: 4.24.0 - algoliasearch-helper: 3.24.1(algoliasearch@4.24.0) + algoliasearch-helper: 3.24.2(algoliasearch@4.24.0) clsx: 2.1.1 eta: 2.2.0 fs-extra: 11.3.0 @@ -4146,17 +4732,26 @@ packages: resolution: {integrity: sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==} dev: false - /@envelop/core@5.1.1: - resolution: {integrity: sha512-6+OukzuNsm33DtLnOats3e7VnnHndqINJbp/vlIyIlSGBc/wtgQiTAijNWwHhnozHc7WmCKzTsPSrGObvkJazg==} + /@envelop/core@5.2.0: + resolution: {integrity: sha512-JMrISsiZ4XQXMtJhs5K4MtM64zFfPHAmpWrkLt+d7d3UC4W39rQu9+4ba3kTV9MyohNr39+wfjBdTa13rKTbjA==} + engines: {node: '>=18.0.0'} + dependencies: + '@envelop/instruments': 1.0.0 + '@envelop/types': 5.2.0 + '@whatwg-node/promise-helpers': 1.2.2 + tslib: 2.8.1 + dev: true + + /@envelop/instruments@1.0.0: + resolution: {integrity: sha512-f4lHoti7QgUIluIGTM0mG9Wf9/w6zc1mosYmyFkrApeHSP2PIUC6a8fMoqkdk6pgVOps39kLdIhOPF8pIKS8/A==} engines: {node: '>=18.0.0'} dependencies: - '@envelop/types': 5.1.1 '@whatwg-node/promise-helpers': 1.2.2 tslib: 2.8.1 dev: true - /@envelop/types@5.1.1: - resolution: {integrity: sha512-uJyCPQRSqxH/4q8/TTTY2fMYIK/Tgv1IhOm6aFUUxuE/EI7muJM/UI85iv9Qo1OCpaafthwRLWzufRp20FyXaA==} + /@envelop/types@5.2.0: + resolution: {integrity: sha512-vCJY6URc8bK1O6p4zVRFpv/ASdyXvLM+Iqn2HP44UfTgEUQLyN4buwLawlkAv/KtzAL7VOeefpF2eKPWk7rHjg==} engines: {node: '>=18.0.0'} dependencies: '@whatwg-node/promise-helpers': 1.2.2 @@ -4478,7 +5073,7 @@ packages: graphql: ^15.5.0 || ^16.0.0 || ^17.0.0 typescript: ^5.0.0 dependencies: - '@0no-co/graphql.web': 1.1.1(graphql@16.10.0) + '@0no-co/graphql.web': 1.1.2(graphql@16.10.0) graphql: 16.10.0 typescript: 5.6.3 dev: true @@ -4510,16 +5105,16 @@ packages: '@graphql-codegen/client-preset': 4.6.4(graphql@16.10.0) '@graphql-codegen/core': 4.0.2(graphql@16.10.0) '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.10.0) - '@graphql-tools/apollo-engine-loader': 8.0.17(graphql@16.10.0) - '@graphql-tools/code-file-loader': 8.1.17(graphql@16.10.0) - '@graphql-tools/git-loader': 8.0.21(graphql@16.10.0) - '@graphql-tools/github-loader': 8.0.17(@types/node@22.13.9)(graphql@16.10.0) - '@graphql-tools/graphql-file-loader': 8.0.16(graphql@16.10.0) - '@graphql-tools/json-file-loader': 8.0.15(graphql@16.10.0) - '@graphql-tools/load': 8.0.16(graphql@16.10.0) + '@graphql-tools/apollo-engine-loader': 8.0.18(graphql@16.10.0) + '@graphql-tools/code-file-loader': 8.1.18(graphql@16.10.0) + '@graphql-tools/git-loader': 8.0.22(graphql@16.10.0) + '@graphql-tools/github-loader': 8.0.18(@types/node@22.13.9)(graphql@16.10.0) + '@graphql-tools/graphql-file-loader': 8.0.17(graphql@16.10.0) + '@graphql-tools/json-file-loader': 8.0.16(graphql@16.10.0) + '@graphql-tools/load': 8.0.17(graphql@16.10.0) '@graphql-tools/prisma-loader': 8.0.17(@types/node@22.13.9)(graphql@16.10.0) - '@graphql-tools/url-loader': 8.0.28(@types/node@22.13.9)(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/url-loader': 8.0.29(@types/node@22.13.9)(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) '@parcel/watcher': 2.5.1 '@whatwg-node/fetch': 0.10.5 chalk: 4.1.2 @@ -4570,7 +5165,7 @@ packages: '@graphql-codegen/typescript-operations': 4.5.1(graphql@16.10.0) '@graphql-codegen/visitor-plugin-common': 5.7.1(graphql@16.10.0) '@graphql-tools/documents': 1.0.1(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) '@graphql-typed-document-node/core': 3.2.0(graphql@16.10.0) graphql: 16.10.0 tslib: 2.6.3 @@ -4584,8 +5179,8 @@ packages: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 dependencies: '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.10.0) - '@graphql-tools/schema': 10.0.20(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/schema': 10.0.21(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) graphql: 16.10.0 tslib: 2.6.3 dev: true @@ -4598,7 +5193,7 @@ packages: dependencies: '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.10.0) '@graphql-codegen/visitor-plugin-common': 5.7.1(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) auto-bind: 4.0.0 graphql: 16.10.0 tslib: 2.6.3 @@ -4640,7 +5235,7 @@ packages: peerDependencies: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 dependencies: - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) change-case-all: 1.0.15 common-tags: 1.8.2 graphql: 16.10.0 @@ -4655,7 +5250,7 @@ packages: graphql: ^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 dependencies: '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) graphql: 16.10.0 tslib: 2.6.3 dev: true @@ -4718,7 +5313,7 @@ packages: '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.10.0) '@graphql-codegen/typescript': 4.1.5(graphql@16.10.0) '@graphql-codegen/visitor-plugin-common': 5.7.1(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) auto-bind: 4.0.0 graphql: 16.10.0 tslib: 2.6.3 @@ -4771,8 +5366,8 @@ packages: dependencies: '@graphql-codegen/plugin-helpers': 5.1.0(graphql@16.10.0) '@graphql-tools/optimize': 2.0.0(graphql@16.10.0) - '@graphql-tools/relay-operation-optimizer': 7.0.16(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/relay-operation-optimizer': 7.0.17(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) auto-bind: 4.0.0 change-case-all: 1.0.15 dependency-graph: 0.11.0 @@ -4784,13 +5379,13 @@ packages: - encoding dev: true - /@graphql-tools/apollo-engine-loader@8.0.17(graphql@16.10.0): - resolution: {integrity: sha512-2DwndS4GurK7VB8LD1paWZPdaYIwqMkMg2a3GU5nNpkL401QAAr2Mw3zYZ6XNe+nNHv4EyhywJ/ahuKKBcmJIA==} + /@graphql-tools/apollo-engine-loader@8.0.18(graphql@16.10.0): + resolution: {integrity: sha512-PSN5YEA3AheVkGlD85w/ukFVXN4e0y6gCNj0vwr3sTaL/Z5eTrqZCmalbEDs5PeZRZBo39tYBDKygcVceh3OQQ==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) '@whatwg-node/fetch': 0.10.5 graphql: 16.10.0 sync-fetch: 0.6.0-2 @@ -4803,20 +5398,20 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) dataloader: 2.2.3 graphql: 16.10.0 tslib: 2.8.1 dev: true - /@graphql-tools/code-file-loader@8.1.17(graphql@16.10.0): - resolution: {integrity: sha512-KQ+6n0HJQcBZ4b2HVV9rFJezyps6QLxRDPeGah3JX+MOLjjOtkpueE4br4x3+byVIm31fwWTA05wQjUx469DkA==} + /@graphql-tools/code-file-loader@8.1.18(graphql@16.10.0): + resolution: {integrity: sha512-/7oFP5EoMc5KcogOnLIxSeSstkxETry9JUvlV4Dw4e0XQv3n2aT1emqAqGznb8zdPsE5ZLwVQ7dEh0CGuYCCNw==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@graphql-tools/graphql-tag-pluck': 8.3.16(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/graphql-tag-pluck': 8.3.17(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) globby: 11.1.0 graphql: 16.10.0 tslib: 2.8.1 @@ -4832,9 +5427,9 @@ packages: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: '@graphql-tools/batch-execute': 9.0.12(graphql@16.10.0) - '@graphql-tools/executor': 1.4.2(graphql@16.10.0) - '@graphql-tools/schema': 10.0.20(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/executor': 1.4.3(graphql@16.10.0) + '@graphql-tools/schema': 10.0.21(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) '@repeaterjs/repeater': 3.0.6 dataloader: 2.2.3 dset: 3.1.4 @@ -4859,8 +5454,8 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@envelop/core': 5.1.1 - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@envelop/core': 5.2.0 + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) graphql: 16.10.0 dev: true @@ -4871,7 +5466,7 @@ packages: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: '@graphql-tools/executor-common': 0.0.3(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) '@whatwg-node/disposablestack': 0.0.5 graphql: 16.10.0 graphql-ws: 6.0.4(graphql@16.10.0)(ws@8.18.1) @@ -4892,7 +5487,7 @@ packages: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: '@graphql-tools/executor-common': 0.0.3(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) '@repeaterjs/repeater': 3.0.6 '@whatwg-node/disposablestack': 0.0.5 '@whatwg-node/fetch': 0.10.5 @@ -4905,14 +5500,14 @@ packages: - '@types/node' dev: true - /@graphql-tools/executor-legacy-ws@1.1.14(graphql@16.10.0): - resolution: {integrity: sha512-8xyIy0uiT5PkmIcOiNJg+Kg9pLwrs9MblxucKmBez8lUCL+0nKpx8o9ntXzmbLcVBA+4hV3wO3E9Bm7gkxiTUA==} + /@graphql-tools/executor-legacy-ws@1.1.15(graphql@16.10.0): + resolution: {integrity: sha512-5VM5m/WQWoIj2GKXuOUvhtzkm11g/rbKYOiLvur6AxD59FdLwVwDisWvarj8rsZ1NUedK312fD22vpKjc2m+dw==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) - '@types/ws': 8.5.14 + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) + '@types/ws': 8.18.0 graphql: 16.10.0 isomorphic-ws: 5.0.0(ws@8.18.1) tslib: 2.8.1 @@ -4922,13 +5517,13 @@ packages: - utf-8-validate dev: true - /@graphql-tools/executor@1.4.2(graphql@16.10.0): - resolution: {integrity: sha512-TzXh4SIfkOp969xeX3Z2dArzLXisAuj+YOnlhqphX+rC/OzZ1m4cZxsxaqosp/hTwlt5xXJFCoOPYjHEAU42Rw==} + /@graphql-tools/executor@1.4.3(graphql@16.10.0): + resolution: {integrity: sha512-QBefKv3h8gxXC/THqFPBF+ZKRVg4PoX/Tpczlv/mOffw6sp0w+pJ1ZeWYIr/Jh+r4kcxgqTd3/1MzYC4cl1EGA==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) '@graphql-typed-document-node/core': 3.2.0(graphql@16.10.0) '@repeaterjs/repeater': 3.0.6 '@whatwg-node/disposablestack': 0.0.6 @@ -4937,14 +5532,14 @@ packages: tslib: 2.8.1 dev: true - /@graphql-tools/git-loader@8.0.21(graphql@16.10.0): - resolution: {integrity: sha512-93c7aG/BBsu44kOh1d50rZtqfa1TRym4su+VLyC8SS7fmzeP9JuysHchsbtEQexJXPNXM9C5BHnVrdzgO9TmZg==} + /@graphql-tools/git-loader@8.0.22(graphql@16.10.0): + resolution: {integrity: sha512-O9TJqhqdouRgIAr2DeqchWq50mUN2OS1dzfrDEJ/k1Rx42gAenOuLft7QO6us90bFdK5BDzgD+5TEhE5a87O0g==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@graphql-tools/graphql-tag-pluck': 8.3.16(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/graphql-tag-pluck': 8.3.17(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) graphql: 16.10.0 is-glob: 4.0.3 micromatch: 4.0.8 @@ -4954,15 +5549,15 @@ packages: - supports-color dev: true - /@graphql-tools/github-loader@8.0.17(@types/node@22.13.9)(graphql@16.10.0): - resolution: {integrity: sha512-igUUqGGV8b5dnhNZBSTweYKIhBNby8fvNe0fv2JyQjDBysnFNAAOFAR6Bnr+8K9QbhW6aHkZInOQrOWxMQ77xg==} + /@graphql-tools/github-loader@8.0.18(@types/node@22.13.9)(graphql@16.10.0): + resolution: {integrity: sha512-st/T8W4ADVA71X2FLJLUciHT0LdYOo08DPuPKIGO0x+aRB8uxgDC8raDWWA8D928Y0OECxJi40+SNX/n07ww+g==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: '@graphql-tools/executor-http': 1.2.8(@types/node@22.13.9)(graphql@16.10.0) - '@graphql-tools/graphql-tag-pluck': 8.3.16(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/graphql-tag-pluck': 8.3.17(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) '@whatwg-node/fetch': 0.10.5 '@whatwg-node/promise-helpers': 1.2.2 graphql: 16.10.0 @@ -4973,22 +5568,22 @@ packages: - supports-color dev: true - /@graphql-tools/graphql-file-loader@8.0.16(graphql@16.10.0): - resolution: {integrity: sha512-/L77iJ0CMbJMm+xgi9m8u3KCcbQ6e//MissdYXOBax2wFH3pkPXJLClSlkoN5GqRd4rGgNrenDhkkqWhVFDQHg==} + /@graphql-tools/graphql-file-loader@8.0.17(graphql@16.10.0): + resolution: {integrity: sha512-N3bjg+XSBUGydWWh7w5FUxgwjXGdXP0OPRDgyPUT1nqKZhfGZmqc0nKJEXMReXsFMwAcFF95mLtkj7gMeKMkpw==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@graphql-tools/import': 7.0.15(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/import': 7.0.16(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) globby: 11.1.0 graphql: 16.10.0 tslib: 2.8.1 unixify: 1.0.0 dev: true - /@graphql-tools/graphql-tag-pluck@8.3.16(graphql@16.10.0): - resolution: {integrity: sha512-zs9bhnqA+7UzSDCIsZHI5Cz9RsbpyVVVDjAUn1ToEFsrAtxvqqvfXnjFS6nZSBTJ7PQK2Jf6spzB0cBOBAGNRQ==} + /@graphql-tools/graphql-tag-pluck@8.3.17(graphql@16.10.0): + resolution: {integrity: sha512-x1ocLp4CWecQ/pwU4jP9YgcVd1fRu5VgDYiddNY4otAQk3Z44ip5Lep1unimce6xBU9FMSNgh6mKIgwmYGpUpQ==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 @@ -4998,46 +5593,46 @@ packages: '@babel/plugin-syntax-import-assertions': 7.26.0(@babel/core@7.26.9) '@babel/traverse': 7.26.9 '@babel/types': 7.26.9 - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) graphql: 16.10.0 tslib: 2.8.1 transitivePeerDependencies: - supports-color dev: true - /@graphql-tools/import@7.0.15(graphql@16.10.0): - resolution: {integrity: sha512-g8PLNIBdhiVH52PbbpJXuWZqZb9oF2xqQaTYu31ssqlxlqAyBQJb/PNnCl3aL6Rl607Pmvvor0+lBbh26Gvn0Q==} + /@graphql-tools/import@7.0.16(graphql@16.10.0): + resolution: {integrity: sha512-YtE0qQbZEe/GlMfN6UO9DKspOndQzyVxG4kzCq2LoWLRiQsAE1z9maCT+62TDEUTHsljGeUY/ekzvSHkTH2Nqg==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) graphql: 16.10.0 resolve-from: 5.0.0 tslib: 2.8.1 dev: true - /@graphql-tools/json-file-loader@8.0.15(graphql@16.10.0): - resolution: {integrity: sha512-e9ehBKNa6LKKqGYjq23qOIbvaYwKsVMRO8p9q1qzdF1izWVIHN9fE9dRb6y78rCwMu/tq1a0bq1KpAH5W6Sz0w==} + /@graphql-tools/json-file-loader@8.0.16(graphql@16.10.0): + resolution: {integrity: sha512-l7LVJMdsphmRcjEx7SezEXg1E24eyjQwQHn04uk41WbvhNfbB3X2fUdDsHzH8dbRXUp+wWABoAIgVOiE1qXpSw==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) globby: 11.1.0 graphql: 16.10.0 tslib: 2.8.1 unixify: 1.0.0 dev: true - /@graphql-tools/load@8.0.16(graphql@16.10.0): - resolution: {integrity: sha512-gD++qJvQYpbRLxBJvWEVKfb8IQZ3YyDkDFyuXVn7A/Fjoi2o6vsij/s6xfimNFyreYZL42MHjC5pWJEGQisDjg==} + /@graphql-tools/load@8.0.17(graphql@16.10.0): + resolution: {integrity: sha512-oFXpXSghoi+qdghBtkJY6VlQqR/BdLG5JVEbSSJcyh1U2cXILTPiO42zWnzjCI+5jxzFDDdBSqTgfGBL33gTLQ==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@graphql-tools/schema': 10.0.20(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/schema': 10.0.21(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) graphql: 16.10.0 p-limit: 3.1.0 tslib: 2.8.1 @@ -5064,13 +5659,13 @@ packages: tslib: 2.8.1 dev: false - /@graphql-tools/merge@9.0.21(graphql@16.10.0): - resolution: {integrity: sha512-5EiVL2InZeBlsZXlXjqyNMD697QP44j/dipXEogHlZcZzWEP/JTDwx9hTfFbmrePVR8+p89gFg1tE25iEgSong==} + /@graphql-tools/merge@9.0.22(graphql@16.10.0): + resolution: {integrity: sha512-bjOs9DlTbo1Yz2UzQcJ78Dn9/pKyY2zNaoqNLfRTTSkO56QFkvqhfjQuqJcqu+V3rtaB2o0VMpWaY6JT8ZTvQA==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) graphql: 16.10.0 tslib: 2.8.1 dev: true @@ -5100,8 +5695,8 @@ packages: peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@graphql-tools/url-loader': 8.0.28(@types/node@22.13.9)(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/url-loader': 8.0.29(@types/node@22.13.9)(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) '@types/js-yaml': 4.0.9 '@whatwg-node/fetch': 0.10.5 chalk: 4.1.2 @@ -5141,14 +5736,14 @@ packages: - supports-color dev: true - /@graphql-tools/relay-operation-optimizer@7.0.16(graphql@16.10.0): - resolution: {integrity: sha512-uE17qf/uhXAFTmDe5ghHC4Y9N51aCNgyPrSwFpgWxfckZvW1idi5MR5cCl8jC1w9129+XDI5WGfFXx1b2GR1Ow==} + /@graphql-tools/relay-operation-optimizer@7.0.17(graphql@16.10.0): + resolution: {integrity: sha512-zEdEIYmDsEtGhP9sl06N8aNFIo3mLrDzSlzIgfc7jKWpOY1H/a8b5MFNQd22kmaiCWlxOjDe3K0cCwWX4ygM+g==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: '@ardatan/relay-compiler': 12.0.2(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) graphql: 16.10.0 tslib: 2.6.3 transitivePeerDependencies: @@ -5168,14 +5763,14 @@ packages: value-or-promise: 1.0.12 dev: false - /@graphql-tools/schema@10.0.20(graphql@16.10.0): - resolution: {integrity: sha512-BmDqXS9gHJF2Cl1k+IOiPCYWApBU6LhqSEPc8WmAxn08HtmhKoCizwiUuWtt8SOV67yoMzC1zJFkBdm3wZX9Fw==} + /@graphql-tools/schema@10.0.21(graphql@16.10.0): + resolution: {integrity: sha512-AECSlNnD0WNxICwfJs93gYn2oHxPmztn1MYBETIQXrJJcymfD6BoUrDlYPa6F27RzRc+gbPZPHMWL26uujfKBg==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: - '@graphql-tools/merge': 9.0.21(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/merge': 9.0.22(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) graphql: 16.10.0 tslib: 2.8.1 dev: true @@ -5192,18 +5787,18 @@ packages: value-or-promise: 1.0.12 dev: false - /@graphql-tools/url-loader@8.0.28(@types/node@22.13.9)(graphql@16.10.0): - resolution: {integrity: sha512-zeshp2c0AFKIatLAhm0BtD0Om4Wr5Cu775rFpk369CA1nA8ZQV25EZ/TIrYwoUkg+b0ERC9H5EZrB2hqTJfaxQ==} + /@graphql-tools/url-loader@8.0.29(@types/node@22.13.9)(graphql@16.10.0): + resolution: {integrity: sha512-xCWmAL20DUzb9inrnrGEAL6PP9Exg8zfM/zkPHtPGNydKGpOXRFXvDoC6DJpwdN3B9HABUjamw38vj1uN5I1Uw==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: '@graphql-tools/executor-graphql-ws': 2.0.3(graphql@16.10.0) '@graphql-tools/executor-http': 1.2.8(@types/node@22.13.9)(graphql@16.10.0) - '@graphql-tools/executor-legacy-ws': 1.1.14(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/executor-legacy-ws': 1.1.15(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) '@graphql-tools/wrap': 10.0.31(graphql@16.10.0) - '@types/ws': 8.5.14 + '@types/ws': 8.18.0 '@whatwg-node/fetch': 0.10.5 '@whatwg-node/promise-helpers': 1.2.2 graphql: 16.10.0 @@ -5232,8 +5827,8 @@ packages: tslib: 2.8.1 dev: false - /@graphql-tools/utils@10.8.3(graphql@16.10.0): - resolution: {integrity: sha512-4QCvx3SWRsbH7wnktl51mBek+zE9hsjsv796XVlJlOUdWpAghJmA3ID2P7/Vwuy7BivVNfuAKe4ucUdE1fG7vA==} + /@graphql-tools/utils@10.8.4(graphql@16.10.0): + resolution: {integrity: sha512-HpHBgcmLIE79jWk1v5Bm0Eb8MaPiwSJT/Iy5xIJ+GMe7yAKpCYrbjf7wb+UMDMkLkfEryvo3syCx8k+TMAZ9bA==} engines: {node: '>=16.0.0'} peerDependencies: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 @@ -5271,8 +5866,8 @@ packages: graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 dependencies: '@graphql-tools/delegate': 10.2.13(graphql@16.10.0) - '@graphql-tools/schema': 10.0.20(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/schema': 10.0.21(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) graphql: 16.10.0 tslib: 2.8.1 dev: true @@ -5832,8 +6427,8 @@ packages: resolution: {integrity: sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==} dev: false - /@langchain/community@0.3.33(@browserbasehq/stagehand@1.13.1)(@ibm-cloud/watsonx-ai@1.5.1)(@langchain/core@0.3.42)(axios@1.7.9)(ibm-cloud-sdk-core@5.1.3)(openai@4.86.1)(ws@8.18.1): - resolution: {integrity: sha512-BtgfvyPvb/HYUWLa5YXoDVMY+8pkZvaZzwp5NSebstVKsitsjuG/pwzZ7gDQO1c8LJZlxAeYyAwwQBI87ibRRg==} + /@langchain/community@0.3.34(@browserbasehq/stagehand@1.13.1)(@ibm-cloud/watsonx-ai@1.5.1)(@langchain/core@0.3.42)(axios@1.7.9)(ibm-cloud-sdk-core@5.1.3)(openai@4.86.1)(ws@8.18.1): + resolution: {integrity: sha512-s0KVulgVIPd90s3m6XZtWrCRGQPWsY93uY62seFMmNhzcyF+I65kKnN04Nbiouthrn/YJ9HB4hW8MJAFuX6RRg==} engines: {node: '>=18'} peerDependencies: '@arcjet/redact': ^v1.0.0-alpha.23 @@ -6216,7 +6811,7 @@ packages: ibm-cloud-sdk-core: 5.1.3 js-yaml: 4.1.0 langchain: 0.3.19(@langchain/core@0.3.42)(axios@1.7.9)(openai@4.86.1)(ws@8.18.1) - langsmith: 0.3.11(openai@4.86.1) + langsmith: 0.3.12(openai@4.86.1) openai: 4.86.1(ws@8.18.1)(zod@3.24.2) uuid: 10.0.0 ws: 8.18.1 @@ -6250,7 +6845,7 @@ packages: camelcase: 6.3.0 decamelize: 1.2.0 js-tiktoken: 1.0.19 - langsmith: 0.3.11(openai@4.86.1) + langsmith: 0.3.12(openai@4.86.1) mustache: 4.2.0 p-queue: 6.6.2 p-retry: 4.6.2 @@ -6402,9 +6997,9 @@ packages: dependencies: '@apollo/server': 4.11.3(graphql@16.10.0) '@apollo/server-plugin-landing-page-graphql-playground': 4.0.0(@apollo/server@4.11.3) - '@nestjs/common': 10.4.15(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/core': 10.4.15(@nestjs/common@10.4.15)(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/graphql': 12.2.2(@nestjs/common@10.4.15)(@nestjs/core@10.4.15)(class-validator@0.14.1)(graphql@16.10.0)(reflect-metadata@0.2.2) + '@nestjs/graphql': 12.2.2(@nestjs/common@10.4.15)(@nestjs/core@10.4.15)(class-transformer@0.5.1)(class-validator@0.14.1)(graphql@16.10.0)(reflect-metadata@0.2.2) graphql: 16.10.0 iterall: 1.3.0 lodash.omit: 4.5.0 @@ -6418,7 +7013,7 @@ packages: axios: ^1.3.1 rxjs: ^6.0.0 || ^7.0.0 dependencies: - '@nestjs/common': 10.4.15(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) axios: 1.8.1(debug@4.4.0) rxjs: 7.8.2 dev: false @@ -6461,7 +7056,7 @@ packages: - webpack-cli dev: true - /@nestjs/common@10.4.15(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2): + /@nestjs/common@10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2): resolution: {integrity: sha512-vaLg1ZgwhG29BuLDxPA9OAcIlgqzp9/N8iG0wGapyUNTf4IY4O6zAHgN6QalwLhFxq7nOI021vdRojR1oF3bqg==} peerDependencies: class-transformer: '*' @@ -6474,6 +7069,7 @@ packages: class-validator: optional: true dependencies: + class-transformer: 0.5.1 class-validator: 0.14.1 iterare: 1.2.1 reflect-metadata: 0.2.2 @@ -6487,7 +7083,7 @@ packages: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 rxjs: ^7.1.0 dependencies: - '@nestjs/common': 10.4.15(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) dotenv: 16.4.5 dotenv-expand: 10.0.0 lodash: 4.17.21 @@ -6512,7 +7108,7 @@ packages: '@nestjs/websockets': optional: true dependencies: - '@nestjs/common': 10.4.15(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/platform-express': 10.4.15(@nestjs/common@10.4.15)(@nestjs/core@10.4.15) '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 @@ -6525,7 +7121,7 @@ packages: transitivePeerDependencies: - encoding - /@nestjs/graphql@12.2.2(@nestjs/common@10.4.15)(@nestjs/core@10.4.15)(class-validator@0.14.1)(graphql@16.10.0)(reflect-metadata@0.2.2): + /@nestjs/graphql@12.2.2(@nestjs/common@10.4.15)(@nestjs/core@10.4.15)(class-transformer@0.5.1)(class-validator@0.14.1)(graphql@16.10.0)(reflect-metadata@0.2.2): resolution: {integrity: sha512-lUDy/1uqbRA1kBKpXcmY0aHhcPbfeG52Wg5+9Jzd1d57dwSjCAmuO+mWy5jz9ugopVCZeK0S/kdAMvA+r9fNdA==} peerDependencies: '@apollo/subgraph': ^2.0.0 @@ -6549,10 +7145,11 @@ packages: '@graphql-tools/merge': 9.0.11(graphql@16.10.0) '@graphql-tools/schema': 10.0.10(graphql@16.10.0) '@graphql-tools/utils': 10.6.1(graphql@16.10.0) - '@nestjs/common': 10.4.15(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/core': 10.4.15(@nestjs/common@10.4.15)(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2) - '@nestjs/mapped-types': 2.0.6(@nestjs/common@10.4.15)(class-validator@0.14.1)(reflect-metadata@0.2.2) + '@nestjs/mapped-types': 2.0.6(@nestjs/common@10.4.15)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) chokidar: 4.0.1 + class-transformer: 0.5.1 class-validator: 0.14.1 fast-glob: 3.3.2 graphql: 16.10.0 @@ -6575,12 +7172,12 @@ packages: peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 dependencies: - '@nestjs/common': 10.4.15(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@types/jsonwebtoken': 9.0.5 jsonwebtoken: 9.0.2 dev: false - /@nestjs/mapped-types@2.0.6(@nestjs/common@10.4.15)(class-validator@0.14.1)(reflect-metadata@0.2.2): + /@nestjs/mapped-types@2.0.6(@nestjs/common@10.4.15)(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2): resolution: {integrity: sha512-84ze+CPfp1OWdpRi1/lOu59hOhTz38eVzJvRKrg9ykRFwDz+XleKfMsG0gUqNZYFa6v53XYzeD+xItt8uDW7NQ==} peerDependencies: '@nestjs/common': ^8.0.0 || ^9.0.0 || ^10.0.0 @@ -6593,7 +7190,8 @@ packages: class-validator: optional: true dependencies: - '@nestjs/common': 10.4.15(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + class-transformer: 0.5.1 class-validator: 0.14.1 reflect-metadata: 0.2.2 dev: false @@ -6604,7 +7202,7 @@ packages: '@nestjs/common': ^10.0.0 '@nestjs/core': ^10.0.0 dependencies: - '@nestjs/common': 10.4.15(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/core': 10.4.15(@nestjs/common@10.4.15)(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2) body-parser: 1.20.3 cors: 2.8.5 @@ -6657,7 +7255,7 @@ packages: '@nestjs/platform-express': optional: true dependencies: - '@nestjs/common': 10.4.15(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/core': 10.4.15(@nestjs/common@10.4.15)(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/platform-express': 10.4.15(@nestjs/common@10.4.15)(@nestjs/core@10.4.15) tslib: 2.8.1 @@ -6672,7 +7270,7 @@ packages: rxjs: ^7.2.0 typeorm: ^0.3.0 dependencies: - '@nestjs/common': 10.4.15(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/common': 10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) '@nestjs/core': 10.4.15(@nestjs/common@10.4.15)(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2) reflect-metadata: 0.2.2 rxjs: 7.8.2 @@ -8268,6 +8866,496 @@ packages: micromark-util-symbol: 1.1.0 dev: false + /@smithy/abort-controller@4.0.1: + resolution: {integrity: sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/chunked-blob-reader-native@4.0.0: + resolution: {integrity: sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/util-base64': 4.0.0 + tslib: 2.8.1 + dev: false + + /@smithy/chunked-blob-reader@5.0.0: + resolution: {integrity: sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + + /@smithy/config-resolver@4.0.1: + resolution: {integrity: sha512-Igfg8lKu3dRVkTSEm98QpZUvKEOa71jDX4vKRcvJVyRc3UgN3j7vFMf0s7xLQhYmKa8kyJGQgUJDOV5V3neVlQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/node-config-provider': 4.0.1 + '@smithy/types': 4.1.0 + '@smithy/util-config-provider': 4.0.0 + '@smithy/util-middleware': 4.0.1 + tslib: 2.8.1 + dev: false + + /@smithy/core@3.1.5: + resolution: {integrity: sha512-HLclGWPkCsekQgsyzxLhCQLa8THWXtB5PxyYN+2O6nkyLt550KQKTlbV2D1/j5dNIQapAZM1+qFnpBFxZQkgCA==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/middleware-serde': 4.0.2 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 + '@smithy/util-body-length-browser': 4.0.0 + '@smithy/util-middleware': 4.0.1 + '@smithy/util-stream': 4.1.2 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + dev: false + + /@smithy/credential-provider-imds@4.0.1: + resolution: {integrity: sha512-l/qdInaDq1Zpznpmev/+52QomsJNZ3JkTl5yrTl02V6NBgJOQ4LY0SFw/8zsMwj3tLe8vqiIuwF6nxaEwgf6mg==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/node-config-provider': 4.0.1 + '@smithy/property-provider': 4.0.1 + '@smithy/types': 4.1.0 + '@smithy/url-parser': 4.0.1 + tslib: 2.8.1 + dev: false + + /@smithy/eventstream-codec@4.0.1: + resolution: {integrity: sha512-Q2bCAAR6zXNVtJgifsU16ZjKGqdw/DyecKNgIgi7dlqw04fqDu0mnq+JmGphqheypVc64CYq3azSuCpAdFk2+A==} + engines: {node: '>=18.0.0'} + dependencies: + '@aws-crypto/crc32': 5.2.0 + '@smithy/types': 4.1.0 + '@smithy/util-hex-encoding': 4.0.0 + tslib: 2.8.1 + dev: false + + /@smithy/eventstream-serde-browser@4.0.1: + resolution: {integrity: sha512-HbIybmz5rhNg+zxKiyVAnvdM3vkzjE6ccrJ620iPL8IXcJEntd3hnBl+ktMwIy12Te/kyrSbUb8UCdnUT4QEdA==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/eventstream-serde-universal': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/eventstream-serde-config-resolver@4.0.1: + resolution: {integrity: sha512-lSipaiq3rmHguHa3QFF4YcCM3VJOrY9oq2sow3qlhFY+nBSTF/nrO82MUQRPrxHQXA58J5G1UnU2WuJfi465BA==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/eventstream-serde-node@4.0.1: + resolution: {integrity: sha512-o4CoOI6oYGYJ4zXo34U8X9szDe3oGjmHgsMGiZM0j4vtNoT+h80TLnkUcrLZR3+E6HIxqW+G+9WHAVfl0GXK0Q==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/eventstream-serde-universal': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/eventstream-serde-universal@4.0.1: + resolution: {integrity: sha512-Z94uZp0tGJuxds3iEAZBqGU2QiaBHP4YytLUjwZWx+oUeohCsLyUm33yp4MMBmhkuPqSbQCXq5hDet6JGUgHWA==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/eventstream-codec': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/fetch-http-handler@5.0.1: + resolution: {integrity: sha512-3aS+fP28urrMW2KTjb6z9iFow6jO8n3MFfineGbndvzGZit3taZhKWtTorf+Gp5RpFDDafeHlhfsGlDCXvUnJA==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/protocol-http': 5.0.1 + '@smithy/querystring-builder': 4.0.1 + '@smithy/types': 4.1.0 + '@smithy/util-base64': 4.0.0 + tslib: 2.8.1 + dev: false + + /@smithy/hash-blob-browser@4.0.1: + resolution: {integrity: sha512-rkFIrQOKZGS6i1D3gKJ8skJ0RlXqDvb1IyAphksaFOMzkn3v3I1eJ8m7OkLj0jf1McP63rcCEoLlkAn/HjcTRw==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/chunked-blob-reader': 5.0.0 + '@smithy/chunked-blob-reader-native': 4.0.0 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/hash-node@4.0.1: + resolution: {integrity: sha512-TJ6oZS+3r2Xu4emVse1YPB3Dq3d8RkZDKcPr71Nj/lJsdAP1c7oFzYqEn1IBc915TsgLl2xIJNuxCz+gLbLE0w==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.1.0 + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + dev: false + + /@smithy/hash-stream-node@4.0.1: + resolution: {integrity: sha512-U1rAE1fxmReCIr6D2o/4ROqAQX+GffZpyMt3d7njtGDr2pUNmAKRWa49gsNVhCh2vVAuf3wXzWwNr2YN8PAXIw==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.1.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + dev: false + + /@smithy/invalid-dependency@4.0.1: + resolution: {integrity: sha512-gdudFPf4QRQ5pzj7HEnu6FhKRi61BfH/Gk5Yf6O0KiSbr1LlVhgjThcvjdu658VE6Nve8vaIWB8/fodmS1rBPQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/is-array-buffer@2.2.0: + resolution: {integrity: sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==} + engines: {node: '>=14.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + + /@smithy/is-array-buffer@4.0.0: + resolution: {integrity: sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + + /@smithy/md5-js@4.0.1: + resolution: {integrity: sha512-HLZ647L27APi6zXkZlzSFZIjpo8po45YiyjMGJZM3gyDY8n7dPGdmxIIljLm4gPt/7rRvutLTTkYJpZVfG5r+A==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.1.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + dev: false + + /@smithy/middleware-content-length@4.0.1: + resolution: {integrity: sha512-OGXo7w5EkB5pPiac7KNzVtfCW2vKBTZNuCctn++TTSOMpe6RZO/n6WEC1AxJINn3+vWLKW49uad3lo/u0WJ9oQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/middleware-endpoint@4.0.6: + resolution: {integrity: sha512-ftpmkTHIFqgaFugcjzLZv3kzPEFsBFSnq1JsIkr2mwFzCraZVhQk2gqN51OOeRxqhbPTkRFj39Qd2V91E/mQxg==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/core': 3.1.5 + '@smithy/middleware-serde': 4.0.2 + '@smithy/node-config-provider': 4.0.1 + '@smithy/shared-ini-file-loader': 4.0.1 + '@smithy/types': 4.1.0 + '@smithy/url-parser': 4.0.1 + '@smithy/util-middleware': 4.0.1 + tslib: 2.8.1 + dev: false + + /@smithy/middleware-retry@4.0.7: + resolution: {integrity: sha512-58j9XbUPLkqAcV1kHzVX/kAR16GT+j7DUZJqwzsxh1jtz7G82caZiGyyFgUvogVfNTg3TeAOIJepGc8TXF4AVQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/node-config-provider': 4.0.1 + '@smithy/protocol-http': 5.0.1 + '@smithy/service-error-classification': 4.0.1 + '@smithy/smithy-client': 4.1.6 + '@smithy/types': 4.1.0 + '@smithy/util-middleware': 4.0.1 + '@smithy/util-retry': 4.0.1 + tslib: 2.8.1 + uuid: 9.0.1 + dev: false + + /@smithy/middleware-serde@4.0.2: + resolution: {integrity: sha512-Sdr5lOagCn5tt+zKsaW+U2/iwr6bI9p08wOkCp6/eL6iMbgdtc2R5Ety66rf87PeohR0ExI84Txz9GYv5ou3iQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/middleware-stack@4.0.1: + resolution: {integrity: sha512-dHwDmrtR/ln8UTHpaIavRSzeIk5+YZTBtLnKwDW3G2t6nAupCiQUvNzNoHBpik63fwUaJPtlnMzXbQrNFWssIA==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/node-config-provider@4.0.1: + resolution: {integrity: sha512-8mRTjvCtVET8+rxvmzRNRR0hH2JjV0DFOmwXPrISmTIJEfnCBugpYYGAsCj8t41qd+RB5gbheSQ/6aKZCQvFLQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/property-provider': 4.0.1 + '@smithy/shared-ini-file-loader': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/node-http-handler@4.0.3: + resolution: {integrity: sha512-dYCLeINNbYdvmMLtW0VdhW1biXt+PPCGazzT5ZjKw46mOtdgToQEwjqZSS9/EN8+tNs/RO0cEWG044+YZs97aA==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/abort-controller': 4.0.1 + '@smithy/protocol-http': 5.0.1 + '@smithy/querystring-builder': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/property-provider@4.0.1: + resolution: {integrity: sha512-o+VRiwC2cgmk/WFV0jaETGOtX16VNPp2bSQEzu0whbReqE1BMqsP2ami2Vi3cbGVdKu1kq9gQkDAGKbt0WOHAQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/protocol-http@5.0.1: + resolution: {integrity: sha512-TE4cpj49jJNB/oHyh/cRVEgNZaoPaxd4vteJNB0yGidOCVR0jCw/hjPVsT8Q8FRmj8Bd3bFZt8Dh7xGCT+xMBQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/querystring-builder@4.0.1: + resolution: {integrity: sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.1.0 + '@smithy/util-uri-escape': 4.0.0 + tslib: 2.8.1 + dev: false + + /@smithy/querystring-parser@4.0.1: + resolution: {integrity: sha512-Ma2XC7VS9aV77+clSFylVUnPZRindhB7BbmYiNOdr+CHt/kZNJoPP0cd3QxCnCFyPXC4eybmyE98phEHkqZ5Jw==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/service-error-classification@4.0.1: + resolution: {integrity: sha512-3JNjBfOWpj/mYfjXJHB4Txc/7E4LVq32bwzE7m28GN79+M1f76XHflUaSUkhOriprPDzev9cX/M+dEB80DNDKA==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.1.0 + dev: false + + /@smithy/shared-ini-file-loader@4.0.1: + resolution: {integrity: sha512-hC8F6qTBbuHRI/uqDgqqi6J0R4GtEZcgrZPhFQnMhfJs3MnUTGSnR1NSJCJs5VWlMydu0kJz15M640fJlRsIOw==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/signature-v4@5.0.1: + resolution: {integrity: sha512-nCe6fQ+ppm1bQuw5iKoeJ0MJfz2os7Ic3GBjOkLOPtavbD1ONoyE3ygjBfz2ythFWm4YnRm6OxW+8p/m9uCoIA==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/is-array-buffer': 4.0.0 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 + '@smithy/util-hex-encoding': 4.0.0 + '@smithy/util-middleware': 4.0.1 + '@smithy/util-uri-escape': 4.0.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + dev: false + + /@smithy/smithy-client@4.1.6: + resolution: {integrity: sha512-UYDolNg6h2O0L+cJjtgSyKKvEKCOa/8FHYJnBobyeoeWDmNpXjwOAtw16ezyeu1ETuuLEOZbrynK0ZY1Lx9Jbw==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/core': 3.1.5 + '@smithy/middleware-endpoint': 4.0.6 + '@smithy/middleware-stack': 4.0.1 + '@smithy/protocol-http': 5.0.1 + '@smithy/types': 4.1.0 + '@smithy/util-stream': 4.1.2 + tslib: 2.8.1 + dev: false + + /@smithy/types@4.1.0: + resolution: {integrity: sha512-enhjdwp4D7CXmwLtD6zbcDMbo6/T6WtuuKCY49Xxc6OMOmUWlBEBDREsxxgV2LIdeQPW756+f97GzcgAwp3iLw==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + + /@smithy/url-parser@4.0.1: + resolution: {integrity: sha512-gPXcIEUtw7VlK8f/QcruNXm7q+T5hhvGu9tl63LsJPZ27exB6dtNwvh2HIi0v7JcXJ5emBxB+CJxwaLEdJfA+g==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/querystring-parser': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/util-base64@4.0.0: + resolution: {integrity: sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + dev: false + + /@smithy/util-body-length-browser@4.0.0: + resolution: {integrity: sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + + /@smithy/util-body-length-node@4.0.0: + resolution: {integrity: sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + + /@smithy/util-buffer-from@2.2.0: + resolution: {integrity: sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/is-array-buffer': 2.2.0 + tslib: 2.8.1 + dev: false + + /@smithy/util-buffer-from@4.0.0: + resolution: {integrity: sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/is-array-buffer': 4.0.0 + tslib: 2.8.1 + dev: false + + /@smithy/util-config-provider@4.0.0: + resolution: {integrity: sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + + /@smithy/util-defaults-mode-browser@4.0.7: + resolution: {integrity: sha512-CZgDDrYHLv0RUElOsmZtAnp1pIjwDVCSuZWOPhIOBvG36RDfX1Q9+6lS61xBf+qqvHoqRjHxgINeQz47cYFC2Q==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/property-provider': 4.0.1 + '@smithy/smithy-client': 4.1.6 + '@smithy/types': 4.1.0 + bowser: 2.11.0 + tslib: 2.8.1 + dev: false + + /@smithy/util-defaults-mode-node@4.0.7: + resolution: {integrity: sha512-79fQW3hnfCdrfIi1soPbK3zmooRFnLpSx3Vxi6nUlqaaQeC5dm8plt4OTNDNqEEEDkvKghZSaoti684dQFVrGQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/config-resolver': 4.0.1 + '@smithy/credential-provider-imds': 4.0.1 + '@smithy/node-config-provider': 4.0.1 + '@smithy/property-provider': 4.0.1 + '@smithy/smithy-client': 4.1.6 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/util-endpoints@3.0.1: + resolution: {integrity: sha512-zVdUENQpdtn9jbpD9SCFK4+aSiavRb9BxEtw9ZGUR1TYo6bBHbIoi7VkrFQ0/RwZlzx0wRBaRmPclj8iAoJCLA==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/node-config-provider': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/util-hex-encoding@4.0.0: + resolution: {integrity: sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + + /@smithy/util-middleware@4.0.1: + resolution: {integrity: sha512-HiLAvlcqhbzhuiOa0Lyct5IIlyIz0PQO5dnMlmQ/ubYM46dPInB+3yQGkfxsk6Q24Y0n3/JmcA1v5iEhmOF5mA==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/util-retry@4.0.1: + resolution: {integrity: sha512-WmRHqNVwn3kI3rKk1LsKcVgPBG6iLTBGC1iYOV3GQegwJ3E8yjzHytPt26VNzOWr1qu0xE03nK0Ug8S7T7oufw==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/service-error-classification': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + + /@smithy/util-stream@4.1.2: + resolution: {integrity: sha512-44PKEqQ303d3rlQuiDpcCcu//hV8sn+u2JBo84dWCE0rvgeiVl0IlLMagbU++o0jCWhYCsHaAt9wZuZqNe05Hw==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/fetch-http-handler': 5.0.1 + '@smithy/node-http-handler': 4.0.3 + '@smithy/types': 4.1.0 + '@smithy/util-base64': 4.0.0 + '@smithy/util-buffer-from': 4.0.0 + '@smithy/util-hex-encoding': 4.0.0 + '@smithy/util-utf8': 4.0.0 + tslib: 2.8.1 + dev: false + + /@smithy/util-uri-escape@4.0.0: + resolution: {integrity: sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==} + engines: {node: '>=18.0.0'} + dependencies: + tslib: 2.8.1 + dev: false + + /@smithy/util-utf8@2.3.0: + resolution: {integrity: sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==} + engines: {node: '>=14.0.0'} + dependencies: + '@smithy/util-buffer-from': 2.2.0 + tslib: 2.8.1 + dev: false + + /@smithy/util-utf8@4.0.0: + resolution: {integrity: sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/util-buffer-from': 4.0.0 + tslib: 2.8.1 + dev: false + + /@smithy/util-waiter@4.0.2: + resolution: {integrity: sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ==} + engines: {node: '>=18.0.0'} + dependencies: + '@smithy/abort-controller': 4.0.1 + '@smithy/types': 4.1.0 + tslib: 2.8.1 + dev: false + /@sqltools/formatter@1.2.5: resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} dev: false @@ -8549,6 +9637,20 @@ packages: dependencies: '@types/estree': 1.0.6 + /@types/apollo-upload-client@18.0.0(@types/react@18.3.18)(graphql-ws@5.16.2)(react-dom@18.3.1)(react@18.3.1)(subscriptions-transport-ws@0.11.0): + resolution: {integrity: sha512-cMgITNemktxasqvp6jiPj15dv84n3FTMvMoYBP1+xonDS+0l6JygIJrj2LJh85rShRzTOOkrElrAsCXXARa3KA==} + dependencies: + '@apollo/client': 3.13.1(@types/react@18.3.18)(graphql-ws@5.16.2)(graphql@16.10.0)(react-dom@18.3.1)(react@18.3.1)(subscriptions-transport-ws@0.11.0) + '@types/extract-files': 13.0.1 + graphql: 16.10.0 + transitivePeerDependencies: + - '@types/react' + - graphql-ws + - react + - react-dom + - subscriptions-transport-ws + dev: false + /@types/aria-query@5.0.4: resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} dev: true @@ -8691,6 +9793,10 @@ packages: '@types/serve-static': 1.15.7 dev: true + /@types/extract-files@13.0.1: + resolution: {integrity: sha512-/fRbzc2lAd7jDJSSnxWiUyXWjdUZZ4HbISLJzVgt1AvrdOa7U49YRPcvuCUywkmURZ7uwJOheDjx19itbQ5KvA==} + dev: false + /@types/fs-extra@11.0.4: resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} dependencies: @@ -8988,8 +10094,8 @@ packages: /@types/validator@13.12.2: resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==} - /@types/ws@8.5.14: - resolution: {integrity: sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==} + /@types/ws@8.18.0: + resolution: {integrity: sha512-8svvI3hMyvN0kKCJMvTJP/x6Y/EoQbepff882wL+Sn5QsXb3etnamgrJq4isrBxSJj5L2AuXcI0+bgkoAXGUJw==} dependencies: '@types/node': 20.17.23 @@ -9677,8 +10783,8 @@ packages: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 - /algoliasearch-helper@3.24.1(algoliasearch@4.24.0): - resolution: {integrity: sha512-knYRACqLH9UpeR+WRUrBzBFR2ulGuOjI2b525k4PNeqZxeFMHJE7YcL7s6Jh12Qza0rtHqZdgHMfeuaaAkf4wA==} + /algoliasearch-helper@3.24.2(algoliasearch@4.24.0): + resolution: {integrity: sha512-vBw/INZDfyh/THbVeDy8On8lZqd2qiUAHde5N4N1ygL4SoeLqLGJ4GHneHrDAYsjikRwTTtodEP0fiXl5MxHFQ==} peerDependencies: algoliasearch: '>= 3.1 < 6' dependencies: @@ -9790,6 +10896,18 @@ packages: normalize-path: 3.0.0 picomatch: 2.3.1 + /apollo-upload-client@18.0.1(@apollo/client@3.13.1)(graphql@16.10.0): + resolution: {integrity: sha512-OQvZg1rK05VNI79D658FUmMdoI2oB/KJKb6QGMa2Si25QXOaAvLMBFUEwJct7wf+19U8vk9ILhidBOU1ZWv6QA==} + engines: {node: ^18.15.0 || >=20.4.0} + peerDependencies: + '@apollo/client': ^3.8.0 + graphql: 14 - 16 + dependencies: + '@apollo/client': 3.13.1(@types/react@18.3.18)(graphql-ws@5.16.2)(graphql@16.10.0)(react-dom@18.3.1)(react@18.3.1)(subscriptions-transport-ws@0.11.0) + extract-files: 13.0.0 + graphql: 16.10.0 + dev: false + /app-root-path@3.1.0: resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==} engines: {node: '>= 6.0.0'} @@ -10009,7 +11127,7 @@ packages: postcss: ^8.1.0 dependencies: browserslist: 4.24.4 - caniuse-lite: 1.0.30001701 + caniuse-lite: 1.0.30001702 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -10324,6 +11442,10 @@ packages: resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} dev: false + /bowser@2.11.0: + resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} + dev: false + /boxen@6.2.1: resolution: {integrity: sha512-H4PEsJXfFI/Pt8sjDWbHlQPx4zL/bvSQjcilJmaulGt5mLDorHOHpmdXAJcBcmru7PhYSp/cDMWRko4ZUMFkSw==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -10374,8 +11496,8 @@ packages: engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true dependencies: - caniuse-lite: 1.0.30001701 - electron-to-chromium: 1.5.110 + caniuse-lite: 1.0.30001702 + electron-to-chromium: 1.5.112 node-releases: 2.0.19 update-browserslist-db: 1.1.3(browserslist@4.24.4) @@ -10532,13 +11654,13 @@ packages: resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} dependencies: browserslist: 4.24.4 - caniuse-lite: 1.0.30001701 + caniuse-lite: 1.0.30001702 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 dev: false - /caniuse-lite@1.0.30001701: - resolution: {integrity: sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==} + /caniuse-lite@1.0.30001702: + resolution: {integrity: sha512-LoPe/D7zioC0REI5W73PeR1e1MLCipRGq/VkovJnd6Df+QVqT+vT33OXCp8QUd7kA7RZrHWxb1B36OQKI/0gOA==} /capital-case@1.0.4: resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==} @@ -10729,6 +11851,9 @@ packages: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} dev: true + /class-transformer@0.5.1: + resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} + /class-validator@0.14.1: resolution: {integrity: sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==} dependencies: @@ -12018,8 +13143,8 @@ packages: jake: 10.9.2 dev: true - /electron-to-chromium@1.5.110: - resolution: {integrity: sha512-/p/OvOm6AfLtQteAHTUWwf+Vhh76PlluagzQlSnxMoOJ4R6SmAScWBrVev6rExJoUhP9zudN9+lBxoYUEmC1HQ==} + /electron-to-chromium@1.5.112: + resolution: {integrity: sha512-oen93kVyqSb3l+ziUgzIOlWt/oOuy4zRmpwestMn4rhFWAoFJeFuCVte9F2fASjeZZo7l/Cif9TiyrdW4CwEMA==} /emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -12862,6 +13987,13 @@ packages: engines: {node: ^12.20 || >= 14.13} dev: true + /extract-files@13.0.0: + resolution: {integrity: sha512-FXD+2Tsr8Iqtm3QZy1Zmwscca7Jx3mMC5Crr+sEP1I303Jy1CYMuYCm7hRTplFNg3XdUavErkxnTzpaqdSoi6g==} + engines: {node: ^14.17.0 || ^16.0.0 || >= 18.0.0} + dependencies: + is-plain-obj: 4.1.0 + dev: false + /fast-content-type-parse@2.0.1: resolution: {integrity: sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==} dev: false @@ -12906,6 +14038,13 @@ packages: /fast-uri@3.0.6: resolution: {integrity: sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==} + /fast-xml-parser@4.4.1: + resolution: {integrity: sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==} + hasBin: true + dependencies: + strnum: 1.1.2 + dev: false + /fastembed@1.14.1: resolution: {integrity: sha512-Y14v+FWZwjNUpQ7mRGYu4N5yF+hZkF7zqzPWzzLbwdIEtYsHy0DSpiVJ+Fg6Oi1fQjrBKASQt0hdSMSjw1/Wtw==} dependencies: @@ -13295,8 +14434,8 @@ packages: tslib: 2.8.1 dev: false - /framer-motion@12.4.9(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-c+nDhfiNUwi8G4BrhrP2hjPsDHzIKRbUhDlcK7oC5kXY4QK1IrT/kuhY4BgK6h2ujDrZ8ocvFrG2X8+b1m/MkQ==} + /framer-motion@12.4.10(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-3Msuyjcr1Pb5hjkn4EJcRe1HumaveP0Gbv4DBMKTPKcV/1GSMkQXj+Uqgneys+9DPcZM18Hac9qY9iUEF5LZtg==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -13309,8 +14448,8 @@ packages: react-dom: optional: true dependencies: - motion-dom: 12.4.5 - motion-utils: 12.0.0 + motion-dom: 12.4.10 + motion-utils: 12.4.10 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) tslib: 2.8.1 @@ -13667,12 +14806,12 @@ packages: cosmiconfig-toml-loader: optional: true dependencies: - '@graphql-tools/graphql-file-loader': 8.0.16(graphql@16.10.0) - '@graphql-tools/json-file-loader': 8.0.15(graphql@16.10.0) - '@graphql-tools/load': 8.0.16(graphql@16.10.0) - '@graphql-tools/merge': 9.0.21(graphql@16.10.0) - '@graphql-tools/url-loader': 8.0.28(@types/node@22.13.9)(graphql@16.10.0) - '@graphql-tools/utils': 10.8.3(graphql@16.10.0) + '@graphql-tools/graphql-file-loader': 8.0.17(graphql@16.10.0) + '@graphql-tools/json-file-loader': 8.0.16(graphql@16.10.0) + '@graphql-tools/load': 8.0.17(graphql@16.10.0) + '@graphql-tools/merge': 9.0.22(graphql@16.10.0) + '@graphql-tools/url-loader': 8.0.29(@types/node@22.13.9)(graphql@16.10.0) + '@graphql-tools/utils': 10.8.4(graphql@16.10.0) cosmiconfig: 8.3.6(typescript@5.6.3) graphql: 16.10.0 jiti: 2.4.2 @@ -13718,6 +14857,16 @@ packages: graphql: 16.10.0 tslib: 2.8.1 + /graphql-upload-minimal@1.6.1(graphql@16.10.0): + resolution: {integrity: sha512-wNUf/KqA0B/OguL1k6qWa4AmAduLUAhXzovh9i14SKbpBa8HX2vc7f+fR67S0rG7fSpGdM/aivxzC329/+9xXw==} + engines: {node: '>=12'} + peerDependencies: + graphql: 0.13.1 - 16 + dependencies: + busboy: 1.6.0 + graphql: 16.10.0 + dev: false + /graphql-ws@5.16.0(graphql@16.10.0): resolution: {integrity: sha512-Ju2RCU2dQMgSKtArPbEtsK5gNLnsQyTNIo/T7cZNp96niC1x0KdJNZV0TIoilceBPQwfb5itrGl8pkFeOUMl4A==} engines: {node: '>=10'} @@ -15992,7 +17141,7 @@ packages: js-tiktoken: 1.0.19 js-yaml: 4.1.0 jsonpointer: 5.0.1 - langsmith: 0.3.11(openai@4.86.1) + langsmith: 0.3.12(openai@4.86.1) openapi-types: 12.1.3 p-retry: 4.6.2 uuid: 10.0.0 @@ -16005,8 +17154,8 @@ packages: - ws dev: false - /langsmith@0.3.11(openai@4.86.1): - resolution: {integrity: sha512-pzA7wemfMjqCiaNY3AtUkQJ7jubIBmKRTl0dMNEUz8A4ewIqCEpB2caiTeeAwVkugEylny80cDk3u16WqL25Sw==} + /langsmith@0.3.12(openai@4.86.1): + resolution: {integrity: sha512-e4qWM27hxEr8GfO6dgXrc3W8La+wxkX1zEtMhxhqS/Th2ujTt5OH7x0uXfXFDqCv9WaC3nquo1Y2s4vpYmLLtg==} peerDependencies: openai: '*' peerDependenciesMeta: @@ -17257,22 +18406,22 @@ packages: motion-utils: 11.18.1 dev: false - /motion-dom@12.4.5: - resolution: {integrity: sha512-Q2xmhuyYug1CGTo0jdsL05EQ4RhIYXlggFS/yPhQQRNzbrhjKQ1tbjThx5Plv68aX31LsUQRq4uIkuDxdO5vRQ==} + /motion-dom@12.4.10: + resolution: {integrity: sha512-ISP5u6FTceoD6qKdLupIPU/LyXBrxGox+P2e3mBbm1+pLdlBbwv01YENJr7+1WZnW5ucVKzFScYsV1eXTCG4Xg==} dependencies: - motion-utils: 12.0.0 + motion-utils: 12.4.10 dev: false /motion-utils@11.18.1: resolution: {integrity: sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==} dev: false - /motion-utils@12.0.0: - resolution: {integrity: sha512-MNFiBKbbqnmvOjkPyOKgHUp3Q6oiokLkI1bEwm5QA28cxMZrv0CbbBGDNmhF6DIXsi1pCQBSs0dX8xjeER1tmA==} + /motion-utils@12.4.10: + resolution: {integrity: sha512-NPwZd94V013SwRf++jMrk2+HEBgPkeIE2RiOzhAuuQlqxMJPkKt/LXVh6Upl+iN8oarSGD2dlY5/bqgsYXDABA==} dev: false - /motion@12.4.9(react-dom@18.3.1)(react@18.3.1): - resolution: {integrity: sha512-lOT2+X9b3yvDEC+pAClTzLSW/D5T/zZweO+UN1lMe86WtGFQIbHU/VjEhwGREW0QryG9KECB1uK3QJo8G3NGag==} + /motion@12.4.10(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-AM21Lyfn7ZHO+nBuHJEA2REFgS3kUM83CLZnzM0ZY1/sVeKGkCtV4LF4O/YsQXyZ9mrUrrnTaUkKquS4eaIYjg==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -17285,7 +18434,7 @@ packages: react-dom: optional: true dependencies: - framer-motion: 12.4.9(react-dom@18.3.1)(react@18.3.1) + framer-motion: 12.4.10(react-dom@18.3.1)(react@18.3.1) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) tslib: 2.8.1 @@ -17405,7 +18554,7 @@ packages: '@playwright/test': 1.50.1 '@swc/helpers': 0.5.5 busboy: 1.6.0 - caniuse-lite: 1.0.30001701 + caniuse-lite: 1.0.30001702 graceful-fs: 4.2.11 postcss: 8.4.31 react: 18.3.1 @@ -19454,8 +20603,8 @@ packages: strip-json-comments: 2.0.1 dev: false - /react-activity-calendar@2.7.8(react@18.3.1): - resolution: {integrity: sha512-lj9IIMrRAoMsXSf6wWo7AcMNXie61Y5EuNApm6rVdJswngyw8LS2Ja50yHrGBFu9GaL1HMutGIoSbr5ifEi9xw==} + /react-activity-calendar@2.7.9(react@18.3.1): + resolution: {integrity: sha512-35B65SiJ18aTEHzK6MQcvPVLFSBr95L/QATM9WaPGSli6hHPV2LlGuoOflYG/TuMlmmcUaBCHMjG6cHwuRzwXQ==} peerDependencies: react: ^18.0.0 || ^19.0.0 dependencies: @@ -21184,6 +22333,10 @@ packages: engines: {node: '>=14.16'} dev: false + /strnum@1.1.2: + resolution: {integrity: sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA==} + dev: false + /strtok3@6.3.0: resolution: {integrity: sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==} engines: {node: '>=10'} @@ -22711,7 +23864,7 @@ packages: '@types/serve-index': 1.9.4 '@types/serve-static': 1.15.7 '@types/sockjs': 0.3.36 - '@types/ws': 8.5.14 + '@types/ws': 8.18.0 ansi-html-community: 0.0.8 bonjour-service: 1.3.0 chokidar: 3.6.0 From 90ebd1f98889e6b6b4f4930d3a9138ea7593aa57 Mon Sep 17 00:00:00 2001 From: NarwhalChen <125920907+NarwhalChen@users.noreply.github.com> Date: Tue, 4 Mar 2025 18:40:08 -0600 Subject: [PATCH 04/13] fix: cannot redirect to project page for the first time (#155) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Refactor** - Improved chat session navigation for smoother transitions and a more integrated user experience. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --- frontend/src/components/sidebar.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/sidebar.tsx b/frontend/src/components/sidebar.tsx index 04939509..7f5c4311 100644 --- a/frontend/src/components/sidebar.tsx +++ b/frontend/src/components/sidebar.tsx @@ -20,6 +20,7 @@ import { } from './ui/sidebar'; import { cn } from '@/lib/utils'; import { ProjectContext } from './chat/code-engine/project-context'; +import { useRouter } from 'next/navigation'; interface SidebarProps { setIsModalOpen: (value: boolean) => void; // Parent setter to update collapse state @@ -57,6 +58,7 @@ export function ChatSideBar({ const event = new Event(EventEnum.NEW_CHAT); window.dispatchEvent(event); }, []); + const router = useRouter(); if (loading) return <SidebarSkeleton />; if (error) { @@ -151,11 +153,7 @@ export function ChatSideBar({ pollChatProject(chat.id).then((p) => { setCurProject(p); }); - window.history.replaceState( - {}, - '', - `/chat?id=${chat.id}` - ); + router.push(`/chat?id=${chat.id}`); setCurrentChatid(chat.id); }} refetchChats={onRefetch} From f92c7888dd030d17bf975667a1c17a1bd29dfa2c Mon Sep 17 00:00:00 2001 From: NarwhalChen <125920907+NarwhalChen@users.noreply.github.com> Date: Tue, 4 Mar 2025 20:29:54 -0600 Subject: [PATCH 05/13] =?UTF-8?q?fix:=20fixing=20bug=20of=20cannot=20runni?= =?UTF-8?q?ng=20docker=20container=20after=20container=20is=E2=80=A6=20(#1?= =?UTF-8?q?47)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … shut down --- frontend/src/app/api/file/route.ts | 2 + frontend/src/app/api/runProject/route.ts | 95 ++++++++++++++++--- frontend/src/components/chat/chat-layout.tsx | 15 ++- .../chat/code-engine/code-engine.tsx | 12 ++- .../components/chat/code-engine/web-view.tsx | 88 ++++++++++++++++- frontend/src/components/root/root-layout.tsx | 28 +++--- 6 files changed, 195 insertions(+), 45 deletions(-) diff --git a/frontend/src/app/api/file/route.ts b/frontend/src/app/api/file/route.ts index 7205aaa2..9892ac00 100644 --- a/frontend/src/app/api/file/route.ts +++ b/frontend/src/app/api/file/route.ts @@ -59,6 +59,8 @@ function getFileType(filePath: string): string { const extension = filePath.split('.').pop()?.toLowerCase() || ''; const typeMap: { [key: string]: string } = { + //TODO: Add more file types + tsx: 'typescriptreact', txt: 'text', md: 'markdown', json: 'json', diff --git a/frontend/src/app/api/runProject/route.ts b/frontend/src/app/api/runProject/route.ts index 45b5d909..cb7269b1 100644 --- a/frontend/src/app/api/runProject/route.ts +++ b/frontend/src/app/api/runProject/route.ts @@ -128,6 +128,9 @@ async function buildAndRunDocker( return new Promise((resolve, reject) => { // 2. Build the Docker image + console.log( + `Starting Docker build for image: ${imageName} in directory: ${directory}` + ); exec( `docker build -t ${imageName} ${directory}`, (buildErr, buildStdout, buildStderr) => { @@ -141,19 +144,61 @@ async function buildAndRunDocker( // 3. Run the Docker container const runCommand = `docker run -d --name ${containerName} -l "traefik.enable=true" \ - -l "traefik.http.routers.${subdomain}.rule=Host(\\"${domain}\\")" \ - -l "traefik.http.services.${subdomain}.loadbalancer.server.port=5173" \ - --network=codefox_traefik_network -p ${exposedPort}:5173 \ - -v "${directory}:/app" \ - ${imageName}`; + -l "traefik.http.routers.${subdomain}.rule=Host(\\"${domain}\\")" \ + -l "traefik.http.services.${subdomain}.loadbalancer.server.port=5173" \ + --network=codefox_traefik_network -p ${exposedPort}:5173 \ + -v "${directory}:/app" \ + ${imageName}`; - console.log(runCommand); + console.log(`Executing run command: ${runCommand}`); exec(runCommand, (runErr, runStdout, runStderr) => { if (runErr) { // If the container name already exists + console.error(`Error during Docker run: ${runStderr}`); if (runStderr.includes('Conflict. The container name')) { - resolve({ domain, containerId: containerName }); + console.log( + `Container name conflict detected. Removing existing container ${containerName}.` + ); + // Remove the existing container + exec( + `docker rm -f ${containerName}`, + (removeErr, removeStdout, removeStderr) => { + if (removeErr) { + console.error( + `Error removing existing container: ${removeStderr}` + ); + return reject(removeErr); + } + console.log( + `Existing container ${containerName} removed. Retrying to run the container.` + ); + + // Retry running the Docker container + exec( + runCommand, + (retryRunErr, retryRunStdout, retryRunStderr) => { + if (retryRunErr) { + console.error( + `Error during Docker run: ${retryRunStderr}` + ); + return reject(retryRunErr); + } + + const containerActualId = retryRunStdout.trim(); + runningContainers.set(projectPath, { + domain, + containerId: containerActualId, + }); + + console.log( + `Container ${containerName} is now running at http://${domain}` + ); + resolve({ domain, containerId: containerActualId }); + } + ); + } + ); return; } console.error(`Error during Docker run: ${runStderr}`); @@ -169,7 +214,6 @@ async function buildAndRunDocker( console.log( `Container ${containerName} is now running at http://${domain}` ); - resolve({ domain, containerId: containerActualId }); }); } @@ -204,11 +248,38 @@ export async function GET(req: Request) { // Check if a container is already running const existingContainer = runningContainers.get(projectPath); if (existingContainer) { - return NextResponse.json({ - message: 'Docker container already running', - domain: existingContainer.domain, - containerId: existingContainer.containerId, + // Check if the container is running + const containerStatus = await new Promise<string>((resolve) => { + exec( + `docker inspect -f "{{.State.Running}}" ${existingContainer.containerId}`, + (err, stdout) => { + if (err) { + resolve('not found'); + } else { + resolve(stdout.trim()); + } + } + ); }); + + if (containerStatus === 'true') { + return NextResponse.json({ + message: 'Docker container already running', + domain: existingContainer.domain, + containerId: existingContainer.containerId, + }); + } else { + // Remove the existing container if it's not running + exec(`docker rm -f ${existingContainer.containerId}`, (removeErr) => { + if (removeErr) { + console.error(`Error removing existing container: ${removeErr}`); + } else { + console.log( + `Removed existing container: ${existingContainer.containerId}` + ); + } + }); + } } // Prevent duplicate builds diff --git a/frontend/src/components/chat/chat-layout.tsx b/frontend/src/components/chat/chat-layout.tsx index 55663614..0f13d951 100644 --- a/frontend/src/components/chat/chat-layout.tsx +++ b/frontend/src/components/chat/chat-layout.tsx @@ -5,7 +5,6 @@ import ProjectModal from '@/components/chat/project-modal'; import { useQuery } from '@apollo/client'; import { GET_USER_PROJECTS } from '@/graphql/request'; import { useAuthContext } from '@/providers/AuthProvider'; -import { ProjectProvider } from './code-engine/project-context'; export default function ChatLayout({ children, @@ -30,14 +29,12 @@ export default function ChatLayout({ return ( <main className="flex h-[calc(100dvh)] flex-col items-center"> - <ProjectProvider> - <ProjectModal - isOpen={isModalOpen} - onClose={() => setIsModalOpen(false)} - refetchProjects={refetch} - /> - <div className="w-full h-full">{children}</div> - </ProjectProvider> + <ProjectModal + isOpen={isModalOpen} + onClose={() => setIsModalOpen(false)} + refetchProjects={refetch} + /> + <div className="w-full h-full">{children}</div> </main> ); } diff --git a/frontend/src/components/chat/code-engine/code-engine.tsx b/frontend/src/components/chat/code-engine/code-engine.tsx index 0e0ab493..64f732e7 100644 --- a/frontend/src/components/chat/code-engine/code-engine.tsx +++ b/frontend/src/components/chat/code-engine/code-engine.tsx @@ -82,12 +82,16 @@ export function CodeEngine({ // Effect: Fetch file structure when projectId changes useEffect(() => { async function fetchFiles() { - if (!curProject?.projectPath) return; + if (!curProject?.projectPath) { + console.log('no project path found'); + return; + } try { const response = await fetch( `/api/project?path=${curProject.projectPath}` ); + console.log('loading file structure'); const data = await response.json(); setFileStructureData(data.res || {}); } catch (error) { @@ -270,12 +274,12 @@ export function CodeEngine({ // Render the CodeEngine layout return ( - <div className="rounded-lg border shadow-sm overflow-hidden h-full"> + <div className="rounded-lg border shadow-sm overflow-scroll h-full"> {/* Header Bar */} <ResponsiveToolbar isLoading={!isProjectReady} /> {/* Main Content Area with Loading */} - <div className="relative h-[calc(100vh-48px-2rem)]"> + <div className="relative h-[calc(100vh-48px-4rem)]"> <AnimatePresence> {!isProjectReady && ( <motion.div @@ -311,7 +315,7 @@ export function CodeEngine({ <Editor height="100%" width="100%" - defaultLanguage="typescript" + defaultLanguage="typescriptreact" value={newCode} language={type} loading={isLoading} diff --git a/frontend/src/components/chat/code-engine/web-view.tsx b/frontend/src/components/chat/code-engine/web-view.tsx index 91562ed9..86d3d890 100644 --- a/frontend/src/components/chat/code-engine/web-view.tsx +++ b/frontend/src/components/chat/code-engine/web-view.tsx @@ -7,6 +7,9 @@ import { ChevronRight, Maximize, ExternalLink, + RefreshCcw, + ZoomIn, + ZoomOut, } from 'lucide-react'; export default function WebPreview() { @@ -15,6 +18,7 @@ export default function WebPreview() { const [displayPath, setDisplayPath] = useState('/'); const [history, setHistory] = useState<string[]>(['/']); const [currentIndex, setCurrentIndex] = useState(0); + const [scale, setScale] = useState(0.7); const iframeRef = useRef(null); const containerRef = useRef<{ projectPath: string; domain: string } | null>( null @@ -49,13 +53,34 @@ export default function WebPreview() { ); const json = await response.json(); - await new Promise((resolve) => setTimeout(resolve, 200)); + await new Promise((resolve) => setTimeout(resolve, 100)); containerRef.current = { projectPath, domain: json.domain, }; - setBaseUrl(`http://${json.domain}`); + + const checkUrlStatus = async (url: string) => { + let status = 0; + while (status !== 200) { + try { + const res = await fetch(url, { method: 'HEAD' }); + status = res.status; + if (status !== 200) { + console.log(`URL status: ${status}. Retrying...`); + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + } catch (err) { + console.error('Error checking URL status:', err); + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + } + }; + + const baseUrl = `http://${json.domain}`; + await checkUrlStatus(baseUrl); + + setBaseUrl(baseUrl); setDisplayPath('/'); } catch (error) { console.error('fetching url error:', error); @@ -109,6 +134,25 @@ export default function WebPreview() { setDisplayPath(history[currentIndex + 1]); } }; + const reloadIframe = () => { + const iframe = document.getElementById('myIframe') as HTMLIFrameElement; + if (iframe) { + const src = iframe.src; + iframe.src = 'about:blank'; + setTimeout(() => { + iframe.src = src; + setScale(0.7); + }, 50); + } + }; + + const zoomIn = () => { + setScale((prevScale) => Math.min(prevScale + 0.1, 2)); // 最大缩放比例为 2 + }; + + const zoomOut = () => { + setScale((prevScale) => Math.max(prevScale - 0.1, 0.5)); // 最小缩放比例为 0.5 + }; return ( <div className="flex flex-col w-full h-full"> @@ -119,7 +163,7 @@ export default function WebPreview() { <Button variant="ghost" size="icon" - className="h-8 w-8" + className="h-6 w-6" onClick={goBack} disabled={!baseUrl || currentIndex === 0} > @@ -128,12 +172,20 @@ export default function WebPreview() { <Button variant="ghost" size="icon" - className="h-8 w-8" + className="h-6 w-6" onClick={goForward} disabled={!baseUrl || currentIndex >= history.length - 1} > <ChevronRight className="h-4 w-4" /> </Button> + <Button + variant="ghost" + size="icon" + className="h-6 w-6" + onClick={reloadIframe} + > + <RefreshCcw /> + </Button> </div> {/* URL Input */} @@ -150,6 +202,24 @@ export default function WebPreview() { {/* Actions */} <div className="flex items-center gap-1"> + <Button + variant="ghost" + size="icon" + onClick={zoomOut} + className="h-8 w-8" + disabled={!baseUrl} + > + <ZoomOut className="h-4 w-4" /> + </Button> + <Button + variant="ghost" + size="icon" + onClick={zoomIn} + className="h-8 w-8" + disabled={!baseUrl} + > + <ZoomIn className="h-4 w-4" /> + </Button> <Button variant="ghost" size="icon" @@ -175,9 +245,17 @@ export default function WebPreview() { <div className="relative flex-1 w-full h-full"> {baseUrl ? ( <iframe + id="myIframe" ref={iframeRef} src={`${baseUrl}${displayPath}`} - className="absolute inset-0 w-full h-full border-none bg-background" + className="absolute inset-0 w-full h-80% border-none bg-background" + style={{ + transform: `scale(${scale})`, + transformOrigin: 'top left', + width: `calc(100% / ${scale})`, + height: `calc(100% / ${scale})`, + border: 'none', + }} /> ) : ( <div className="absolute inset-0 w-full h-full flex items-center justify-center bg-background"> diff --git a/frontend/src/components/root/root-layout.tsx b/frontend/src/components/root/root-layout.tsx index 3074b7e1..7fc8ff05 100644 --- a/frontend/src/components/root/root-layout.tsx +++ b/frontend/src/components/root/root-layout.tsx @@ -42,21 +42,19 @@ export default function RootLayout({ children }: RootLayoutProps) { className="fixed left-0 top-0 h-full z-50" style={{ width: isCollapsed ? '80px' : '250px' }} > - <ProjectProvider> - <ChatSideBar - setIsModalOpen={() => {}} - isCollapsed={isCollapsed} - setIsCollapsed={setIsCollapsed} - isMobile={false} - currentChatId={''} - chatListUpdated={chatListUpdated} - setChatListUpdated={setChatListUpdated} - chats={chats} - loading={loading} - error={error} - onRefetch={refetchChats} - /> - </ProjectProvider> + <ChatSideBar + setIsModalOpen={() => {}} + isCollapsed={isCollapsed} + setIsCollapsed={setIsCollapsed} + isMobile={false} + currentChatId={''} + chatListUpdated={chatListUpdated} + setChatListUpdated={setChatListUpdated} + chats={chats} + loading={loading} + error={error} + onRefetch={refetchChats} + /> </motion.div> )} From af3af8ee065b115645fe6d8de3ae2c5fa1401a60 Mon Sep 17 00:00:00 2001 From: ZHallen122 <106571949+ZHallen122@users.noreply.github.com> Date: Tue, 4 Mar 2025 21:34:44 -0500 Subject: [PATCH 06/13] feat: implement SMTP email service, adding env also adding email verification (#153) This creates backend email service support. 1, send verification email 2, confirm token for User email 3, resend email 4, template for verification email Frontend: 1, register to tell user check email 2, after user click confirm email link. Frontend should send request to backend to do check Vedio: https://jam.dev/c/1e63139a-e24f-4baf-968c-a9d2e771fef0 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a robust email verification workflow that requires users to confirm their email addresses upon registration. - Added the ability to resend confirmation emails and a dedicated confirmation page that provides real-time success or error feedback. - **Chores** - Expanded configuration options to support customizable email settings via environment variables. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- backend/.env.example | 11 + backend/package.json | 2 + backend/src/app.module.ts | 2 + backend/src/auth/auth.module.ts | 2 + backend/src/auth/auth.resolver.ts | 16 + backend/src/auth/auth.service.ts | 143 +- backend/src/mail/mail.module.ts | 47 + backend/src/mail/mail.service.ts | 59 + backend/src/mail/templates/confirmation.hbs | 26 + backend/src/mail/templates/passwordReset.hbs | 41 + backend/src/user/dto/resend-email.input.ts | 11 + backend/src/user/user.model.ts | 9 + backend/src/user/user.module.ts | 9 +- backend/src/user/user.resolver.ts | 16 + frontend/src/app/auth/confirm/page.tsx | 102 ++ frontend/src/components/sign-up-modal.tsx | 264 +++- frontend/src/graphql/mutations/auth.ts | 19 + pnpm-lock.yaml | 1473 +++++++++++++++++- 18 files changed, 2149 insertions(+), 103 deletions(-) create mode 100644 backend/src/mail/mail.module.ts create mode 100644 backend/src/mail/mail.service.ts create mode 100644 backend/src/mail/templates/confirmation.hbs create mode 100644 backend/src/mail/templates/passwordReset.hbs create mode 100644 backend/src/user/dto/resend-email.input.ts create mode 100644 frontend/src/app/auth/confirm/page.tsx diff --git a/backend/.env.example b/backend/.env.example index 7f8afe80..c3c50c60 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -21,3 +21,14 @@ S3_ENDPOINT="https://<account_id>.r2.cloudflarestorage.com" # Cloudflare R2 e S3_ACCOUNT_ID="your_cloudflare_account_id" # Your Cloudflare account ID S3_PUBLIC_URL="https://pub-xxx.r2.dev" # Your R2 public bucket URL +# mail +# Set to false to disable all email functionality +MAIL_ENABLED=false + +MAIL_HOST=smtp.example.com +MAIL_USER=user@example.com +MAIL_PASSWORD=topsecret +MAIL_FROM=noreply@example.com +MAIL_PORT=587 +MAIL_DOMAIN=your_net + diff --git a/backend/package.json b/backend/package.json index d084f34d..aa09d147 100644 --- a/backend/package.json +++ b/backend/package.json @@ -31,6 +31,7 @@ "@aws-sdk/client-s3": "^3.758.0", "@huggingface/hub": "latest", "@huggingface/transformers": "latest", + "@nestjs-modules/mailer": "^2.0.2", "@nestjs/apollo": "^12.2.0", "@nestjs/axios": "^3.0.3", "@nestjs/common": "^10.0.0", @@ -59,6 +60,7 @@ "graphql-ws": "^5.16.0", "lodash": "^4.17.21", "markdown-to-txt": "^2.0.1", + "nodemailer": "^6.10.0", "normalize-path": "^3.0.0", "openai": "^4.77.0", "p-queue-es5": "^6.0.2", diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index 041abc60..8a5c4a26 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -15,6 +15,7 @@ import { AppResolver } from './app.resolver'; import { APP_INTERCEPTOR } from '@nestjs/core'; import { LoggingInterceptor } from 'src/interceptor/LoggingInterceptor'; import { PromptToolModule } from './prompt-tool/prompt-tool.module'; +import { MailModule } from './mail/mail.module'; // TODO(Sma1lboy): move to a separate file function isProduction(): boolean { @@ -47,6 +48,7 @@ function isProduction(): boolean { TokenModule, ChatModule, PromptToolModule, + MailModule, TypeOrmModule.forFeature([User]), ], providers: [ diff --git a/backend/src/auth/auth.module.ts b/backend/src/auth/auth.module.ts index 91ea39b8..1065fcf5 100644 --- a/backend/src/auth/auth.module.ts +++ b/backend/src/auth/auth.module.ts @@ -9,6 +9,7 @@ import { User } from 'src/user/user.model'; import { AuthResolver } from './auth.resolver'; import { RefreshToken } from './refresh-token/refresh-token.model'; import { JwtCacheModule } from 'src/jwt-cache/jwt-cache.module'; +import { MailModule } from 'src/mail/mail.module'; @Module({ imports: [ @@ -23,6 +24,7 @@ import { JwtCacheModule } from 'src/jwt-cache/jwt-cache.module'; inject: [ConfigService], }), JwtCacheModule, + MailModule, ], providers: [AuthService, AuthResolver], exports: [AuthService, JwtModule], diff --git a/backend/src/auth/auth.resolver.ts b/backend/src/auth/auth.resolver.ts index b0d3946e..316f2042 100644 --- a/backend/src/auth/auth.resolver.ts +++ b/backend/src/auth/auth.resolver.ts @@ -18,6 +18,15 @@ export class RefreshTokenResponse { refreshToken: string; } +@ObjectType() +export class EmailConfirmationResponse { + @Field() + message: string; + + @Field({ nullable: true }) + success?: boolean; +} + @Resolver() export class AuthResolver { constructor(private readonly authService: AuthService) {} @@ -33,4 +42,11 @@ export class AuthResolver { ): Promise<RefreshTokenResponse> { return this.authService.refreshToken(refreshToken); } + + @Mutation(() => EmailConfirmationResponse) + async confirmEmail( + @Args('token') token: string, + ): Promise<EmailConfirmationResponse> { + return this.authService.confirmEmail(token); + } } diff --git a/backend/src/auth/auth.service.ts b/backend/src/auth/auth.service.ts index 8c94a22c..413261ad 100644 --- a/backend/src/auth/auth.service.ts +++ b/backend/src/auth/auth.service.ts @@ -19,23 +19,127 @@ import { Role } from './role/role.model'; import { RefreshToken } from './refresh-token/refresh-token.model'; import { randomUUID } from 'crypto'; import { compare, hash } from 'bcrypt'; -import { RefreshTokenResponse } from './auth.resolver'; +import { + EmailConfirmationResponse, + RefreshTokenResponse, +} from './auth.resolver'; +import { MailService } from 'src/mail/mail.service'; @Injectable() export class AuthService { + private readonly isMailEnabled: boolean; + constructor( @InjectRepository(User) private userRepository: Repository<User>, private jwtService: JwtService, private jwtCacheService: JwtCacheService, private configService: ConfigService, + private mailService: MailService, @InjectRepository(Menu) private menuRepository: Repository<Menu>, @InjectRepository(Role) private roleRepository: Repository<Role>, @InjectRepository(RefreshToken) private refreshTokenRepository: Repository<RefreshToken>, - ) {} + ) { + // Read the MAIL_ENABLED environment variable, default to 'true' + this.isMailEnabled = + this.configService.get<string>('MAIL_ENABLED', 'true').toLowerCase() === + 'true'; + } + + async confirmEmail(token: string): Promise<EmailConfirmationResponse> { + try { + const payload = await this.jwtService.verifyAsync(token); + + // Check if payload has the required email field + if (!payload || !payload.email) { + return { + message: 'Invalid token format', + success: false, + }; + } + + // Find user and update + const user = await this.userRepository.findOne({ + where: { email: payload.email }, + }); + + if (user && !user.isEmailConfirmed) { + user.isEmailConfirmed = true; + await this.userRepository.save(user); + + return { + message: 'Email confirmed successfully!', + success: true, + }; + } + + return { + message: 'Email already confirmed or user not found.', + success: false, + }; + } catch (error) { + return { + message: 'Invalid or expired token', + success: false, + }; + } + } + + async sendVerificationEmail(user: User): Promise<EmailConfirmationResponse> { + // Generate confirmation token + const verifyToken = this.jwtService.sign( + { email: user.email }, + { expiresIn: '30m' }, + ); + + // Send confirmation email + await this.mailService.sendConfirmationEmail(user.email, verifyToken); + + // update user last time send email time + user.lastEmailSendTime = new Date(); + await this.userRepository.save(user); + + return { + message: 'Verification email sent successfully!', + success: true, + }; + } + + async resendVerificationEmail(email: string) { + const user = await this.userRepository.findOne({ + where: { email }, + }); + + if (!user) { + throw new Error('User not found'); + } + + if (user.isEmailConfirmed) { + return { message: 'Email already confirmed!' }; + } + + // Check if a cooldown period has passed (e.g., 1 minute) + const cooldownPeriod = 1 * 60 * 1000; // 1 minute in milliseconds + if ( + user.lastEmailSendTime && + new Date().getTime() - user.lastEmailSendTime.getTime() < cooldownPeriod + ) { + const timeLeft = Math.ceil( + (cooldownPeriod - + (new Date().getTime() - user.lastEmailSendTime.getTime())) / + 1000, + ); + return { + message: `Please wait ${timeLeft} seconds before requesting another email`, + success: false, + }; + } + + return this.sendVerificationEmail(user); + } async register(registerUserInput: RegisterUserInput): Promise<User> { const { username, email, password } = registerUserInput; @@ -50,13 +154,31 @@ export class AuthService { } const hashedPassword = await hash(password, 10); - const newUser = this.userRepository.create({ - username, - email, - password: hashedPassword, - }); - return this.userRepository.save(newUser); + let newUser; + if (this.isMailEnabled) { + newUser = this.userRepository.create({ + username, + email, + password: hashedPassword, + isEmailConfirmed: false, + }); + } else { + newUser = this.userRepository.create({ + username, + email, + password: hashedPassword, + isEmailConfirmed: true, + }); + } + + await this.userRepository.save(newUser); + + if (this.isMailEnabled) { + await this.sendVerificationEmail(newUser); + } + + return newUser; } async login(loginUserInput: LoginUserInput): Promise<RefreshTokenResponse> { @@ -70,6 +192,10 @@ export class AuthService { throw new UnauthorizedException('Invalid credentials'); } + if (!user.isEmailConfirmed) { + throw new Error('Email not confirmed. Please check your inbox.'); + } + const isPasswordValid = await compare(password, user.password); if (!isPasswordValid) { @@ -113,6 +239,7 @@ export class AuthService { return false; } } + async logout(token: string): Promise<boolean> { try { await this.jwtService.verifyAsync(token); diff --git a/backend/src/mail/mail.module.ts b/backend/src/mail/mail.module.ts new file mode 100644 index 00000000..8767bfd8 --- /dev/null +++ b/backend/src/mail/mail.module.ts @@ -0,0 +1,47 @@ +import { MailerModule } from '@nestjs-modules/mailer'; +import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter'; +import { Module } from '@nestjs/common'; +import { MailService } from './mail.service'; +import { join } from 'path'; +import { ConfigModule, ConfigService } from '@nestjs/config'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { User } from 'src/user/user.model'; +import { JwtModule } from '@nestjs/jwt'; + +@Module({ + imports: [ + ConfigModule, + TypeOrmModule.forFeature([User]), + MailerModule.forRootAsync({ + // imports: [ConfigModule], // import module if not enabled globally + useFactory: async (config: ConfigService) => ({ + // transport: config.get("MAIL_TRANSPORT"), + // or + transport: { + host: config.get('MAIL_HOST'), + port: config.get<number>('MAIL_PORT'), + secure: false, + auth: { + user: config.get('MAIL_USER'), + pass: config.get('MAIL_PASSWORD'), + }, + }, + defaults: { + from: `"Your App" <${config.get<string>('MAIL_FROM')}>`, + }, + template: { + dir: join(__dirname, 'templates'), + adapter: new HandlebarsAdapter(), + options: { + strict: true, + }, + }, + }), + inject: [ConfigService], + }), + JwtModule, + ], + providers: [MailService], + exports: [MailService], +}) +export class MailModule {} diff --git a/backend/src/mail/mail.service.ts b/backend/src/mail/mail.service.ts new file mode 100644 index 00000000..9b0d4dae --- /dev/null +++ b/backend/src/mail/mail.service.ts @@ -0,0 +1,59 @@ +import { Injectable } from '@nestjs/common'; +import { MailerService } from '@nestjs-modules/mailer'; +import { InjectRepository } from '@nestjs/typeorm'; +import { User } from 'src/user/user.model'; +import { Repository } from 'typeorm'; +import { ConfigService } from '@nestjs/config'; + +@Injectable() +export class MailService { + constructor( + @InjectRepository(User) + private userRepository: Repository<User>, + private readonly mailerService: MailerService, + private configService: ConfigService, + ) {} + + async sendConfirmationEmail(email: string, token: string) { + const confirmUrl = `https://${this.configService.get('MAIL_DOMAIN')}/auth/confirm?token=${token}`; + + await this.mailerService.sendMail({ + to: email, + subject: 'Confirm Your Email', + template: './confirmation', + context: { confirmUrl }, // Data for template + }); + } + + async sendPasswordResetEmail(user: User, token: string) { + const frontendUrl = this.configService.get('FRONTEND_URL'); + const url = `${frontendUrl}/reset-password?token=${token}`; + + await this.mailerService.sendMail({ + to: user.email, + subject: 'Password Reset Request', + template: './passwordReset', + context: { + name: user.username, + firstName: user.username, + url, + }, + }); + } + + // async sendConfirmationEmail(user: User, token: string) { + // const frontendUrl = this.configService.get('FRONTEND_URL'); + // const url = `${frontendUrl}/confirm-email?token=${token}`; + + // await this.mailerService.sendMail({ + // to: user.email, + // subject: 'Welcome! Confirm Your Email', + // template: './confirmation', // This will use the confirmation.hbs template + // context: { // Data to be sent to the template + // name: user.username, + // firstName: user.firstName || user.username, + // url, + // }, + // }); + // } +} diff --git a/backend/src/mail/templates/confirmation.hbs b/backend/src/mail/templates/confirmation.hbs new file mode 100644 index 00000000..603300b7 --- /dev/null +++ b/backend/src/mail/templates/confirmation.hbs @@ -0,0 +1,26 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="UTF-8"> + <title>Confirm Your Email</title> +</head> +<body style="font-family: Arial, sans-serif; background-color: #f4f4f4; padding: 20px; text-align: center;"> + <div style="max-width: 600px; background-color: #ffffff; padding: 30px; border-radius: 8px; margin: auto;"> + <h2 style="color: #333;">Welcome to CodeFox! 🎉</h2> + <p style="font-size: 16px; color: #555;"> + Hi there! Thanks for signing up. Please confirm your email address to activate your account. + </p> + <p> + <a href="{{confirmUrl}}" style="display: inline-block; padding: 12px 24px; font-size: 16px; color: #fff; background-color: #007bff; text-decoration: none; border-radius: 5px;"> + Confirm Email + </a> + </p> + <p style="font-size: 14px; color: #888;"> + If you didn't create an account, you can ignore this email. The confirmation link will expire in 24 hours. + </p> + <p style="font-size: 14px; color: #888;"> + Need help? Contact our support team at <a href="mailto:support@codefox.net">support@codefox.net</a>. + </p> + </div> +</body> +</html> diff --git a/backend/src/mail/templates/passwordReset.hbs b/backend/src/mail/templates/passwordReset.hbs new file mode 100644 index 00000000..e32326e2 --- /dev/null +++ b/backend/src/mail/templates/passwordReset.hbs @@ -0,0 +1,41 @@ +<!-- src/mail/templates/confirmation.hbs --> +<!DOCTYPE html> +<html> +<head> + <meta charset="utf-8"> + <title>Email Confirmation</title> + <style> + body { + font-family: Arial, sans-serif; + line-height: 1.6; + color: #333; + } + .container { + max-width: 600px; + margin: 0 auto; + padding: 20px; + } + .button { + display: inline-block; + padding: 10px 20px; + background-color: #3498db; + color: white; + text-decoration: none; + border-radius: 4px; + } + </style> +</head> +<body> + <div class="container"> + <h1>Welcome to Our App!</h1> + <p>Hello test user,</p> + <p>Thank you for registering. Please confirm your email address by clicking the button below:</p> + <p> + + </p> + <p>If you did not request this email, please ignore it.</p> + <p>This link will expire in 24 hours.</p> + <p>Regards,<br>The App Team</p> + </div> +</body> +</html> \ No newline at end of file diff --git a/backend/src/user/dto/resend-email.input.ts b/backend/src/user/dto/resend-email.input.ts new file mode 100644 index 00000000..05b9b964 --- /dev/null +++ b/backend/src/user/dto/resend-email.input.ts @@ -0,0 +1,11 @@ +// src/auth/dto/resend-email.input.ts +import { InputType, Field } from '@nestjs/graphql'; +import { IsEmail, IsNotEmpty } from 'class-validator'; + +@InputType() +export class ResendEmailInput { + @Field() + @IsEmail() + @IsNotEmpty() + email: string; +} diff --git a/backend/src/user/user.model.ts b/backend/src/user/user.model.ts index ba46b489..5cd35dfd 100644 --- a/backend/src/user/user.model.ts +++ b/backend/src/user/user.model.ts @@ -11,6 +11,7 @@ import { ManyToMany, JoinTable, OneToMany, + UpdateDateColumn, } from 'typeorm'; @Entity() @@ -32,6 +33,14 @@ export class User extends SystemBaseModel { @IsEmail() email: string; + @Field(() => Boolean) + @Column({ default: false }) + isEmailConfirmed: boolean; + + @Field() + @UpdateDateColumn({ type: 'datetime' }) + lastEmailSendTime: Date; + @Field(() => [Chat]) @OneToMany(() => Chat, (chat) => chat.user, { cascade: true, diff --git a/backend/src/user/user.module.ts b/backend/src/user/user.module.ts index c443575c..9f01e50e 100644 --- a/backend/src/user/user.module.ts +++ b/backend/src/user/user.module.ts @@ -6,8 +6,15 @@ import { User } from './user.model'; import { TypeOrmModule } from '@nestjs/typeorm'; import { JwtModule } from '@nestjs/jwt'; import { AuthModule } from 'src/auth/auth.module'; +import { MailModule } from 'src/mail/mail.module'; + @Module({ - imports: [TypeOrmModule.forFeature([User]), JwtModule, AuthModule], + imports: [ + TypeOrmModule.forFeature([User]), + JwtModule, + AuthModule, + MailModule, + ], providers: [UserResolver, UserService, DateScalar], exports: [UserService], }) diff --git a/backend/src/user/user.resolver.ts b/backend/src/user/user.resolver.ts index fe6cd5d6..37617be9 100644 --- a/backend/src/user/user.resolver.ts +++ b/backend/src/user/user.resolver.ts @@ -16,6 +16,8 @@ import { GetUserIdFromToken, } from 'src/decorator/get-auth-token.decorator'; import { Logger } from '@nestjs/common'; +import { EmailConfirmationResponse } from 'src/auth/auth.resolver'; +import { ResendEmailInput } from './dto/resend-email.input'; @ObjectType() class LoginResponse { @@ -33,6 +35,20 @@ export class UserResolver { private readonly authService: AuthService, ) {} + // @Mutation(() => EmailConfirmationResponse) + // async resendConfirmationEmail( + // @Args('input') resendInput: ResendConfirmationInput, + // ): Promise<EmailConfirmationResponse> { + // return this.authService.resendVerificationEmail(resendInput.email); + // } + + @Mutation(() => EmailConfirmationResponse) + async resendConfirmationEmail( + @Args('input') input: ResendEmailInput, + ): Promise<EmailConfirmationResponse> { + return this.authService.resendVerificationEmail(input.email); + } + @Mutation(() => User) async registerUser( @Args('input') registerUserInput: RegisterUserInput, diff --git a/frontend/src/app/auth/confirm/page.tsx b/frontend/src/app/auth/confirm/page.tsx new file mode 100644 index 00000000..e8c3258d --- /dev/null +++ b/frontend/src/app/auth/confirm/page.tsx @@ -0,0 +1,102 @@ +'use client'; + +import { useEffect, useState } from 'react'; +import { useMutation } from '@apollo/client'; +import { CONFIRM_EMAIL_MUTATION } from '@/graphql/mutations/auth'; +import { useRouter, useSearchParams } from 'next/navigation'; +import { BackgroundGradient } from '@/components/ui/background-gradient'; +import { + TextureCardHeader, + TextureCardTitle, + TextureCardContent, +} from '@/components/ui/texture-card'; +import { AlertCircle, CheckCircle } from 'lucide-react'; +import { Button } from '@/components/ui/button'; + +export default function ConfirmEmailPage() { + const searchParams = useSearchParams(); + const router = useRouter(); + const [status, setStatus] = useState<'loading' | 'success' | 'error'>( + 'loading' + ); + const [message, setMessage] = useState('Verifying your email...'); + + const [confirmEmail] = useMutation(CONFIRM_EMAIL_MUTATION, { + onCompleted: (data) => { + if (data.confirmEmail.success) { + setStatus('success'); + setMessage(data.confirmEmail.message || 'Email verified successfully!'); + // Redirect to home page after a short delay + setTimeout(() => { + router.push('/'); + }, 3000); + } else { + setStatus('error'); + setMessage(data.confirmEmail.message || 'Failed to verify email.'); + } + }, + onError: (error) => { + setStatus('error'); + setMessage( + error.message || 'An error occurred while verifying your email.' + ); + }, + }); + + useEffect(() => { + const token = searchParams.get('token'); + + if (!token) { + setStatus('error'); + setMessage('Invalid verification link. No token provided.'); + return; + } + + // Call the mutation to confirm the email + confirmEmail({ + variables: { + token, + }, + }); + }, [confirmEmail, searchParams]); + + return ( + <div className="flex items-center justify-center min-h-screen p-4"> + <BackgroundGradient className="rounded-[22px] p-4 bg-white dark:bg-zinc-900 max-w-md w-full"> + <TextureCardHeader className="flex flex-col gap-2 items-center justify-center p-4"> + <TextureCardTitle className="text-center text-2xl"> + Email Verification + </TextureCardTitle> + + <div className="mt-4 flex items-center justify-center"> + {status === 'loading' && ( + <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary-500" /> + )} + {status === 'success' && ( + <CheckCircle className="h-16 w-16 text-green-500" /> + )} + {status === 'error' && ( + <AlertCircle className="h-16 w-16 text-red-500" /> + )} + </div> + </TextureCardHeader> + + <TextureCardContent className="text-center"> + <p className="mb-6">{message}</p> + + {status === 'success' && ( + <p className="text-sm text-neutral-600 dark:text-neutral-400"> + You will be redirected to the home page shortly... + </p> + )} + + {status === 'error' && ( + <Button onClick={() => router.push('/')} className="mt-4"> + Go to Home Page + </Button> + )} + </TextureCardContent> + </BackgroundGradient> + </div> + ); +} diff --git a/frontend/src/components/sign-up-modal.tsx b/frontend/src/components/sign-up-modal.tsx index 1b0833f6..c7537fff 100644 --- a/frontend/src/components/sign-up-modal.tsx +++ b/frontend/src/components/sign-up-modal.tsx @@ -18,10 +18,14 @@ import { TextureSeparator, } from '@/components/ui/texture-card'; import { useMutation } from '@apollo/client'; -import { REGISTER_USER } from '@/graphql/mutations/auth'; +import { + REGISTER_USER, + RESEND_CONFIRMATION_EMAIL_MUTATION, +} from '@/graphql/mutations/auth'; import { useRouter } from 'next/navigation'; import { VisuallyHidden } from '@radix-ui/react-visually-hidden'; -import { AlertCircle } from 'lucide-react'; +import { AlertCircle, CheckCircle, Mail, Clock } from 'lucide-react'; +import { useEffect } from 'react'; export function SignUpModal({ isOpen, @@ -35,6 +39,9 @@ export function SignUpModal({ const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const [errorMessage, setErrorMessage] = useState<string | null>(null); + const [registrationSuccess, setRegistrationSuccess] = useState(false); + const [resendCooldown, setResendCooldown] = useState(0); + const [resendMessage, setResendMessage] = useState<string | null>(null); const [registerUser, { loading }] = useMutation(REGISTER_USER, { onError: (error) => { @@ -45,7 +52,7 @@ export function SignUpModal({ } }, onCompleted: () => { - onClose(); + setRegistrationSuccess(true); }, }); @@ -73,6 +80,56 @@ export function SignUpModal({ } }; + const [resendConfirmationEmail, { loading: resendLoading }] = useMutation( + RESEND_CONFIRMATION_EMAIL_MUTATION, + { + onCompleted: (data) => { + if (data.resendConfirmationEmail.success) { + setResendMessage( + 'Verification email has been resent. Please check your inbox.' + ); + setResendCooldown(60); // Start 60 second cooldown + } else { + setResendMessage( + data.resendConfirmationEmail.message || + 'Failed to resend. Please try again later.' + ); + } + }, + onError: (error) => { + setResendMessage(`Error: ${error.message}`); + }, + } + ); + + useEffect(() => { + let timer: NodeJS.Timeout; + if (resendCooldown > 0) { + timer = setTimeout(() => { + setResendCooldown(resendCooldown - 1); + }, 1000); + } else if (resendCooldown === 0) { + // Clear the resend message once cooldown is complete + if (resendMessage && resendMessage.includes('has been resent')) { + setResendMessage(null); + } + } + return () => clearTimeout(timer); + }, [resendCooldown, resendMessage]); + + const handleResendConfirmation = () => { + if (resendCooldown > 0) return; + + setResendMessage(null); + resendConfirmationEmail({ + variables: { + input: { + email, + }, + }, + }); + }; + return ( <Dialog open={isOpen} onOpenChange={onClose}> <DialogContent className="sm:max-w-[425px] fixed top-[50%] left-[50%] transform -translate-x-[50%] -translate-y-[50%] p-0"> @@ -85,73 +142,144 @@ export function SignUpModal({ <BackgroundGradient className="rounded-[22px] p-4 bg-white dark:bg-zinc-900"> <div className="w-full"> - <TextureCardHeader className="flex flex-col gap-1 items-center justify-center p-4"> - <TextureCardTitle>Create your account</TextureCardTitle> - <p className="text-center text-neutral-600 dark:text-neutral-400"> - Welcome! Please fill in the details to get started. - </p> - </TextureCardHeader> - <TextureSeparator /> - <TextureCardContent> - <form onSubmit={handleSubmit} className="space-y-2"> - <div className="space-y-1"> - <Label htmlFor="name">Name</Label> - <Input - id="name" - placeholder="Name" - type="text" - value={name} - onChange={(e) => { - setName(e.target.value); - setErrorMessage(null); - }} - required - className="w-full" - /> - </div> - <div className="space-y-1"> - <Label htmlFor="email">Email</Label> - <Input - id="email" - placeholder="Email" - type="email" - value={email} - onChange={(e) => { - setEmail(e.target.value); - setErrorMessage(null); - }} - required - className="w-full" - /> - </div> - <div className="space-y-1"> - <Label htmlFor="password">Password</Label> - <Input - id="password" - placeholder="Password" - type="password" - value={password} - onChange={(e) => { - setPassword(e.target.value); - setErrorMessage(null); - }} - required - className="w-full" - /> - </div> - - {errorMessage && ( - <div className="flex items-center gap-2 text-primary-700 dark:text-primary-400 text-sm p-2 rounded-md bg-primary-50 dark:bg-zinc-800 border border-primary-200 dark:border-primary-800"> - <AlertCircle className="h-4 w-4" /> - <span>{errorMessage}</span> + {registrationSuccess ? ( + <> + <TextureCardHeader className="flex flex-col gap-1 items-center justify-center p-4"> + <CheckCircle className="h-12 w-12 text-green-500 mb-2" /> + <TextureCardTitle>Verification Email Sent</TextureCardTitle> + <p className="text-center text-neutral-600 dark:text-neutral-400"> + Please check your email to complete registration. We have + sent a verification link to{' '} + <span className="font-medium">{email}</span>. + </p> + </TextureCardHeader> + <TextureSeparator /> + <TextureCardContent className="space-y-4"> + <div className="flex flex-col gap-2 p-4 rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-100 dark:border-blue-800"> + <div className="flex items-center gap-2"> + <Mail className="h-5 w-5 text-blue-500" /> + <span className="font-medium text-blue-700 dark:text-blue-300"> + Email Verification Required + </span> + </div> + <p className="text-sm text-blue-600 dark:text-blue-400"> + To complete your registration, please click the + verification link sent to your email address. + </p> </div> - )} - <Button type="submit" className="w-full" disabled={loading}> - {loading ? 'Signing up...' : 'Sign up'} - </Button> - </form> - </TextureCardContent> + {resendMessage && ( + <div + className={`flex items-center gap-2 text-sm p-2 rounded-md ${ + resendMessage.includes('has been resent') + ? 'bg-green-50 dark:bg-green-900/20 border border-green-100 dark:border-green-800 text-green-700 dark:text-green-400' + : 'bg-primary-50 dark:bg-zinc-800 border border-primary-200 dark:border-primary-800 text-primary-700 dark:text-primary-400' + }`} + > + {resendMessage.includes('has been resent') ? ( + <CheckCircle className="h-4 w-4" /> + ) : ( + <AlertCircle className="h-4 w-4" /> + )} + <span>{resendMessage}</span> + </div> + )} + + <div className="flex flex-col gap-2"> + <Button onClick={onClose} className="w-full"> + Got it + </Button> + <Button + onClick={handleResendConfirmation} + variant="outline" + className="w-full" + disabled={resendCooldown > 0 || resendLoading} + > + {resendLoading ? ( + 'Sending...' + ) : resendCooldown > 0 ? ( + <span className="flex items-center gap-2"> + <Clock className="h-4 w-4" /> + Resend available in {resendCooldown}s + </span> + ) : ( + 'Resend verification email' + )} + </Button> + </div> + </TextureCardContent> + </> + ) : ( + <> + <TextureCardHeader className="flex flex-col gap-1 items-center justify-center p-4"> + <TextureCardTitle>Create your account</TextureCardTitle> + <p className="text-center text-neutral-600 dark:text-neutral-400"> + Welcome! Please fill in the details to get started. + </p> + </TextureCardHeader> + <TextureSeparator /> + <TextureCardContent> + <form onSubmit={handleSubmit} className="space-y-2"> + <div className="space-y-1"> + <Label htmlFor="name">Name</Label> + <Input + id="name" + placeholder="Name" + type="text" + value={name} + onChange={(e) => { + setName(e.target.value); + setErrorMessage(null); + }} + required + className="w-full" + /> + </div> + <div className="space-y-1"> + <Label htmlFor="email">Email</Label> + <Input + id="email" + placeholder="Email" + type="email" + value={email} + onChange={(e) => { + setEmail(e.target.value); + setErrorMessage(null); + }} + required + className="w-full" + /> + </div> + <div className="space-y-1"> + <Label htmlFor="password">Password</Label> + <Input + id="password" + placeholder="Password" + type="password" + value={password} + onChange={(e) => { + setPassword(e.target.value); + setErrorMessage(null); + }} + required + className="w-full" + /> + </div> + + {errorMessage && ( + <div className="flex items-center gap-2 text-primary-700 dark:text-primary-400 text-sm p-2 rounded-md bg-primary-50 dark:bg-zinc-800 border border-primary-200 dark:border-primary-800"> + <AlertCircle className="h-4 w-4" /> + <span>{errorMessage}</span> + </div> + )} + + <Button type="submit" className="w-full" disabled={loading}> + {loading ? 'Signing up...' : 'Sign up'} + </Button> + </form> + </TextureCardContent> + </> + )} </div> </BackgroundGradient> </DialogContent> diff --git a/frontend/src/graphql/mutations/auth.ts b/frontend/src/graphql/mutations/auth.ts index 9c00ec93..694b26ab 100644 --- a/frontend/src/graphql/mutations/auth.ts +++ b/frontend/src/graphql/mutations/auth.ts @@ -18,6 +18,7 @@ export const LOGIN_USER = gql` } } `; + export const REFRESH_TOKEN_MUTATION = gql` mutation RefreshToken($refreshToken: String!) { refreshToken(refreshToken: $refreshToken) { @@ -26,3 +27,21 @@ export const REFRESH_TOKEN_MUTATION = gql` } } `; + +export const CONFIRM_EMAIL_MUTATION = gql` + mutation ConfirmEmail($token: String!) { + confirmEmail(token: $token) { + message + success + } + } +`; + +export const RESEND_CONFIRMATION_EMAIL_MUTATION = gql` + mutation ResendConfirmationEmail($input: ResendEmailInput!) { + resendConfirmationEmail(input: $input) { + message + success + } + } +`; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 814f243e..2b20fb9e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,6 +45,9 @@ importers: '@huggingface/transformers': specifier: latest version: 3.3.3 + '@nestjs-modules/mailer': + specifier: ^2.0.2 + version: 2.0.2(@nestjs/common@10.4.15)(@nestjs/core@10.4.15)(nodemailer@6.10.0) '@nestjs/apollo': specifier: ^12.2.0 version: 12.2.2(@apollo/server@4.11.3)(@nestjs/common@10.4.15)(@nestjs/core@10.4.15)(@nestjs/graphql@12.2.2)(graphql@16.10.0) @@ -129,6 +132,9 @@ importers: markdown-to-txt: specifier: ^2.0.1 version: 2.0.1 + nodemailer: + specifier: ^6.10.0 + version: 6.10.0 normalize-path: specifier: ^3.0.0 version: 3.0.0 @@ -3322,6 +3328,112 @@ packages: dependencies: '@jridgewell/trace-mapping': 0.3.9 + /@css-inline/css-inline-android-arm-eabi@0.14.1: + resolution: {integrity: sha512-LNUR8TY4ldfYi0mi/d4UNuHJ+3o8yLQH9r2Nt6i4qeg1i7xswfL3n/LDLRXvGjBYqeEYNlhlBQzbPwMX1qrU6A==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@css-inline/css-inline-android-arm64@0.14.1: + resolution: {integrity: sha512-tH5us0NYGoTNBHOUHVV7j9KfJ4DtFOeTLA3cM0XNoMtArNu2pmaaBMFJPqECzavfXkLc7x5Z22UPZYjoyHfvCA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: false + optional: true + + /@css-inline/css-inline-darwin-arm64@0.14.1: + resolution: {integrity: sha512-QE5W1YRIfRayFrtrcK/wqEaxNaqLULPI0gZB4ArbFRd3d56IycvgBasDTHPre5qL2cXCO3VyPx+80XyHOaVkag==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@css-inline/css-inline-darwin-x64@0.14.1: + resolution: {integrity: sha512-mAvv2sN8awNFsbvBzlFkZPbCNZ6GCWY5/YcIz7V5dPYw+bHHRbjnlkNTEZq5BsDxErVrMIGvz05PGgzuNvZvdQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + + /@css-inline/css-inline-linux-arm-gnueabihf@0.14.1: + resolution: {integrity: sha512-AWC44xL0X7BgKvrWEqfSqkT2tJA5kwSGrAGT+m0gt11wnTYySvQ6YpX0fTY9i3ppYGu4bEdXFjyK2uY1DTQMHA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@css-inline/css-inline-linux-arm64-gnu@0.14.1: + resolution: {integrity: sha512-drj0ciiJgdP3xKXvNAt4W+FH4KKMs8vB5iKLJ3HcH07sNZj58Sx++2GxFRS1el3p+GFp9OoYA6dgouJsGEqt0Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@css-inline/css-inline-linux-arm64-musl@0.14.1: + resolution: {integrity: sha512-FzknI+st8eA8YQSdEJU9ykcM0LZjjigBuynVF5/p7hiMm9OMP8aNhWbhZ8LKJpKbZrQsxSGS4g9Vnr6n6FiSdQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@css-inline/css-inline-linux-x64-gnu@0.14.1: + resolution: {integrity: sha512-yubbEye+daDY/4vXnyASAxH88s256pPati1DfVoZpU1V0+KP0BZ1dByZOU1ktExurbPH3gZOWisAnBE9xon0Uw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@css-inline/css-inline-linux-x64-musl@0.14.1: + resolution: {integrity: sha512-6CRAZzoy1dMLPC/tns2rTt1ZwPo0nL/jYBEIAsYTCWhfAnNnpoLKVh5Nm+fSU3OOwTTqU87UkGrFJhObD/wobQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + + /@css-inline/css-inline-win32-x64-msvc@0.14.1: + resolution: {integrity: sha512-nzotGiaiuiQW78EzsiwsHZXbxEt6DiMUFcDJ6dhiliomXxnlaPyBfZb6/FMBgRJOf6sknDt/5695OttNmbMYzg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + + /@css-inline/css-inline@0.14.1: + resolution: {integrity: sha512-u4eku+hnPqqHIGq/ZUQcaP0TrCbYeLIYBaK7qClNRGZbnh8RC4gVxLEIo8Pceo1nOK9E5G4Lxzlw5KnXcvflfA==} + engines: {node: '>= 10'} + optionalDependencies: + '@css-inline/css-inline-android-arm-eabi': 0.14.1 + '@css-inline/css-inline-android-arm64': 0.14.1 + '@css-inline/css-inline-darwin-arm64': 0.14.1 + '@css-inline/css-inline-darwin-x64': 0.14.1 + '@css-inline/css-inline-linux-arm-gnueabihf': 0.14.1 + '@css-inline/css-inline-linux-arm64-gnu': 0.14.1 + '@css-inline/css-inline-linux-arm64-musl': 0.14.1 + '@css-inline/css-inline-linux-x64-gnu': 0.14.1 + '@css-inline/css-inline-linux-x64-musl': 0.14.1 + '@css-inline/css-inline-win32-x64-msvc': 0.14.1 + dev: false + /@csstools/cascade-layer-name-parser@2.0.4(@csstools/css-parser-algorithms@3.0.4)(@csstools/css-tokenizer@3.0.3): resolution: {integrity: sha512-7DFHlPuIxviKYZrOiwVU/PiHLm3lLUR23OMuEEtfEOQTOp9hzQ2JjdY6X5H18RVuUPJqSCI+qNnD5iOLMVE0bA==} engines: {node: '>=18'} @@ -6976,6 +7088,32 @@ packages: react-dom: 18.3.1(react@18.3.1) dev: false + /@nestjs-modules/mailer@2.0.2(@nestjs/common@10.4.15)(@nestjs/core@10.4.15)(nodemailer@6.10.0): + resolution: {integrity: sha512-+z4mADQasg0H1ZaGu4zZTuKv2pu+XdErqx99PLFPzCDNTN/q9U59WPgkxVaHnsvKHNopLj5Xap7G4ZpptduoYw==} + peerDependencies: + '@nestjs/common': '>=7.0.9' + '@nestjs/core': '>=7.0.9' + nodemailer: '>=6.4.6' + dependencies: + '@css-inline/css-inline': 0.14.1 + '@nestjs/common': 10.4.15(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.2) + '@nestjs/core': 10.4.15(@nestjs/common@10.4.15)(@nestjs/platform-express@10.4.15)(reflect-metadata@0.2.2)(rxjs@7.8.2) + glob: 10.3.12 + nodemailer: 6.10.0 + optionalDependencies: + '@types/ejs': 3.1.5 + '@types/mjml': 4.7.4 + '@types/pug': 2.0.10 + ejs: 3.1.10 + handlebars: 4.7.8 + liquidjs: 10.21.0 + mjml: 4.15.3 + preview-email: 3.1.0 + pug: 3.0.3 + transitivePeerDependencies: + - encoding + dev: false + /@nestjs/apollo@12.2.2(@apollo/server@4.11.3)(@nestjs/common@10.4.15)(@nestjs/core@10.4.15)(@nestjs/graphql@12.2.2)(graphql@16.10.0): resolution: {integrity: sha512-gsDqSfsmTSvF0k3XaRESRgM3uE/YFO+59txCsq7T1EadDOVOuoF3zVQiFmi6D50Rlnqohqs63qjjf46mgiiXgQ==} peerDependencies: @@ -7751,6 +7889,12 @@ packages: '@octokit/webhooks-methods': 5.1.1 dev: false + /@one-ini/wasm@0.1.1: + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + requiresBuild: true + dev: false + optional: true + /@parcel/watcher-android-arm64@2.5.1: resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} engines: {node: '>= 10.0.0'} @@ -8822,6 +8966,15 @@ packages: resolution: {integrity: sha512-kkKUDVlII2DQiKy7UstOR1ErJP8kUKAQ4oa+SQtM0K+lPdmmjj0YnnxBgtTVYH7mUKtbsxeFC9y0AmK7Yb78/A==} dev: true + /@selderee/plugin-htmlparser2@0.11.0: + resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} + requiresBuild: true + dependencies: + domhandler: 5.0.3 + selderee: 0.11.0 + dev: false + optional: true + /@sideway/address@4.1.5: resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} dependencies: @@ -9740,6 +9893,11 @@ packages: resolution: {integrity: sha512-zf2GwV/G6TdaLwpLDcGTIkHnXf8JEf/viMux+khqKQKDa8/8BAUtXXZS563GnvJ4Fg0PBLGAaFf2GekEVSZ6GQ==} dev: false + /@types/ejs@3.1.5: + resolution: {integrity: sha512-nv+GSx77ZtXiJzwKdsASqi+YQ5Z7vwHsTP0JY2SiQgjGckkBRKZnk8nIM+7oUZ1VCtuTz0+By4qVR7fqzp/Dfg==} + dev: false + optional: true + /@types/eslint-scope@3.7.7: resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} dependencies: @@ -9917,6 +10075,19 @@ packages: /@types/mime@1.3.5: resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} + /@types/mjml-core@4.15.1: + resolution: {integrity: sha512-qu8dUksU8yXX18qMTFINkM4uoz7WQYC5F14lcWeSNmWbulaGG0KG19yeZwpx75b9RJXr8WI/FRHH0LyQTU9JbA==} + requiresBuild: true + dev: false + optional: true + + /@types/mjml@4.7.4: + resolution: {integrity: sha512-vyi1vzWgMzFMwZY7GSZYX0GU0dmtC8vLHwpgk+NWmwbwRSrlieVyJ9sn5elodwUfklJM7yGl0zQeet1brKTWaQ==} + dependencies: + '@types/mjml-core': 4.15.1 + dev: false + optional: true + /@types/ms@2.1.0: resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} @@ -9973,6 +10144,11 @@ packages: /@types/prop-types@15.7.14: resolution: {integrity: sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==} + /@types/pug@2.0.10: + resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==} + dev: false + optional: true + /@types/qs@6.9.18: resolution: {integrity: sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==} @@ -10653,6 +10829,13 @@ packages: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} dev: false + /abbrev@2.0.0: + resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + requiresBuild: true + dev: false + optional: true + /abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -10686,6 +10869,14 @@ packages: dependencies: acorn: 8.14.0 + /acorn@7.4.1: + resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==} + engines: {node: '>=0.4.0'} + hasBin: true + requiresBuild: true + dev: false + optional: true + /acorn@8.14.0: resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} engines: {node: '>=0.4.0'} @@ -10783,6 +10974,16 @@ packages: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + /alce@1.2.0: + resolution: {integrity: sha512-XppPf2S42nO2WhvKzlwzlfcApcXHzjlod30pKmcWjRgLOtqoe5DMuqdiYoM6AgyXksc6A6pV4v1L/WW217e57w==} + engines: {node: '>=0.8.0'} + requiresBuild: true + dependencies: + esprima: 1.2.5 + estraverse: 1.9.3 + dev: false + optional: true + /algoliasearch-helper@3.24.2(algoliasearch@4.24.0): resolution: {integrity: sha512-vBw/INZDfyh/THbVeDy8On8lZqd2qiUAHde5N4N1ygL4SoeLqLGJ4GHneHrDAYsjikRwTTtodEP0fiXl5MxHFQ==} peerDependencies: @@ -10840,7 +11041,6 @@ packages: /ansi-colors@4.1.3: resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} engines: {node: '>=6'} - dev: true /ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} @@ -11071,7 +11271,12 @@ packages: /asap@2.0.6: resolution: {integrity: sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==} - dev: true + + /assert-never@1.4.0: + resolution: {integrity: sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA==} + requiresBuild: true + dev: false + optional: true /ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} @@ -11099,7 +11304,6 @@ packages: /async@3.2.6: resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - dev: true /asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -11354,6 +11558,15 @@ packages: babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.9) dev: true + /babel-walk@3.0.0-canary-5: + resolution: {integrity: sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==} + engines: {node: '>= 10.0.0'} + requiresBuild: true + dependencies: + '@babel/types': 7.26.9 + dev: false + optional: true + /backo2@1.0.2: resolution: {integrity: sha512-zj6Z6M7Eq+PBZ7PQxl5NT665MvJdAkzp0f60nAJ+sLaSCBPMwVak5ZegFbgVCzFcCJTKFoMizvM5Ld7+JrRJHA==} dev: false @@ -11622,6 +11835,15 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} + /camel-case@3.0.0: + resolution: {integrity: sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==} + requiresBuild: true + dependencies: + no-case: 2.3.2 + upper-case: 1.1.3 + dev: false + optional: true + /camel-case@4.1.2: resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} dependencies: @@ -11679,7 +11901,6 @@ packages: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - dev: true /chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -11760,6 +11981,14 @@ packages: /character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + /character-parser@2.2.0: + resolution: {integrity: sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==} + requiresBuild: true + dependencies: + is-regex: 1.2.1 + dev: false + optional: true + /character-reference-invalid@1.1.4: resolution: {integrity: sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==} dev: false @@ -11867,6 +12096,15 @@ packages: clsx: 2.1.1 dev: false + /clean-css@4.2.4: + resolution: {integrity: sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==} + engines: {node: '>= 4.0'} + requiresBuild: true + dependencies: + source-map: 0.6.1 + dev: false + optional: true + /clean-css@5.3.3: resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} engines: {node: '>= 10.0'} @@ -12075,7 +12313,6 @@ packages: /commander@6.2.1: resolution: {integrity: sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==} engines: {node: '>= 6'} - dev: true /commander@7.2.0: resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} @@ -12194,6 +12431,15 @@ packages: upper-case: 2.0.2 dev: true + /constantinople@4.0.1: + resolution: {integrity: sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==} + requiresBuild: true + dependencies: + '@babel/parser': 7.26.9 + '@babel/types': 7.26.9 + dev: false + optional: true + /content-disposition@0.5.2: resolution: {integrity: sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==} engines: {node: '>= 0.6'} @@ -12404,6 +12650,19 @@ packages: dependencies: tslib: 2.8.1 + /cross-spawn@6.0.6: + resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==} + engines: {node: '>=4.8'} + requiresBuild: true + dependencies: + nice-try: 1.0.5 + path-key: 2.0.1 + semver: 5.7.2 + shebang-command: 1.2.0 + which: 1.3.1 + dev: false + optional: true + /cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -12907,7 +13166,6 @@ packages: /detect-indent@6.1.0: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} - dev: true /detect-libc@1.0.3: resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} @@ -12923,7 +13181,6 @@ packages: /detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} - dev: true /detect-node-es@1.1.0: resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} @@ -12985,6 +13242,16 @@ packages: dependencies: path-type: 4.0.0 + /display-notification@2.0.0: + resolution: {integrity: sha512-TdmtlAcdqy1NU+j7zlkDdMnCL878zriLaBmoD9quOoq1ySSSGv03l0hXK5CvIFZlIfFI/hizqdQuW+Num7xuhw==} + engines: {node: '>=4'} + requiresBuild: true + dependencies: + escape-string-applescript: 1.0.0 + run-applescript: 3.2.0 + dev: false + optional: true + /dlv@1.1.3: resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} @@ -13008,6 +13275,12 @@ packages: dependencies: esutils: 2.0.3 + /doctypes@1.1.0: + resolution: {integrity: sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==} + requiresBuild: true + dev: false + optional: true + /dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} dev: true @@ -13050,6 +13323,15 @@ packages: webidl-conversions: 7.0.0 dev: true + /domhandler@3.3.0: + resolution: {integrity: sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==} + engines: {node: '>= 4'} + requiresBuild: true + dependencies: + domelementtype: 2.3.0 + dev: false + optional: true + /domhandler@4.3.1: resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} engines: {node: '>= 4'} @@ -13132,6 +13414,19 @@ packages: safe-buffer: 5.2.1 dev: false + /editorconfig@1.0.4: + resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} + engines: {node: '>=14'} + hasBin: true + requiresBuild: true + dependencies: + '@one-ini/wasm': 0.1.1 + commander: 10.0.1 + minimatch: 9.0.1 + semver: 7.7.1 + dev: false + optional: true + /ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} @@ -13141,7 +13436,6 @@ packages: hasBin: true dependencies: jake: 10.9.2 - dev: true /electron-to-chromium@1.5.112: resolution: {integrity: sha512-oen93kVyqSb3l+ziUgzIOlWt/oOuy4zRmpwestMn4rhFWAoFJeFuCVte9F2fASjeZZo7l/Cif9TiyrdW4CwEMA==} @@ -13186,6 +13480,13 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} + /encoding-japanese@2.2.0: + resolution: {integrity: sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A==} + engines: {node: '>=8.10.0'} + requiresBuild: true + dev: false + optional: true + /encoding@0.1.13: resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} requiresBuild: true @@ -13417,6 +13718,13 @@ packages: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} + /escape-goat@3.0.0: + resolution: {integrity: sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==} + engines: {node: '>=10'} + requiresBuild: true + dev: false + optional: true + /escape-goat@4.0.0: resolution: {integrity: sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg==} engines: {node: '>=12'} @@ -13425,6 +13733,13 @@ packages: /escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + /escape-string-applescript@1.0.0: + resolution: {integrity: sha512-4/hFwoYaC6TkpDn9A3pTC52zQPArFeXuIfhUtCGYdauTzXVP9H3BDr3oO/QzQehMpLDC7srvYgfwvImPFGfvBA==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: false + optional: true + /escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} @@ -13774,6 +14089,14 @@ packages: acorn-jsx: 5.3.2(acorn@8.14.0) eslint-visitor-keys: 3.4.3 + /esprima@1.2.5: + resolution: {integrity: sha512-S9VbPDU0adFErpDai3qDkjq8+G05ONtKzcyNrPKg/ZKa+tf879nX2KexNU95b31UoTJjRLInNBHHHjFPoCd7lQ==} + engines: {node: '>=0.4.0'} + hasBin: true + requiresBuild: true + dev: false + optional: true + /esprima@4.0.1: resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} engines: {node: '>=4'} @@ -13791,6 +14114,13 @@ packages: dependencies: estraverse: 5.3.0 + /estraverse@1.9.3: + resolution: {integrity: sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: false + optional: true + /estraverse@4.3.0: resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} engines: {node: '>=4.0'} @@ -13886,6 +14216,21 @@ packages: resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} engines: {node: '>=0.8.x'} + /execa@0.10.0: + resolution: {integrity: sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw==} + engines: {node: '>=4'} + requiresBuild: true + dependencies: + cross-spawn: 6.0.6 + get-stream: 3.0.0 + is-stream: 1.1.0 + npm-run-path: 2.0.2 + p-finally: 1.0.0 + signal-exit: 3.0.7 + strip-eof: 1.0.0 + dev: false + optional: true + /execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} @@ -13963,6 +14308,12 @@ packages: transitivePeerDependencies: - supports-color + /extend-object@1.0.0: + resolution: {integrity: sha512-0dHDIXC7y7LDmCh/lp1oYkmv73K25AMugQI07r8eFopkW6f7Ufn1q+ETMsJjnV9Am14SlElkqy3O92r6xEaxPw==} + requiresBuild: true + dev: false + optional: true + /extend-shallow@2.0.1: resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} engines: {node: '>=0.10.0'} @@ -14178,7 +14529,6 @@ packages: resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} dependencies: minimatch: 5.1.6 - dev: true /filename-reserved-regex@3.0.0: resolution: {integrity: sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==} @@ -14254,6 +14604,20 @@ packages: path-exists: 5.0.0 dev: false + /fixpack@4.0.0: + resolution: {integrity: sha512-5SM1+H2CcuJ3gGEwTiVo/+nd/hYpNj9Ch3iMDOQ58ndY+VGQ2QdvaUTkd3otjZvYnd/8LF/HkJ5cx7PBq0orCQ==} + hasBin: true + requiresBuild: true + dependencies: + alce: 1.2.0 + chalk: 3.0.0 + detect-indent: 6.1.0 + detect-newline: 3.1.0 + extend-object: 1.0.0 + rc: 1.2.8 + dev: false + optional: true + /flat-cache@3.2.0: resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==} engines: {node: ^10.12.0 || >=12.0.0} @@ -14610,6 +14974,13 @@ packages: engines: {node: '>=8.0.0'} dev: true + /get-port@5.1.1: + resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} + engines: {node: '>=8'} + requiresBuild: true + dev: false + optional: true + /get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} @@ -14617,7 +14988,14 @@ packages: dunder-proto: 1.0.1 es-object-atoms: 1.1.1 - /get-stream@6.0.1: + /get-stream@3.0.0: + resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==} + engines: {node: '>=4'} + requiresBuild: true + dev: false + optional: true + + /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} @@ -14671,6 +15049,18 @@ packages: path-scurry: 1.11.1 dev: true + /glob@10.3.12: + resolution: {integrity: sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + dependencies: + foreground-child: 3.3.1 + jackspeak: 2.3.6 + minimatch: 9.0.5 + minipass: 7.1.2 + path-scurry: 1.11.1 + dev: false + /glob@10.4.5: resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} hasBin: true @@ -14934,6 +15324,20 @@ packages: resolution: {integrity: sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==} dev: false + /handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + dev: false + optional: true + /has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} @@ -15203,11 +15607,40 @@ packages: terser: 5.39.0 dev: false + /html-minifier@4.0.0: + resolution: {integrity: sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==} + engines: {node: '>=6'} + hasBin: true + requiresBuild: true + dependencies: + camel-case: 3.0.0 + clean-css: 4.2.4 + commander: 2.20.3 + he: 1.2.0 + param-case: 2.1.1 + relateurl: 0.2.7 + uglify-js: 3.19.3 + dev: false + optional: true + /html-tags@3.3.1: resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} engines: {node: '>=8'} dev: false + /html-to-text@9.0.5: + resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==} + engines: {node: '>=14'} + requiresBuild: true + dependencies: + '@selderee/plugin-htmlparser2': 0.11.0 + deepmerge: 4.3.1 + dom-serializer: 2.0.0 + htmlparser2: 8.0.2 + selderee: 0.11.0 + dev: false + optional: true + /html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} dev: false @@ -15236,6 +15669,17 @@ packages: webpack: 5.98.0(webpack-cli@5.1.4) dev: false + /htmlparser2@5.0.1: + resolution: {integrity: sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==} + requiresBuild: true + dependencies: + domelementtype: 2.3.0 + domhandler: 3.3.0 + domutils: 2.8.0 + entities: 2.2.0 + dev: false + optional: true + /htmlparser2@6.1.0: resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} dependencies: @@ -15254,6 +15698,17 @@ packages: entities: 4.5.0 dev: false + /htmlparser2@9.1.0: + resolution: {integrity: sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==} + requiresBuild: true + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.2.2 + entities: 4.5.0 + dev: false + optional: true + /http-cache-semantics@4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} dev: false @@ -15775,6 +16230,15 @@ packages: hasBin: true dev: false + /is-expression@4.0.0: + resolution: {integrity: sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==} + requiresBuild: true + dependencies: + acorn: 7.4.1 + object-assign: 4.1.1 + dev: false + optional: true + /is-extendable@0.1.1: resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} engines: {node: '>=0.10.0'} @@ -15920,6 +16384,12 @@ packages: resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} dev: true + /is-promise@2.2.2: + resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} + requiresBuild: true + dev: false + optional: true + /is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -15928,7 +16398,6 @@ packages: gopd: 1.2.0 has-tostringtag: 1.0.2 hasown: 2.0.2 - dev: true /is-regexp@1.0.0: resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==} @@ -15959,6 +16428,13 @@ packages: call-bound: 1.0.4 dev: true + /is-stream@1.1.0: + resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: false + optional: true + /is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} @@ -16177,7 +16653,6 @@ packages: '@isaacs/cliui': 8.0.2 optionalDependencies: '@pkgjs/parseargs': 0.11.0 - dev: true /jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} @@ -16195,7 +16670,6 @@ packages: chalk: 4.1.2 filelist: 1.0.4 minimatch: 3.1.2 - dev: true /jest-changed-files@29.7.0: resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} @@ -16887,6 +17361,33 @@ packages: resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} dev: true + /js-beautify@1.15.4: + resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} + engines: {node: '>=14'} + hasBin: true + requiresBuild: true + dependencies: + config-chain: 1.1.13 + editorconfig: 1.0.4 + glob: 10.4.5 + js-cookie: 3.0.5 + nopt: 7.2.1 + dev: false + optional: true + + /js-cookie@3.0.5: + resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==} + engines: {node: '>=14'} + requiresBuild: true + dev: false + optional: true + + /js-stringify@1.0.2: + resolution: {integrity: sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==} + requiresBuild: true + dev: false + optional: true + /js-tiktoken@1.0.19: resolution: {integrity: sha512-XC63YQeEcS47Y53gg950xiZ4IWmkfMe4p2V9OSaBt26q+p47WHn18izuXzSclCI73B7yGqtfRsT6jcZQI0y08g==} dependencies: @@ -17038,6 +17539,15 @@ packages: semver: 7.7.1 dev: false + /jstransformer@1.0.0: + resolution: {integrity: sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==} + requiresBuild: true + dependencies: + is-promise: 2.2.2 + promise: 7.3.1 + dev: false + optional: true + /jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -17048,6 +17558,22 @@ packages: object.values: 1.2.1 dev: true + /juice@10.0.1: + resolution: {integrity: sha512-ZhJT1soxJCkOiO55/mz8yeBKTAJhRzX9WBO+16ZTqNTONnnVlUPyVBIzQ7lDRjaBdTbid+bAnyIon/GM3yp4cA==} + engines: {node: '>=10.0.0'} + hasBin: true + requiresBuild: true + dependencies: + cheerio: 1.0.0-rc.12 + commander: 6.2.1 + mensch: 0.3.4 + slick: 1.12.2 + web-resource-inliner: 6.0.1 + transitivePeerDependencies: + - encoding + dev: false + optional: true + /jwa@1.4.1: resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} dependencies: @@ -17197,6 +17723,12 @@ packages: shell-quote: 1.8.2 dev: false + /leac@0.6.0: + resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==} + requiresBuild: true + dev: false + optional: true + /leven@3.1.0: resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} engines: {node: '>=6'} @@ -17208,9 +17740,32 @@ packages: prelude-ls: 1.2.1 type-check: 0.4.0 + /libbase64@1.3.0: + resolution: {integrity: sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==} + requiresBuild: true + dev: false + optional: true + + /libmime@5.3.6: + resolution: {integrity: sha512-j9mBC7eiqi6fgBPAGvKCXJKJSIASanYF4EeA4iBzSG0HxQxmXnR3KbyWqTn4CwsKSebqCv2f5XZfAO6sKzgvwA==} + requiresBuild: true + dependencies: + encoding-japanese: 2.2.0 + iconv-lite: 0.6.3 + libbase64: 1.3.0 + libqp: 2.1.1 + dev: false + optional: true + /libphonenumber-js@1.12.4: resolution: {integrity: sha512-vLmhg7Gan7idyAKfc6pvCtNzvar4/eIzrVVk3hjNFH5+fGqyjD0gQRovdTrDl20wsmZhBtmZpcsR0tOfquwb8g==} + /libqp@2.1.1: + resolution: {integrity: sha512-0Wd+GPz1O134cP62YU2GTOPNA7Qgl09XwCqM5zpBv87ERCXdfDtyKXvV7c9U22yWJh44QZqBocFnXN11K96qow==} + requiresBuild: true + dev: false + optional: true + /lifecycle-utils@1.7.3: resolution: {integrity: sha512-T7zs7J6/sgsqwVyG34Sfo5LTQmlPmmqaUe3yBhdF8nq24RtR/HtbkNZRhNbr9BEaKySdSgH+P9H5U9X+p0WjXw==} dev: false @@ -17226,6 +17781,23 @@ packages: /lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + /linkify-it@5.0.0: + resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==} + requiresBuild: true + dependencies: + uc.micro: 2.1.0 + dev: false + optional: true + + /liquidjs@10.21.0: + resolution: {integrity: sha512-DouqxNU2jfoZzb1LinVjOc/f6ssitGIxiDJT+kEKyYqPSSSd+WmGOAhtWbVm1/n75svu4aQ+FyQ3ctd3wh1bbw==} + engines: {node: '>=14'} + hasBin: true + dependencies: + commander: 10.0.1 + dev: false + optional: true + /listr2@4.0.5: resolution: {integrity: sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==} engines: {node: '>=12'} @@ -17420,6 +17992,12 @@ packages: tslib: 2.6.3 dev: true + /lower-case@1.1.4: + resolution: {integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==} + requiresBuild: true + dev: false + optional: true + /lower-case@2.0.2: resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} dependencies: @@ -17479,6 +18057,33 @@ packages: '@jridgewell/sourcemap-codec': 1.5.0 dev: true + /mailparser@3.7.2: + resolution: {integrity: sha512-iI0p2TCcIodR1qGiRoDBBwboSSff50vQAWytM5JRggLfABa4hHYCf3YVujtuzV454xrOP352VsAPIzviqMTo4Q==} + requiresBuild: true + dependencies: + encoding-japanese: 2.2.0 + he: 1.2.0 + html-to-text: 9.0.5 + iconv-lite: 0.6.3 + libmime: 5.3.6 + linkify-it: 5.0.0 + mailsplit: 5.4.2 + nodemailer: 6.9.16 + punycode.js: 2.3.1 + tlds: 1.255.0 + dev: false + optional: true + + /mailsplit@5.4.2: + resolution: {integrity: sha512-4cczG/3Iu3pyl8JgQ76dKkisurZTmxMrA4dj/e8d2jKYcFTZ7MxOzg1gTioTDMPuFXwTrVuN/gxhkrO7wLg7qA==} + requiresBuild: true + dependencies: + libbase64: 1.3.0 + libmime: 5.3.6 + libqp: 2.1.1 + dev: false + optional: true + /make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -17805,6 +18410,12 @@ packages: readable-stream: 3.6.2 dev: false + /mensch@0.3.4: + resolution: {integrity: sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==} + requiresBuild: true + dev: false + optional: true + /merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} @@ -18225,7 +18836,6 @@ packages: resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} engines: {node: '>=4.0.0'} hasBin: true - dev: true /mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} @@ -18276,7 +18886,15 @@ packages: engines: {node: '>=10'} dependencies: brace-expansion: 2.0.1 - dev: true + + /minimatch@9.0.1: + resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} + engines: {node: '>=16 || 14 >=14.17'} + requiresBuild: true + dependencies: + brace-expansion: 2.0.1 + dev: false + optional: true /minimatch@9.0.3: resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==} @@ -18359,21 +18977,448 @@ packages: resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} engines: {node: '>=16 || 14 >=14.17'} - /minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} + /minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + dependencies: + minipass: 3.3.6 + yallist: 4.0.0 + dev: false + + /minizlib@3.0.1: + resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==} + engines: {node: '>= 18'} + dependencies: + minipass: 7.1.2 + rimraf: 5.0.10 + dev: false + + /mjml-accordion@4.15.3: + resolution: {integrity: sha512-LPNVSj1LyUVYT9G1gWwSw3GSuDzDsQCu0tPB2uDsq4VesYNnU6v3iLCQidMiR6azmIt13OEozG700ygAUuA6Ng==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-body@4.15.3: + resolution: {integrity: sha512-7pfUOVPtmb0wC+oUOn4xBsAw4eT5DyD6xqaxj/kssu6RrFXOXgJaVnDPAI9AzIvXJ/5as9QrqRGYAddehwWpHQ==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-button@4.15.3: + resolution: {integrity: sha512-79qwn9AgdGjJR1vLnrcm2rq2AsAZkKC5JPwffTMG+Nja6zGYpTDZFZ56ekHWr/r1b5WxkukcPj2PdevUug8c+Q==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-carousel@4.15.3: + resolution: {integrity: sha512-3ju6I4l7uUhPRrJfN3yK9AMsfHvrYbRkcJ1GRphFHzUj37B2J6qJOQUpzA547Y4aeh69TSb7HFVf1t12ejQxVw==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-cli@4.15.3: + resolution: {integrity: sha512-+V2TDw3tXUVEptFvLSerz125C2ogYl8klIBRY1m5BHd4JvGVf3yhx8N3PngByCzA6PGcv/eydGQN+wy34SHf0Q==} + hasBin: true + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + chokidar: 3.6.0 + glob: 10.3.12 + html-minifier: 4.0.0 + js-beautify: 1.15.4 + lodash: 4.17.21 + minimatch: 9.0.5 + mjml-core: 4.15.3 + mjml-migrate: 4.15.3 + mjml-parser-xml: 4.15.3 + mjml-validator: 4.15.3 + yargs: 17.7.2 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-column@4.15.3: + resolution: {integrity: sha512-hYdEFdJGHPbZJSEysykrevEbB07yhJGSwfDZEYDSbhQQFjV2tXrEgYcFD5EneMaowjb55e3divSJxU4c5q4Qgw==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-core@4.15.3: + resolution: {integrity: sha512-Dmwk+2cgSD9L9GmTbEUNd8QxkTZtW9P7FN/ROZW/fGZD6Hq6/4TB0zEspg2Ow9eYjZXO2ofOJ3PaQEEShKV0kQ==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + cheerio: 1.0.0-rc.12 + detect-node: 2.1.0 + html-minifier: 4.0.0 + js-beautify: 1.15.4 + juice: 10.0.1 + lodash: 4.17.21 + mjml-migrate: 4.15.3 + mjml-parser-xml: 4.15.3 + mjml-validator: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-divider@4.15.3: + resolution: {integrity: sha512-vh27LQ9FG/01y0b9ntfqm+GT5AjJnDSDY9hilss2ixIUh0FemvfGRfsGVeV5UBVPBKK7Ffhvfqc7Rciob9Spzw==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-group@4.15.3: + resolution: {integrity: sha512-HSu/rKnGZVKFq3ciT46vi1EOy+9mkB0HewO4+P6dP/Y0UerWkN6S3UK11Cxsj0cAp0vFwkPDCdOeEzRdpFEkzA==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-head-attributes@4.15.3: + resolution: {integrity: sha512-2ISo0r5ZKwkrvJgDou9xVPxxtXMaETe2AsAA02L89LnbB2KC0N5myNsHV0sEysTw9+CfCmgjAb0GAI5QGpxKkQ==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-head-breakpoint@4.15.3: + resolution: {integrity: sha512-Eo56FA5C2v6ucmWQL/JBJ2z641pLOom4k0wP6CMZI2utfyiJ+e2Uuinj1KTrgDcEvW4EtU9HrfAqLK9UosLZlg==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-head-font@4.15.3: + resolution: {integrity: sha512-CzV2aDPpiNIIgGPHNcBhgyedKY4SX3BJoTwOobSwZVIlEA6TAWB4Z9WwFUmQqZOgo1AkkiTHPZQvGcEhFFXH6g==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-head-html-attributes@4.15.3: + resolution: {integrity: sha512-MDNDPMBOgXUZYdxhosyrA2kudiGO8aogT0/cODyi2Ed9o/1S7W+je11JUYskQbncqhWKGxNyaP4VWa+6+vUC/g==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-head-preview@4.15.3: + resolution: {integrity: sha512-J2PxCefUVeFwsAExhrKo4lwxDevc5aKj888HBl/wN4EuWOoOg06iOGCxz4Omd8dqyFsrqvbBuPqRzQ+VycGmaA==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-head-style@4.15.3: + resolution: {integrity: sha512-9J+JuH+mKrQU65CaJ4KZegACUgNIlYmWQYx3VOBR/tyz+8kDYX7xBhKJCjQ1I4wj2Tvga3bykd89Oc2kFZ5WOw==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-head-title@4.15.3: + resolution: {integrity: sha512-IM59xRtsxID4DubQ0iLmoCGXguEe+9BFG4z6y2xQDrscIa4QY3KlfqgKGT69ojW+AVbXXJPEVqrAi4/eCsLItQ==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-head@4.15.3: + resolution: {integrity: sha512-o3mRuuP/MB5fZycjD3KH/uXsnaPl7Oo8GtdbJTKtH1+O/3pz8GzGMkscTKa97l03DAG2EhGrzzLcU2A6eshwFw==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-hero@4.15.3: + resolution: {integrity: sha512-9cLAPuc69yiuzNrMZIN58j+HMK1UWPaq2i3/Fg2ZpimfcGFKRcPGCbEVh0v+Pb6/J0+kf8yIO0leH20opu3AyQ==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-image@4.15.3: + resolution: {integrity: sha512-g1OhSdofIytE9qaOGdTPmRIp7JsCtgO0zbsn1Fk6wQh2gEL55Z40j/VoghslWAWTgT2OHFdBKnMvWtN6U5+d2Q==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-migrate@4.15.3: + resolution: {integrity: sha512-sr/+35RdxZroNQVegjpfRHJ5hda9XCgaS4mK2FGO+Mb1IUevKfeEPII3F/cHDpNwFeYH3kAgyqQ22ClhGLWNBA==} + hasBin: true + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + js-beautify: 1.15.4 + lodash: 4.17.21 + mjml-core: 4.15.3 + mjml-parser-xml: 4.15.3 + yargs: 17.7.2 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-navbar@4.15.3: + resolution: {integrity: sha512-VsKH/Jdlf8Yu3y7GpzQV5n7JMdpqvZvTSpF6UQXL0PWOm7k6+LX+sCZimOfpHJ+wCaaybpxokjWZ71mxOoCWoA==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-parser-xml@4.15.3: + resolution: {integrity: sha512-Tz0UX8/JVYICLjT+U8J1f/TFxIYVYjzZHeh4/Oyta0pLpRLeZlxEd71f3u3kdnulCKMP4i37pFRDmyLXAlEuLw==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + detect-node: 2.1.0 + htmlparser2: 9.1.0 + lodash: 4.17.21 + dev: false + optional: true + + /mjml-preset-core@4.15.3: + resolution: {integrity: sha512-1zZS8P4O0KweWUqNS655+oNnVMPQ1Rq1GaZq5S9JfwT1Vh/m516lSmiTW9oko6gGHytt5s6Yj6oOeu5Zm8FoLw==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + mjml-accordion: 4.15.3 + mjml-body: 4.15.3 + mjml-button: 4.15.3 + mjml-carousel: 4.15.3 + mjml-column: 4.15.3 + mjml-divider: 4.15.3 + mjml-group: 4.15.3 + mjml-head: 4.15.3 + mjml-head-attributes: 4.15.3 + mjml-head-breakpoint: 4.15.3 + mjml-head-font: 4.15.3 + mjml-head-html-attributes: 4.15.3 + mjml-head-preview: 4.15.3 + mjml-head-style: 4.15.3 + mjml-head-title: 4.15.3 + mjml-hero: 4.15.3 + mjml-image: 4.15.3 + mjml-navbar: 4.15.3 + mjml-raw: 4.15.3 + mjml-section: 4.15.3 + mjml-social: 4.15.3 + mjml-spacer: 4.15.3 + mjml-table: 4.15.3 + mjml-text: 4.15.3 + mjml-wrapper: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-raw@4.15.3: + resolution: {integrity: sha512-IGyHheOYyRchBLiAEgw3UM11kFNmBSMupu2BDdejC6ZiDhEAdG+tyERlsCwDPYtXanvFpGWULIu3XlsUPc+RZw==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-section@4.15.3: + resolution: {integrity: sha512-JfVPRXH++Hd933gmQfG8JXXCBCR6fIzC3DwiYycvanL/aW1cEQ2EnebUfQkt5QzlYjOkJEH+JpccAsq3ln6FZQ==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-social@4.15.3: + resolution: {integrity: sha512-7sD5FXrESOxpT9Z4Oh36bS6u/geuUrMP1aCg2sjyAwbPcF1aWa2k9OcatQfpRf6pJEhUZ18y6/WBBXmMVmSzXg==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-spacer@4.15.3: + resolution: {integrity: sha512-3B7Qj+17EgDdAtZ3NAdMyOwLTX1jfmJuY7gjyhS2HtcZAmppW+cxqHUBwCKfvSRgTQiccmEvtNxaQK+tfyrZqA==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-table@4.15.3: + resolution: {integrity: sha512-FLx7DcRKTdKdcOCbMyBaeudeHaHpwPveRrBm6WyQe3LXx6FfdmOh59i71/16LFQMgBOD3N4/UJkzxLzlTJzMqQ==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-text@4.15.3: + resolution: {integrity: sha512-+C0hxCmw9kg0XzT6vhE5mFkK6y225nC8UEQcN94K0fBCjPKkM+HqZMwGX205fzdGRi+Bxa55b/VhrIVwdv+8vw==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + transitivePeerDependencies: + - encoding + dev: false + optional: true + + /mjml-validator@4.15.3: + resolution: {integrity: sha512-Xb72KdqRwjv/qM2rJpV22syyP2N3cRQ9VVDrN6u2FSzLq02buFNxmSPJ7CKhat3PrUNdVHU75KZwOf/tz4UEhA==} + requiresBuild: true + dependencies: + '@babel/runtime': 7.26.9 + dev: false + optional: true + + /mjml-wrapper@4.15.3: + resolution: {integrity: sha512-ditsCijeHJrmBmObtJmQ18ddLxv5oPyMTdPU8Di8APOnD2zPk7Z4UAuJSl7HXB45oFiivr3MJf4koFzMUSZ6Gg==} + requiresBuild: true dependencies: - minipass: 3.3.6 - yallist: 4.0.0 + '@babel/runtime': 7.26.9 + lodash: 4.17.21 + mjml-core: 4.15.3 + mjml-section: 4.15.3 + transitivePeerDependencies: + - encoding dev: false + optional: true - /minizlib@3.0.1: - resolution: {integrity: sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg==} - engines: {node: '>= 18'} + /mjml@4.15.3: + resolution: {integrity: sha512-bW2WpJxm6HS+S3Yu6tq1DUPFoTxU9sPviUSmnL7Ua+oVO3WA5ILFWqvujUlz+oeuM+HCwEyMiP5xvKNPENVjYA==} + hasBin: true dependencies: - minipass: 7.1.2 - rimraf: 5.0.10 + '@babel/runtime': 7.26.9 + mjml-cli: 4.15.3 + mjml-core: 4.15.3 + mjml-migrate: 4.15.3 + mjml-preset-core: 4.15.3 + mjml-validator: 4.15.3 + transitivePeerDependencies: + - encoding dev: false + optional: true /mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} @@ -18575,6 +19620,20 @@ packages: - babel-plugin-macros dev: false + /nice-try@1.0.5: + resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + requiresBuild: true + dev: false + optional: true + + /no-case@2.3.2: + resolution: {integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==} + requiresBuild: true + dependencies: + lower-case: 1.1.4 + dev: false + optional: true + /no-case@3.0.4: resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} dependencies: @@ -18738,6 +19797,18 @@ packages: /node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + /nodemailer@6.10.0: + resolution: {integrity: sha512-SQ3wZCExjeSatLE/HBaXS5vqUOQk6GtBdIIKxiFdmm01mOQZX/POJkO3SUX1wDiYcwUOJwT23scFSC9fY2H8IA==} + engines: {node: '>=6.0.0'} + dev: false + + /nodemailer@6.9.16: + resolution: {integrity: sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ==} + engines: {node: '>=6.0.0'} + requiresBuild: true + dev: false + optional: true + /nodemon@3.1.9: resolution: {integrity: sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==} engines: {node: '>=10'} @@ -18763,6 +19834,16 @@ packages: abbrev: 1.1.1 dev: false + /nopt@7.2.1: + resolution: {integrity: sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + hasBin: true + requiresBuild: true + dependencies: + abbrev: 2.0.0 + dev: false + optional: true + /normalize-path@2.1.1: resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} engines: {node: '>=0.10.0'} @@ -18783,6 +19864,15 @@ packages: engines: {node: '>=14.16'} dev: false + /npm-run-path@2.0.2: + resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==} + engines: {node: '>=4'} + requiresBuild: true + dependencies: + path-key: 2.0.1 + dev: false + optional: true + /npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} @@ -18992,6 +20082,16 @@ packages: protobufjs: 7.4.0 dev: false + /open@7.4.2: + resolution: {integrity: sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + is-docker: 2.2.1 + is-wsl: 2.2.0 + dev: false + optional: true + /open@8.4.2: resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==} engines: {node: '>=12'} @@ -19107,6 +20207,15 @@ packages: engines: {node: '>=12.20'} dev: false + /p-event@4.2.0: + resolution: {integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + p-timeout: 3.2.0 + dev: false + optional: true + /p-finally@1.0.0: resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} engines: {node: '>=4'} @@ -19211,6 +20320,15 @@ packages: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} + /p-wait-for@3.2.0: + resolution: {integrity: sha512-wpgERjNkLrBiFmkMEjuZJEWKKDrNfHCKA1OhyN1wg1FrLkULbviEy6py1AyJUgZ72YWFbZ38FIpnqvVqAlDUwA==} + engines: {node: '>=8'} + requiresBuild: true + dependencies: + p-timeout: 3.2.0 + dev: false + optional: true + /package-json-from-dist@1.0.1: resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} @@ -19224,6 +20342,14 @@ packages: semver: 7.7.1 dev: false + /param-case@2.1.1: + resolution: {integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==} + requiresBuild: true + dependencies: + no-case: 2.3.2 + dev: false + optional: true + /param-case@3.0.4: resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} dependencies: @@ -19302,6 +20428,15 @@ packages: dependencies: entities: 4.5.0 + /parseley@0.12.1: + resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==} + requiresBuild: true + dependencies: + leac: 0.6.0 + peberminta: 0.9.0 + dev: false + optional: true + /parseurl@1.3.3: resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} engines: {node: '>= 0.8'} @@ -19345,6 +20480,13 @@ packages: resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==} dev: false + /path-key@2.0.1: + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} + engines: {node: '>=4'} + requiresBuild: true + dev: false + optional: true + /path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -19387,6 +20529,12 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + /peberminta@0.9.0: + resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==} + requiresBuild: true + dev: false + optional: true + /peek-readable@4.1.0: resolution: {integrity: sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==} engines: {node: '>=8'} @@ -20374,6 +21522,24 @@ packages: engines: {node: '>=4'} dev: false + /preview-email@3.1.0: + resolution: {integrity: sha512-ZtV1YrwscEjlrUzYrTSs6Nwo49JM3pXLM4fFOBSC3wSni+bxaWlw9/Qgk75PZO8M7cX2EybmL2iwvaV3vkAttw==} + engines: {node: '>=14'} + dependencies: + ci-info: 3.9.0 + display-notification: 2.0.0 + fixpack: 4.0.0 + get-port: 5.1.1 + mailparser: 3.7.2 + nodemailer: 6.10.0 + open: 7.4.2 + p-event: 4.2.0 + p-wait-for: 3.2.0 + pug: 3.0.3 + uuid: 9.0.1 + dev: false + optional: true + /prism-react-renderer@2.4.1(react@18.3.1): resolution: {integrity: sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==} peerDependencies: @@ -20432,7 +21598,6 @@ packages: resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} dependencies: asap: 2.0.6 - dev: true /prompts@2.4.2: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} @@ -20471,6 +21636,7 @@ packages: /proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + requiresBuild: true dev: false /protobufjs@7.4.0: @@ -20512,6 +21678,120 @@ packages: resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} dev: false + /pug-attrs@3.0.0: + resolution: {integrity: sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==} + requiresBuild: true + dependencies: + constantinople: 4.0.1 + js-stringify: 1.0.2 + pug-runtime: 3.0.1 + dev: false + optional: true + + /pug-code-gen@3.0.3: + resolution: {integrity: sha512-cYQg0JW0w32Ux+XTeZnBEeuWrAY7/HNE6TWnhiHGnnRYlCgyAUPoyh9KzCMa9WhcJlJ1AtQqpEYHc+vbCzA+Aw==} + requiresBuild: true + dependencies: + constantinople: 4.0.1 + doctypes: 1.1.0 + js-stringify: 1.0.2 + pug-attrs: 3.0.0 + pug-error: 2.1.0 + pug-runtime: 3.0.1 + void-elements: 3.1.0 + with: 7.0.2 + dev: false + optional: true + + /pug-error@2.1.0: + resolution: {integrity: sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==} + requiresBuild: true + dev: false + optional: true + + /pug-filters@4.0.0: + resolution: {integrity: sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==} + requiresBuild: true + dependencies: + constantinople: 4.0.1 + jstransformer: 1.0.0 + pug-error: 2.1.0 + pug-walk: 2.0.0 + resolve: 1.22.10 + dev: false + optional: true + + /pug-lexer@5.0.1: + resolution: {integrity: sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==} + requiresBuild: true + dependencies: + character-parser: 2.2.0 + is-expression: 4.0.0 + pug-error: 2.1.0 + dev: false + optional: true + + /pug-linker@4.0.0: + resolution: {integrity: sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==} + requiresBuild: true + dependencies: + pug-error: 2.1.0 + pug-walk: 2.0.0 + dev: false + optional: true + + /pug-load@3.0.0: + resolution: {integrity: sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==} + requiresBuild: true + dependencies: + object-assign: 4.1.1 + pug-walk: 2.0.0 + dev: false + optional: true + + /pug-parser@6.0.0: + resolution: {integrity: sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==} + requiresBuild: true + dependencies: + pug-error: 2.1.0 + token-stream: 1.0.0 + dev: false + optional: true + + /pug-runtime@3.0.1: + resolution: {integrity: sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==} + requiresBuild: true + dev: false + optional: true + + /pug-strip-comments@2.0.0: + resolution: {integrity: sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==} + requiresBuild: true + dependencies: + pug-error: 2.1.0 + dev: false + optional: true + + /pug-walk@2.0.0: + resolution: {integrity: sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==} + requiresBuild: true + dev: false + optional: true + + /pug@3.0.3: + resolution: {integrity: sha512-uBi6kmc9f3SZ3PXxqcHiUZLmIXgfgWooKWXcwSGwQd2Zi5Rb0bT14+8CJjJgI8AB+nndLaNgHGrcc6bPIB665g==} + dependencies: + pug-code-gen: 3.0.3 + pug-filters: 4.0.0 + pug-lexer: 5.0.1 + pug-linker: 4.0.0 + pug-load: 3.0.0 + pug-parser: 6.0.0 + pug-runtime: 3.0.1 + pug-strip-comments: 2.0.0 + dev: false + optional: true + /pump@3.0.2: resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} dependencies: @@ -20519,6 +21799,13 @@ packages: once: 1.4.0 dev: false + /punycode.js@2.3.1: + resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} + engines: {node: '>=6'} + requiresBuild: true + dev: false + optional: true + /punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -21444,6 +22731,15 @@ packages: strip-json-comments: 3.1.1 dev: false + /run-applescript@3.2.0: + resolution: {integrity: sha512-Ep0RsvAjnRcBX1p5vogbaBdAGu/8j/ewpvGqnQYunnLd9SM0vWcPJewPKNnWFggf0hF0pwIgwV5XK7qQ7UZ8Qg==} + engines: {node: '>=4'} + requiresBuild: true + dependencies: + execa: 0.10.0 + dev: false + optional: true + /run-async@2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} @@ -21565,6 +22861,14 @@ packages: kind-of: 6.0.3 dev: false + /selderee@0.11.0: + resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==} + requiresBuild: true + dependencies: + parseley: 0.12.1 + dev: false + optional: true + /select-hose@2.0.0: resolution: {integrity: sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==} dev: false @@ -21584,6 +22888,13 @@ packages: semver: 7.7.1 dev: false + /semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + requiresBuild: true + dev: false + optional: true + /semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true @@ -21755,12 +23066,28 @@ packages: '@img/sharp-win32-x64': 0.33.5 dev: false + /shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dependencies: + shebang-regex: 1.0.0 + dev: false + optional: true + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} dependencies: shebang-regex: 3.0.0 + /shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: false + optional: true + /shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} @@ -21934,6 +23261,12 @@ packages: is-fullwidth-code-point: 5.0.0 dev: false + /slick@1.12.2: + resolution: {integrity: sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==} + requiresBuild: true + dev: false + optional: true + /smart-buffer@4.2.0: resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} @@ -22308,6 +23641,13 @@ packages: engines: {node: '>=8'} dev: true + /strip-eof@1.0.0: + resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: false + optional: true + /strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} @@ -22751,6 +24091,13 @@ packages: tslib: 2.6.3 dev: true + /tlds@1.255.0: + resolution: {integrity: sha512-tcwMRIioTcF/FcxLev8MJWxCp+GUALRhFEqbDoZrnowmKSGqPrl5pqS+Sut2m8BgJ6S4FExCSSpGffZ0Tks6Aw==} + hasBin: true + requiresBuild: true + dev: false + optional: true + /tmp@0.0.33: resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} engines: {node: '>=0.6.0'} @@ -22777,6 +24124,12 @@ packages: resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} engines: {node: '>=0.6'} + /token-stream@1.0.0: + resolution: {integrity: sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==} + requiresBuild: true + dev: false + optional: true + /token-types@4.2.1: resolution: {integrity: sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ==} engines: {node: '>=10'} @@ -23328,6 +24681,20 @@ packages: hasBin: true dev: true + /uc.micro@2.1.0: + resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} + requiresBuild: true + dev: false + optional: true + + /uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} + hasBin: true + requiresBuild: true + dev: false + optional: true + /uid@2.0.2: resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==} engines: {node: '>=8'} @@ -23520,6 +24887,12 @@ packages: tslib: 2.6.3 dev: true + /upper-case@1.1.3: + resolution: {integrity: sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==} + requiresBuild: true + dev: false + optional: true + /upper-case@2.0.2: resolution: {integrity: sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==} dependencies: @@ -23685,6 +25058,13 @@ packages: convert-source-map: 2.0.0 dev: true + /valid-data-url@3.0.1: + resolution: {integrity: sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==} + engines: {node: '>=10'} + requiresBuild: true + dev: false + optional: true + /validate-npm-package-name@6.0.0: resolution: {integrity: sha512-d7KLgL1LD3U3fgnvWEY1cQXoO/q6EQ1BSz48Sa149V/5zVTAbgmZIpyI8TRi6U9/JNyeYLlTKsEMPtLC27RFUg==} engines: {node: ^18.17.0 || >=20.5.0} @@ -23725,6 +25105,13 @@ packages: '@types/unist': 3.0.3 vfile-message: 4.0.2 + /void-elements@3.1.0: + resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==} + engines: {node: '>=0.10.0'} + requiresBuild: true + dev: false + optional: true + /w3c-xmlserializer@4.0.0: resolution: {integrity: sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==} engines: {node: '>=14'} @@ -23761,6 +25148,22 @@ packages: resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==} dev: false + /web-resource-inliner@6.0.1: + resolution: {integrity: sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==} + engines: {node: '>=10.0.0'} + requiresBuild: true + dependencies: + ansi-colors: 4.1.3 + escape-goat: 3.0.0 + htmlparser2: 5.0.1 + mime: 2.6.0 + node-fetch: 2.7.0 + valid-data-url: 3.0.1 + transitivePeerDependencies: + - encoding + dev: false + optional: true + /web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} @@ -24155,10 +25558,28 @@ packages: /wildcard@2.0.1: resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==} + /with@7.0.2: + resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==} + engines: {node: '>= 10.0.0'} + requiresBuild: true + dependencies: + '@babel/parser': 7.26.9 + '@babel/types': 7.26.9 + assert-never: 1.4.0 + babel-walk: 3.0.0-canary-5 + dev: false + optional: true + /word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + /wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + requiresBuild: true + dev: false + optional: true + /wrap-ansi@6.2.0: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} From f9cbbc939239ff667f3d97692d5ccfda08a3a0bf Mon Sep 17 00:00:00 2001 From: PengyuChen01 <121841974+PengyuChen01@users.noreply.github.com> Date: Tue, 4 Mar 2025 21:36:51 -0500 Subject: [PATCH 07/13] feat(frontend): landing page ui optimization (#149) Co-authored-by: pengyu <pengyuchen01@gmail.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- frontend/src/app/(main)/page.tsx | 26 +++++++++++----- frontend/src/components/auth-choice-modal.tsx | 21 +++++++++++-- frontend/src/components/root/nav.tsx | 4 +-- frontend/src/components/root/prompt-form.tsx | 2 +- frontend/src/components/sidebar.tsx | 30 ++++++++++++++++--- frontend/src/components/sign-in-modal.tsx | 4 ++- 6 files changed, 70 insertions(+), 17 deletions(-) diff --git a/frontend/src/app/(main)/page.tsx b/frontend/src/app/(main)/page.tsx index e6842819..1f942a76 100644 --- a/frontend/src/app/(main)/page.tsx +++ b/frontend/src/app/(main)/page.tsx @@ -8,9 +8,15 @@ import { useAuthContext } from '@/providers/AuthProvider'; import { ProjectsSection } from '@/components/root/projects-section'; import { PromptForm, PromptFormRef } from '@/components/root/prompt-form'; import { ProjectContext } from '@/components/chat/code-engine/project-context'; - +import { SignInModal } from '@/components/sign-in-modal'; +import { SignUpModal } from '@/components/sign-up-modal'; +import { useRouter } from 'next/navigation'; export default function HomePage() { + // States for AuthChoiceModal const [showAuthChoice, setShowAuthChoice] = useState(false); + const router = useRouter(); + const [showSignIn, setShowSignIn] = useState(false); + const [showSignUp, setShowSignUp] = useState(false); const promptFormRef = useRef<PromptFormRef>(null); const { isAuthorized } = useAuthContext(); @@ -19,9 +25,7 @@ export default function HomePage() { const handleSubmit = async () => { if (!promptFormRef.current) return; - // Get form data from the prompt form const { message, isPublic, model } = promptFormRef.current.getPromptData(); - if (!message.trim()) return; try { @@ -37,12 +41,11 @@ export default function HomePage() { } } catch (error) { console.error('Error creating project:', error); - // Error handling is done via toast in ProjectContext } }; return ( - <div className="pt-32 pb-24 px-6 "> + <div className="pt-32 pb-24 px-6"> <motion.div className="flex flex-col items-center" initial={{ opacity: 0, y: 20 }} @@ -85,19 +88,28 @@ export default function HomePage() { </div> </motion.div> - {/* Modals */} + {/* Choice Modal */} <AuthChoiceModal isOpen={showAuthChoice} onClose={() => setShowAuthChoice(false)} onSignUpClick={() => { setShowAuthChoice(false); + setTimeout(() => { + setShowSignUp(true); + }, 100); }} onSignInClick={() => { setShowAuthChoice(false); + setTimeout(() => { + setShowSignIn(true); + }, 100); }} /> - {/* Add this to your global CSS for the subtle pulse animation */} + {/* SignInModal & SignUpModal */} + <SignInModal isOpen={showSignIn} onClose={() => setShowSignIn(false)} /> + <SignUpModal isOpen={showSignUp} onClose={() => setShowSignUp(false)} /> + <style jsx global>{` .animate-pulse-subtle { animation: pulse-subtle 2s infinite; diff --git a/frontend/src/components/auth-choice-modal.tsx b/frontend/src/components/auth-choice-modal.tsx index 02119567..b37de238 100644 --- a/frontend/src/components/auth-choice-modal.tsx +++ b/frontend/src/components/auth-choice-modal.tsx @@ -1,4 +1,5 @@ 'use client'; + import { Dialog, DialogContent, DialogTitle } from '@/components/ui/dialog'; import { BackgroundGradient } from '@/components/ui/background-gradient'; import { Button } from '@/components/ui/button'; @@ -24,6 +25,7 @@ export function AuthChoiceModal({ <VisuallyHidden> <DialogTitle>Choose Authentication Method</DialogTitle> </VisuallyHidden> + <BackgroundGradient className="rounded-[22px] p-4 bg-white dark:bg-zinc-900"> <div className="w-full p-6 space-y-6"> <h2 className="text-2xl font-semibold text-center dark:text-white"> @@ -33,16 +35,31 @@ export function AuthChoiceModal({ Choose how you want to continue </p> <div className="space-y-4"> + {/* Sign In button */} <Button className="w-full py-6 text-lg bg-primary hover:bg-primary/90" - onClick={onSignInClick} + onClick={() => { + // 1) Close current modal + onClose(); + // 2) After a brief delay, call onSignInClick + setTimeout(() => { + onSignInClick(); + }, 100); + }} > Sign in </Button> + + {/* Sign Up button */} <Button variant="outline" className="w-full py-6 text-lg" - onClick={onSignUpClick} + onClick={() => { + onClose(); + setTimeout(() => { + onSignUpClick(); + }, 100); + }} > Create an account </Button> diff --git a/frontend/src/components/root/nav.tsx b/frontend/src/components/root/nav.tsx index 51a1add3..e24b805a 100644 --- a/frontend/src/components/root/nav.tsx +++ b/frontend/src/components/root/nav.tsx @@ -174,7 +174,7 @@ const FloatingNavbar = forwardRef<NavbarRef, FloatingNavbarProps>( alert('Coming Soon'); } else if (label === 'Codefox Journey') { e.preventDefault(); - window.open('https://github.com/Sma1lboy/codefox', '_blank'); + alert('Coming Soon'); } else { handleTabChange(index); } @@ -182,7 +182,7 @@ const FloatingNavbar = forwardRef<NavbarRef, FloatingNavbarProps>( return ( <> - <div className={`fixed top-5 left-0 right-0 z-50 ${className}`}> + <div className={` top-5 left-0 right-0 z-50 ${className}`}> <motion.div className={`w-full flex justify-around items-center px-6 py-4 ${containerClassName}`} initial="hidden" diff --git a/frontend/src/components/root/prompt-form.tsx b/frontend/src/components/root/prompt-form.tsx index 408e489a..3fca2bff 100644 --- a/frontend/src/components/root/prompt-form.tsx +++ b/frontend/src/components/root/prompt-form.tsx @@ -211,7 +211,7 @@ export const PromptForm = forwardRef<PromptFormRef, PromptFormProps>( > <SelectTrigger className={cn( - 'w-[117px] h-6 border-0 focus:ring-0 hover:bg-gray-100 dark:hover:bg-gray-600 pl-1', + 'h-6 border-0 focus:ring-0 hover:bg-gray-100 dark:hover:bg-gray-600 pl-1 min-w-max', (isLoading || isRegenerating) && 'opacity-50 cursor-not-allowed' )} diff --git a/frontend/src/components/sidebar.tsx b/frontend/src/components/sidebar.tsx index 7f5c4311..4e6b7147 100644 --- a/frontend/src/components/sidebar.tsx +++ b/frontend/src/components/sidebar.tsx @@ -8,6 +8,7 @@ import UserSettings from './user-settings'; import { SideBarItem } from './sidebar-item'; import { Chat } from '@/graphql/type'; import { EventEnum } from '../const/EventEnum'; +import { useRouter } from 'next/navigation'; import { SidebarContent, @@ -49,6 +50,7 @@ export function ChatSideBar({ onRefetch, }: SidebarProps) { // Use a local state only for the currently selected chat. + const router = useRouter(); const [currentChatid, setCurrentChatid] = useState(''); const { setCurProject, pollChatProject } = useContext(ProjectContext); // Handler for starting a new chat. @@ -84,8 +86,27 @@ export function ChatSideBar({ className="lg:flex items-center justify-center cursor-pointer p-2 ml-3.5 mt-2" onClick={() => setIsCollapsed(!isCollapsed)} /> - - <div className="flex items-center justify-start w-[85%] h-14 text-sm xl:text-lg font-normal pl-4 gap-2"> + <Button + onClick={() => router.push('/')} + variant="ghost" + className=" + w-full + h-14 + flex + items-center + justify-start + px-4 + gap-2 + text-sm + xl:text-lg + font-normal + rounded-md + hover:bg-yellow-50 + transition-all + duration-200 + ease-in-out + " + > <Image src="/codefox.svg" alt="CodeFox Logo" @@ -98,9 +119,10 @@ export function ChatSideBar({ CodeFox </span> )} - </div> + </Button> + {/* Divider Line */} - <div className="border-t border-dotted border-gray-300 my-2 w-[85%] mx-auto"></div> + <div className="border-t border-dotted border-gray-300 my-2 w-full mx-auto" /> <Button onClick={() => setIsModalOpen(true)} diff --git a/frontend/src/components/sign-in-modal.tsx b/frontend/src/components/sign-in-modal.tsx index de69cc63..498a45b2 100644 --- a/frontend/src/components/sign-in-modal.tsx +++ b/frontend/src/components/sign-in-modal.tsx @@ -43,7 +43,9 @@ export function SignInModal({ isOpen, onClose }: SignInModalProps) { if (data?.login) { // Store tokens where desired (session storage for access, local for refresh) login(data.login.accessToken, data.login.refreshToken); - toast.success('Login successful!'); + toast.success('Login successful!', { + position: 'bottom-right', + }); setErrorMessage(null); onClose(); // Close the modal From 20301ed8906bb2d70967ac47939aaacd4547d45f Mon Sep 17 00:00:00 2001 From: NarwhalChen <125920907+NarwhalChen@users.noreply.github.com> Date: Tue, 4 Mar 2025 20:39:48 -0600 Subject: [PATCH 08/13] fix(frontend): fix cannot redirect into chat/project page after create project from home page (#151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Enhanced file recognition to support .tsx files as TypeScript React. - Introduced new iframe controls, allowing users to refresh content and adjust zoom levels. - Updated the code editor’s default language for a more accurate editing experience. - Revised project navigation to use query parameters for improved clarity. - **Bug Fixes** - Improved Docker container management with enhanced logging, error handling, and retry mechanisms. - **Refactor** - Simplified chat and sidebar structures by removing unnecessary context providers. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --- frontend/src/components/chat/code-engine/project-context.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/chat/code-engine/project-context.tsx b/frontend/src/components/chat/code-engine/project-context.tsx index 78dbdd16..0aa394e3 100644 --- a/frontend/src/components/chat/code-engine/project-context.tsx +++ b/frontend/src/components/chat/code-engine/project-context.tsx @@ -113,7 +113,7 @@ export function ProjectProvider({ children }: { children: ReactNode }) { // Navigate to chat page after project creation if (data?.createProject?.id) { toast.success('Project created successfully!'); - router.push(`/chat/${data.createProject.id}`); + router.push(`/chat?id=${data.createProject.id}`); } }, onError: (error) => { From 34a86c6df9ee0f732db87468c54210ea741975d0 Mon Sep 17 00:00:00 2001 From: NarwhalChen <125920907+NarwhalChen@users.noreply.github.com> Date: Tue, 4 Mar 2025 20:51:50 -0600 Subject: [PATCH 09/13] =?UTF-8?q?feat(frontend):=20implement=20expandable?= =?UTF-8?q?=20project=20cards=20and=20fetch=20public=20p=E2=80=A6=20(#152)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …rojects query <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced an interactive `ExpandableCard` component for project previews, featuring a modal overlay with smooth animations. - Added a custom hook for detecting clicks outside of elements to enhance user interactions. - **Refactor** - Streamlined the public projects display by integrating a new GraphQL query and simplifying the component structure for improved data retrieval and presentation. - **Chores** - Removed unused props from the `SidebarProps` interface and the `ChatSideBar` component for a cleaner codebase. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- frontend/src/components/root/expand-card.tsx | 173 ++++++++++++++++++ .../src/components/root/projects-section.tsx | 81 +------- frontend/src/components/sidebar.tsx | 6 - frontend/src/graphql/request.ts | 16 ++ frontend/src/graphql/schema.gql | 7 +- 5 files changed, 199 insertions(+), 84 deletions(-) create mode 100644 frontend/src/components/root/expand-card.tsx diff --git a/frontend/src/components/root/expand-card.tsx b/frontend/src/components/root/expand-card.tsx new file mode 100644 index 00000000..1095aacc --- /dev/null +++ b/frontend/src/components/root/expand-card.tsx @@ -0,0 +1,173 @@ +'use client'; +import Image from 'next/image'; +import React, { useEffect, useRef, useState } from 'react'; +import { AnimatePresence, motion } from 'framer-motion'; +import { X } from 'lucide-react'; + +export function ExpandableCard({ projects }) { + const [active, setActive] = useState(null); + const [iframeUrl, setIframeUrl] = useState(''); + const ref = useRef<HTMLDivElement>(null); + + useEffect(() => { + function onKeyDown(event: KeyboardEvent) { + if (event.key === 'Escape') { + setActive(null); + } + } + + if (active && typeof active === 'object') { + document.body.style.overflow = 'hidden'; + } else { + document.body.style.overflow = 'auto'; + } + + window.addEventListener('keydown', onKeyDown); + return () => window.removeEventListener('keydown', onKeyDown); + }, [active]); + + const getWebUrl = async (project) => { + if (!project) return; + console.log('project:', project); + const projectPath = project.path; + + try { + const response = await fetch( + `/api/runProject?projectPath=${encodeURIComponent(projectPath)}`, + { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + } + ); + const json = await response.json(); + const baseUrl = `http://${json.domain}`; + setIframeUrl(baseUrl); + setActive(project); + } catch (error) { + console.error('fetching url error:', error); + } + }; + + return ( + <> + <AnimatePresence mode="wait"> + {active && ( + <motion.div + onClick={() => setActive(null)} + initial={{ opacity: 0 }} + animate={{ opacity: 1 }} + exit={{ opacity: 0 }} + transition={{ + duration: 0.3, + ease: [0.4, 0, 0.2, 1], + }} + className="fixed inset-0 backdrop-blur-[2px] bg-black/20 h-full w-full z-50" + style={{ willChange: 'opacity' }} + /> + )} + </AnimatePresence> + + <AnimatePresence mode="wait"> + {active ? ( + <div className="fixed inset-0 grid place-items-center z-[80] m-4"> + <motion.button + initial={{ opacity: 0, scale: 0.9 }} + animate={{ opacity: 1, scale: 1 }} + exit={{ opacity: 0, scale: 0.9 }} + transition={{ duration: 0.2 }} + className="flex absolute top-4 right-4 items-center justify-center bg-white/90 hover:bg-white rounded-full h-8 w-8" + onClick={() => setActive(null)} + > + <X className="h-4 w-4 z-50" /> + </motion.button> + + <motion.div + layoutId={`card-${active.id}`} + ref={ref} + className="w-full h-full flex flex-col bg-white dark:bg-neutral-900 rounded-2xl overflow-hidden" + style={{ willChange: 'transform, opacity' }} + > + <motion.div className="flex-1 p-6 h-[50%]"> + <motion.div + layoutId={`content-${active.id}`} + className="h-full" + > + <motion.h3 + layoutId={`title-${active.id}`} + className="text-xl font-semibold text-gray-900 dark:text-gray-100" + > + {active.name} + </motion.h3> + <motion.div + layoutId={`meta-${active.id}`} + className="mt-2 w-full h-full" + > + <iframe + src={iframeUrl} + className="w-full h-[100%]" + title="Project Preview" + /> + </motion.div> + </motion.div> + </motion.div> + </motion.div> + </div> + ) : null} + </AnimatePresence> + + <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6"> + {projects.map((project) => ( + <motion.div + key={project.id} + layoutId={`card-${project.id}`} + onClick={() => getWebUrl(project)} + className="group cursor-pointer" + > + <motion.div + layoutId={`image-container-${project.id}`} + className="relative rounded-xl overflow-hidden" + > + <motion.div layoutId={`image-${project.id}`}> + <Image + src={project.image} + alt={project.name} + width={600} + height={200} + className="w-full h-48 object-cover transition duration-300 group-hover:scale-105" + /> + </motion.div> + + <motion.div + initial={{ opacity: 0 }} + whileHover={{ opacity: 1 }} + transition={{ duration: 0.2 }} + className="absolute inset-0 bg-black/40 flex items-center justify-center" + > + <span className="text-white font-medium px-4 py-2 rounded-lg bg-white/20 backdrop-blur-sm"> + View Project + </span> + </motion.div> + </motion.div> + + <motion.div layoutId={`content-${project.id}`} className="mt-3"> + <motion.h3 + layoutId={`title-${project.id}`} + className="font-medium text-gray-900 dark:text-gray-100" + > + {project.name} + </motion.h3> + <motion.div + layoutId={`meta-${project.id}`} + className="mt-1 text-sm text-gray-500" + > + {project.author} + </motion.div> + </motion.div> + </motion.div> + ))} + </div> + </> + ); +} diff --git a/frontend/src/components/root/projects-section.tsx b/frontend/src/components/root/projects-section.tsx index 42b63eea..792d2a42 100644 --- a/frontend/src/components/root/projects-section.tsx +++ b/frontend/src/components/root/projects-section.tsx @@ -1,74 +1,6 @@ -import { gql, useQuery } from '@apollo/client'; -import Image from 'next/image'; - -const FETCH_PUBLIC_PROJECTS = gql` - query FetchPublicProjects($input: FetchPublicProjectsInputs!) { - fetchPublicProjects(input: $input) { - id - projectName - createdAt - user { - username - } - photoUrl - subNumber - } - } -`; - -const ProjectCard = ({ project }) => ( - <div className="cursor-pointer group space-y-3"> - {/* Image section with card styling */} - <div className="relative rounded-lg overflow-hidden shadow-md transform transition-all duration-300 hover:-translate-y-2 hover:shadow-xl"> - <Image - src={project.image} - alt={project.name} - width={600} - height={200} - className="w-full h-36 object-cover transition-all duration-300 group-hover:brightness-75" - /> - <div className="absolute bottom-0 right-0 bg-black bg-opacity-70 text-white text-xs px-2 py-1 rounded-tl-md"> - {project.forkNum} forks - </div> - - {/* "View Detail" hover effect */} - <div className="absolute inset-0 flex items-center justify-center opacity-0 group-hover:opacity-100 transition-opacity duration-300"> - <button className="bg-primary-500 hover:bg-primary-600 text-white px-4 py-2 rounded-md font-medium transform transition-transform duration-300 scale-90 group-hover:scale-100"> - View Detail - </button> - </div> - </div> - - {/* Info section */} - <div className="px-1"> - <div className="flex flex-col space-y-2"> - <h3 className="text-sm font-semibold text-gray-800 dark:text-gray-100 truncate"> - {project.name} - </h3> - <div className="flex items-center justify-between"> - <div className="flex items-center"> - <span className="inline-block w-5 h-5 rounded-full bg-gray-300 dark:bg-gray-600 mr-2"></span> - <span className="text-sm font-medium text-gray-700 dark:text-gray-300"> - {project.author} - </span> - </div> - <span className="text-xs text-gray-500 dark:text-gray-400"> - {formatDate(project.createDate)} - </span> - </div> - </div> - </div> - </div> -); - -const formatDate = (dateString) => { - const date = new Date(dateString); - return date.toLocaleDateString('en-US', { - year: 'numeric', - month: 'short', - day: 'numeric', - }); -}; +import { useQuery } from '@apollo/client'; +import { FETCH_PUBLIC_PROJECTS } from '@/graphql/request'; +import { ExpandableCard } from './expand-card'; export function ProjectsSection() { // Execute the GraphQL query with provided variables @@ -83,6 +15,7 @@ export function ProjectsSection() { const transformedProjects = fetchedProjects.map((project) => ({ id: project.id, name: project.projectName, + path: project.projectPath, createDate: project.createdAt ? new Date(project.createdAt).toISOString().split('T')[0] : '2025-01-01', @@ -112,11 +45,7 @@ export function ProjectsSection() { ) : ( <div> {transformedProjects.length > 0 ? ( - <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6"> - {transformedProjects.map((project) => ( - <ProjectCard key={project.id} project={project} /> - ))} - </div> + <ExpandableCard projects={transformedProjects} /> ) : ( // Show message when no projects are available <div className="text-center py-10 text-gray-500 dark:text-gray-400"> diff --git a/frontend/src/components/sidebar.tsx b/frontend/src/components/sidebar.tsx index 4e6b7147..d69c0ede 100644 --- a/frontend/src/components/sidebar.tsx +++ b/frontend/src/components/sidebar.tsx @@ -19,9 +19,7 @@ import { SidebarRail, SidebarFooter, } from './ui/sidebar'; -import { cn } from '@/lib/utils'; import { ProjectContext } from './chat/code-engine/project-context'; -import { useRouter } from 'next/navigation'; interface SidebarProps { setIsModalOpen: (value: boolean) => void; // Parent setter to update collapse state @@ -41,9 +39,6 @@ export function ChatSideBar({ setIsModalOpen, isCollapsed, setIsCollapsed, - isMobile, - chatListUpdated, - setChatListUpdated, chats, loading, error, @@ -60,7 +55,6 @@ export function ChatSideBar({ const event = new Event(EventEnum.NEW_CHAT); window.dispatchEvent(event); }, []); - const router = useRouter(); if (loading) return <SidebarSkeleton />; if (error) { diff --git a/frontend/src/graphql/request.ts b/frontend/src/graphql/request.ts index 3a0d5fef..31f60f55 100644 --- a/frontend/src/graphql/request.ts +++ b/frontend/src/graphql/request.ts @@ -17,6 +17,22 @@ export interface ModelTagsData { getAvailableModelTags: string[]; } +export const FETCH_PUBLIC_PROJECTS = gql` + query FetchPublicProjects($input: FetchPublicProjectsInputs!) { + fetchPublicProjects(input: $input) { + id + projectName + projectPath + createdAt + user { + username + } + photoUrl + subNumber + } + } +`; + export const CREATE_CHAT = gql` mutation CreateChat($input: NewChatInput!) { createChat(newChatInput: $input) { diff --git a/frontend/src/graphql/schema.gql b/frontend/src/graphql/schema.gql index c637f81d..b14c81ba 100644 --- a/frontend/src/graphql/schema.gql +++ b/frontend/src/graphql/schema.gql @@ -137,7 +137,9 @@ type Project { projectPath: String! subNumber: Float! - """Projects that are copies of this project""" + """ + Projects that are copies of this project + """ subscribers: [Project!] uniqueProjectId: String! updatedAt: Date! @@ -224,7 +226,8 @@ type User { isActive: Boolean! isDeleted: Boolean! projects: [Project!]! - subscribedProjects: [Project!] @deprecated(reason: "Use projects with forkedFromId instead") + subscribedProjects: [Project!] + @deprecated(reason: "Use projects with forkedFromId instead") updatedAt: Date! username: String! } \ No newline at end of file From a6cf08512b8793c449a6404aa69b6c9b9a83b9c0 Mon Sep 17 00:00:00 2001 From: Jackson Chen <90215880+Sma1lboy@users.noreply.github.com> Date: Tue, 4 Mar 2025 20:58:23 -0600 Subject: [PATCH 10/13] chore: remove unused model configuration from environment file (#145) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Documentation** - Revamped the project overview with a clear title, reorganized sections highlighting key features, architecture, system requirements, development tools, and additional resources. - Enhanced installation instructions, troubleshooting guidance, and URLs for accessing different components. - **Chores** - Streamlined configuration by removing redundant AI model integration settings. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --- README.md | 434 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 314 insertions(+), 120 deletions(-) diff --git a/README.md b/README.md index 57ce37b8..a1013b3c 100644 --- a/README.md +++ b/README.md @@ -1,187 +1,381 @@ -> [!CAUTION] -> Still working on it, it will release soon +# CodeFox  -Welcome to CODEFOX! A next generation AI sequence full stack project generator with interactive chatbot +Welcome to CODEFOX! A next generation AI sequence full stack project generator with interactive chatbot. -# News +## Key Features -🌟 Oct. 18, 2024: First line of Codefox code committed. - -# Exciting features - -💻 **Transforming Ideas into Projects** -🚀 **Extraordinary Modeling System**: Integrates an AI model to seamlessly connect every aspect of your project. +💻 **Transforming Ideas into Projects** +🚀 **Extraordinary Modeling System**: Integrates an AI model to seamlessly connect every aspect of your project. 🤖 **Multi-Agent Generator**: Create and manage multiple intelligent agents to enhance project functionality. -⚡ **One-Click Deployment**: Deploy your project effortlessly to cloud services or clone it locally with ease. -✨ **Live Preview**: Interact with your project while engaging in AI-powered conversations to make real-time modifications. +⚡ **One-Click Deployment**: Deploy your project effortlessly to cloud services or clone it locally with ease. +✨ **Live Preview**: Interact with your project while engaging in AI-powered conversations to make real-time modifications. 🔧 **Precise Code Customization**: Leverage targeted and efficient visual tools for precise module adjustments. -## Support - -> [!WARNING] -> adding later - -**Revolutionize development with this disruptive platform. Join now and set the new standard!** - -```mermaid -graph TD - subgraph Project_Generate_Layer[Project Generate Layer] - UP[User Project Info] --> PRD[Product Requirements Document] - PRD --> FRD[Feature Requirements Document] - PRD --> UXSD[UX Sitemap Document] - UXSD --> UXDD[UX Datamap Document] - UXDD --> DRD[Database Requirements Document] - DRD --> DBS[DB/schemas] - DRD --> DBP[DB/postgres] - DRD --> BRD[Backend Requirements Document] - - %% Frontend related generations - UXSD --> USS[ux/sitemap-structure] - USS --> ROUTE[frontend/routing] - UXDD --> UDS[ux/datamap-structure] - UXDD --> UDV[ux/datamap-views] - - %% Webview generations - USS --> WV1[webview/page1] - USS --> WV2[webview/page2] - USS --> WV3[webview/page3] - USS --> ROOT[webview/root] - UDV --> ROOT - - %% Optional: Show multiple pages with a note - note[...more webviews...] - USS --> note - end - - %% Styling - classDef default fill:#f9f9f9,stroke:#333,stroke-width:2px - classDef boxStyle fill:#fff,stroke:#666,stroke-width:1px - classDef noteStyle fill:#fff4e6,stroke:#d9480f,stroke-width:1px - class UP,PRD,FRD,UXSD,UXDD,DRD,DBS,DBP,BRD,USS,UDS,UDV,ROUTE,WV1,WV2,WV3,ROOT boxStyle - class note noteStyle - classDef layerStyle fill:#f4f4f4,stroke:#666,stroke-width:1px,stroke-dasharray: 5 5 - class Project_Generate_Layer layerStyle -``` - -# CodeFox Development Guide - ## Prerequisites -Before you begin, ensure you have the following installed: +### System Requirements - Node.js >= 18.0.0 +- PostgreSQL >= 14.0 +- GPU (Optional, for local LLM model running) +- Memory: Minimum 16GB RAM recommended +- Storage: At least 10GB free space + +### Development Tools + - PNPM 9.1.2 (`npm install -g pnpm@9.1.2`) - Tmux >= 3.2 - Tmuxinator >= 3.0.0 (`gem install tmuxinator`) -## Project Structure - -The project consists of three main components: +### Optional Requirements -``` -codefox/ -├── backend/ # NestJS backend server -├── frontend/ # Next.js frontend application -└── llm-server/ # LLM service -``` +- NVIDIA CUDA Toolkit (for GPU acceleration) +- Docker & Docker Compose (for containerized development) ## Installation -1. Clone the repository: +1. **Clone the repository** ```bash git clone <repository-url> cd codefox ``` -2. Install dependencies: +2. **Install dependencies** ```bash pnpm install ``` -3. Set up environment variables: +3. **Environment Configuration** -```bash -# Copy and configure environment files for each service -cp backend/.env.template backend/.env -cp frontend/.env.template frontend/.env -cp llm-server/.env.template llm-server/.env +Create and configure environment files for each service: + +**Backend (.env)** + +```env +PORT=8080 +JWT_SECRET=<your-jwt-secret> +JWT_REFRESH=<your-refresh-token-secret> +SALT_ROUNDS=10 +OPENAI_BASE_URI=http://localhost:3001 ``` +**Frontend (.env)** + +```env +NEXT_PUBLIC_GRAPHQL_URL=http://localhost:8080/graphql +``` + +**LLM Server (.env)** + +```env +PORT=3001 +MODEL_PATH=/path/to/model +MODEL_TYPE=llama +NODE_ENV=development +``` + +**Model Configuration (.codefox/config.json)** + +Configure the AI models for chat and embedding. For cloud-based models, provide endpoint and token. For local models, omit the endpoint. + +```json +{ + "$schema": "../config.schema.json", + "chat": [ + { + "model": "openai/gpt-4o-mini", // Required: Model identifier + "alias": "gpt-4o-mini", // Required: Model alias for reference + "endpoint": "https://openrouter.ai/api/v1", // Optional: API endpoint (omit for local models) + "token": "<your-openrouter-token>", // Optional: API token (required if endpoint is specified) + "default": true, // Optional: Set as default model + "rps": 30 // Optional: Requests per second limit + } + ], + "embedding": [ + { + "model": "openai/text-embedding-ada-002", // Required: Model identifier + "endpoint": "https://api.openai.com", // Optional: API endpoint (omit for local models) + "token": "<your-openai-token>" // Optional: API token (required if endpoint is specified) + } + ] +} +``` + +Model Configuration Fields: + +- **Chat Models**: + + - `model`: (Required) Model identifier + - `alias`: (Required) Model reference name in the system + - `endpoint`: (Optional) API endpoint URL. Omit for local models + - `token`: (Optional) API access token. Required if endpoint is specified + - `default`: (Optional) Set as the default model for chat + - `rps`: (Optional) Rate limit for API requests per second + +- **Embedding Models**: + - `model`: (Required) Model identifier + - `endpoint`: (Optional) API endpoint URL. Omit for local models + - `token`: (Optional) API access token. Required if endpoint is specified + ## Development ### Using Tmuxinator (Recommended) -The project includes a Tmuxinator configuration for easy development. This will start all services in separate windows with proper layouts: +Start all services with the pre-configured Tmuxinator setup: ```bash -pnpm dev:tmux +pnpm dev ``` -This command will create: - -- Window 1: Backend server -- Window 2: Frontend development server (left) and GraphQL codegen watcher (right) -- Window 3: LLM server - -Tmux Navigation: +This creates a development environment with: -- `Ctrl+a 1/2/3` - Switch between windows -- `Ctrl+a r` - Restart current pane's service -- `Ctrl+a d` - Detach from session +- Backend server (http://localhost:8080) +- Frontend development server (http://localhost:3000) +- LLM server (http://localhost:3001) +- GraphQL codegen watcher ### Manual Development -If you prefer to run services individually: +Start services individually: ```bash -# Start all services +# Start backend +cd backend pnpm dev -# Or start services individually -pnpm dev:backend # Start backend only -cd frontend && pnpm dev # Start frontend only -cd llm-server && pnpm dev # Start LLM server only +# Start frontend +cd frontend +pnpm dev + +# Start LLM server +cd llm-server +pnpm dev ``` -## Additional Commands +### Development URLs + +- Frontend: http://localhost:3000 +- Backend GraphQL Playground: http://localhost:8080/graphql +- LLM Server: http://localhost:3001 + +## Architecture Overview + +CodeFox consists of three main components that work together: -```bash -pnpm build # Build all packages -pnpm lint # Run linting -pnpm format # Format code -pnpm test # Run tests +``` + +-------------+ + | Frontend | + | (Next.js) | + +------+------+ + | + | GraphQL + | + +------v------+ + | Backend | + | (NestJS) | + +------+------+ + | + | HTTP/WebSocket + | + +------v------+ + | LLM Server | + +-------------+ ``` -## GraphQL Code Generation +- **Frontend (Next.js)**: Provides the user interface and handles client-side logic +- **Backend (NestJS)**: Manages business logic, authentication, and project generation +- **LLM Server**: Handles AI model interactions and code generation -The frontend uses GraphQL with automatic type generation. The codegen watcher is automatically started in the Tmuxinator setup, but you can also run it manually: +### Build System Architecture -```bash -cd frontend -pnpm generate:watch +The backend includes a sophisticated build system that manages project generation through a sequence of dependent tasks. Here's how it works: + +```mermaid +sequenceDiagram + participant User + participant Context as BuilderContext + participant Manager as HandlerManager + participant Handler as BuildHandler + participant Monitor as BuildMonitor + participant VDir as VirtualDirectory + + User->>Context: Create new build sequence + Context->>Manager: Initialize handlers + Context->>Monitor: Start sequence monitoring + + loop For each node in sequence + Context->>Context: Check dependencies + alt Dependencies met + Context->>Manager: Get handler instance + Manager-->>Context: Return handler + Context->>Monitor: Start node execution + Context->>Handler: Execute run() + + Handler->>VDir: Update virtual files + VDir-->>Handler: Files updated + + Handler-->>Context: Return result + Context->>Monitor: End node execution + else Dependencies not met + Context->>Context: Wait and retry + end + end + + Context->>Monitor: Generate build report + Context-->>User: Return project UUID ``` -## Troubleshooting +Key components: -If you encounter any issues: +1. **BuilderContext** -1. Ensure all environment variables are properly set -2. Check if all required services are running -3. Clear node_modules and reinstall dependencies: + - Manages the execution state of build nodes + - Handles dependency resolution + - Coordinates between handlers and virtual filesystem -```bash -pnpm clean -pnpm install +2. **BuildHandlerManager** + + - Singleton managing handler instances + - Provides handler registration and retrieval + - Manages handler dependencies + +3. **BuildHandler** + + - Implements specific build tasks + - Can declare dependencies on other handlers + - Has access to virtual filesystem and model + +4. **BuildMonitor** + + - Tracks execution progress + - Records timing and success/failure + - Generates build reports + +5. **VirtualDirectory** + - Manages in-memory file structure + - Provides file operations during build + - Ensures atomic file updates + +### Full-Stack Project Generation Workflow + +The build system follows a structured workflow to generate a complete full-stack project: + +```mermaid +graph TD + %% Project Initialization + Init[Project Initialization] --> Product[Product Requirements] + Product --> UX[UX Design] + + %% UX Design Flow + UX --> Sitemap[Sitemap Structure] + UX --> Datamap[Data Structure] + + %% Backend Development + Datamap --> DB[Database Schema] + DB --> BE[Backend Structure] + BE --> API[API Design] + BE --> BackendCode[Backend Code] + API --> BackendCode + + %% Frontend Development + Sitemap --> Routes[Route Structure] + Datamap --> Components[Component Design] + Components --> Views[View Implementation] + + %% File Management and Generation + Views --> FE[Frontend Code] + API --> FE + + %% Subgraphs for different roles + subgraph "Product Manager" + Init + Product + end + + subgraph "UX Designer" + UX + Sitemap + Datamap + end + + subgraph "Backend Engineer" + DB + BE + API + BackendCode + end + + subgraph "Frontend Engineer" + Routes + Components + Views + FE + end + + %% Styling + classDef product fill:#e1f5fe,stroke:#01579b + classDef ux fill:#f3e5f5,stroke:#4a148c + classDef backend fill:#e8f5e9,stroke:#1b5e20 + classDef frontend fill:#fff3e0,stroke:#e65100 + + class Init,Product product + class UX,Sitemap,Datamap ux + class DB,BE,API,BackendCode backend + class Routes,Components,Views,FE frontend ``` -4. For Tmuxinator issues: - - Ensure Tmux is running version 3.2 or higher - - Check if the session is already running: `tmux ls` - - Kill existing session if needed: `tmux kill-session -t codefox` +## Troubleshooting + +### Common Issues + +1. **Port Conflicts** + + - Ensure ports 3000, 8080, and 3001 are available + - Check for any running processes: `lsof -i :<port>` + +2. **Environment Issues** + + - Verify all environment variables are properly set + - Ensure model path is correct in LLM server configuration + - Verify model configurations in .codefox/config.json: + - Check model identifiers are correct + - Validate endpoint URLs for cloud-based models + - Ensure API tokens are valid + - Verify local model paths for non-cloud models + +3. **Build Issues** + + ```bash + # Clean installation + pnpm clean + rm -rf node_modules + pnpm install + + # Rebuild all packages + pnpm build + ``` + +4. **Tmuxinator Issues** + - Ensure Tmux version is >= 3.2: `tmux -V` + - Kill existing session: `tmux kill-session -t codefox` + - Check session status: `tmux ls` + +## Additional Resources + +- [API Documentation](./docs/api.md) +- [Contributing Guidelines](./CONTRIBUTING.md) +- [Change Log](./CHANGELOG.md) + +## Support + +For support and questions: + +- GitHub Issues: [Create an issue](https://github.com/your-repo/issues) +- Documentation: [CodeFox Docs](./codefox-docs) ## License From 4ca3a2c9daefa9798e575e1bf37b8d48cfb21661 Mon Sep 17 00:00:00 2001 From: PengyuChen01 <121841974+PengyuChen01@users.noreply.github.com> Date: Tue, 4 Mar 2025 21:59:00 -0500 Subject: [PATCH 11/13] Feat fix sidebar need reload when first time load (#150) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Improved authentication experience with dedicated sign-in and sign-up modals featuring smoother transitions. - Conditional data fetching based on user authorization enhances security and performance. - Navigation updates now provide clear alerts for upcoming features (e.g., a “Coming Soon” message when selecting specific tabs). - **Style** - Enhanced UI responsiveness with adjustments to layouts, flexible component widths, and repositioned notifications for a better visual experience. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: pengyu <pengyuchen01@gmail.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Sma1lboy <541898146chen@gmail.com> Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- frontend/src/components/chat/chat-layout.tsx | 4 +++- .../chat/code-engine/project-context.tsx | 4 +++- frontend/src/components/chat/index.tsx | 3 +++ frontend/src/components/sidebar.tsx | 8 -------- frontend/src/hooks/useChatList.ts | 4 +++- frontend/src/hooks/useModels.ts | 4 ++-- frontend/src/providers/AuthProvider.tsx | 17 ++++++++++++----- 7 files changed, 26 insertions(+), 18 deletions(-) diff --git a/frontend/src/components/chat/chat-layout.tsx b/frontend/src/components/chat/chat-layout.tsx index 0f13d951..70a00070 100644 --- a/frontend/src/components/chat/chat-layout.tsx +++ b/frontend/src/components/chat/chat-layout.tsx @@ -14,7 +14,9 @@ export default function ChatLayout({ const { isAuthorized } = useAuthContext(); const [isModalOpen, setIsModalOpen] = useState(false); - const { refetch } = useQuery(GET_USER_PROJECTS); + const { refetch } = useQuery(GET_USER_PROJECTS, { + skip: !isAuthorized, + }); const router = useRouter(); useEffect(() => { diff --git a/frontend/src/components/chat/code-engine/project-context.tsx b/frontend/src/components/chat/code-engine/project-context.tsx index 0aa394e3..df3578c3 100644 --- a/frontend/src/components/chat/code-engine/project-context.tsx +++ b/frontend/src/components/chat/code-engine/project-context.tsx @@ -18,6 +18,7 @@ import { import { Project } from '../project-modal'; import { useRouter } from 'next/navigation'; import { toast } from 'sonner'; // Assuming you use Sonner for toasts +import { useAuthContext } from '@/providers/AuthProvider'; export interface ProjectContextType { projects: Project[]; @@ -48,7 +49,7 @@ export const ProjectContext = createContext<ProjectContextType | undefined>( export function ProjectProvider({ children }: { children: ReactNode }) { const router = useRouter(); - + const { isAuthorized } = useAuthContext(); const [projects, setProjects] = useState<Project[]>([]); const [curProject, setCurProject] = useState<Project | undefined>(undefined); const [filePath, setFilePath] = useState<string | null>(null); @@ -85,6 +86,7 @@ export function ProjectProvider({ children }: { children: ReactNode }) { const { loading, error, refetch } = useQuery(GET_USER_PROJECTS, { fetchPolicy: 'network-only', + skip: !isAuthorized, onCompleted: (data) => { setProjects(data.getUserProjects); // If we have a current project in the list, update it diff --git a/frontend/src/components/chat/index.tsx b/frontend/src/components/chat/index.tsx index e373739c..4ed551c8 100644 --- a/frontend/src/components/chat/index.tsx +++ b/frontend/src/components/chat/index.tsx @@ -17,9 +17,11 @@ import { useChatStream } from '@/hooks/useChatStream'; import { CodeEngine } from './code-engine/code-engine'; import { useProjectStatusMonitor } from '@/hooks/useProjectStatusMonitor'; import { Loader2 } from 'lucide-react'; +import { useAuthContext } from '@/providers/AuthProvider'; export default function Chat() { // Initialize state, refs, and custom hooks + const { isAuthorized } = useAuthContext(); const urlParams = new URLSearchParams(window.location.search); const [chatId, setChatId] = useState(''); const [messages, setMessages] = useState<any[]>([]); @@ -36,6 +38,7 @@ export default function Chat() { // Apollo query to fetch chat history useQuery(GET_CHAT_HISTORY, { variables: { chatId }, + skip: !isAuthorized || !chatId, onCompleted: (data) => { if (data?.getChatHistory) { setMessages(data.getChatHistory); diff --git a/frontend/src/components/sidebar.tsx b/frontend/src/components/sidebar.tsx index d69c0ede..3dc97c92 100644 --- a/frontend/src/components/sidebar.tsx +++ b/frontend/src/components/sidebar.tsx @@ -55,20 +55,12 @@ export function ChatSideBar({ const event = new Event(EventEnum.NEW_CHAT); window.dispatchEvent(event); }, []); - if (loading) return <SidebarSkeleton />; if (error) { console.error('Error loading chats:', error); return null; } - console.log( - 'ChatSideBar state: isCollapsed:', - isCollapsed, - 'currentChatid:', - currentChatid - ); - return ( <div data-collapsed={isCollapsed} diff --git a/frontend/src/hooks/useChatList.ts b/frontend/src/hooks/useChatList.ts index 9b3b8e7d..31245a60 100644 --- a/frontend/src/hooks/useChatList.ts +++ b/frontend/src/hooks/useChatList.ts @@ -2,10 +2,11 @@ import { useQuery } from '@apollo/client'; import { GET_USER_CHATS } from '@/graphql/request'; import { Chat } from '@/graphql/type'; import { useState, useCallback, useMemo } from 'react'; +import { useAuthContext } from '@/providers/AuthProvider'; export function useChatList() { const [chatListUpdated, setChatListUpdated] = useState(false); - + const { isAuthorized } = useAuthContext(); const { data: chatData, loading, @@ -13,6 +14,7 @@ export function useChatList() { refetch, } = useQuery<{ getUserChats: Chat[] }>(GET_USER_CHATS, { fetchPolicy: chatListUpdated ? 'network-only' : 'cache-first', + skip: !isAuthorized, }); const handleRefetch = useCallback(() => { diff --git a/frontend/src/hooks/useModels.ts b/frontend/src/hooks/useModels.ts index 3ccc3260..1a23270d 100644 --- a/frontend/src/hooks/useModels.ts +++ b/frontend/src/hooks/useModels.ts @@ -9,9 +9,9 @@ interface ModelsCache { models: string[]; lastUpdate: number; } - const CACHE_DURATION = 30 * 60 * 1000; export const useModels = () => { + const { isAuthorized } = useAuthContext(); const [selectedModel, setSelectedModel] = useState<string | undefined>( undefined ); @@ -50,7 +50,7 @@ export const useModels = () => { const { data, loading, error } = useQuery<{ getAvailableModelTags: string[]; }>(GET_MODEL_TAGS, { - skip: !shouldUpdateCache(), + skip: !isAuthorized || !shouldUpdateCache(), onCompleted: (data) => { console.log(data); if (data?.getAvailableModelTags) { diff --git a/frontend/src/providers/AuthProvider.tsx b/frontend/src/providers/AuthProvider.tsx index 968ca16a..20b99d25 100644 --- a/frontend/src/providers/AuthProvider.tsx +++ b/frontend/src/providers/AuthProvider.tsx @@ -53,12 +53,10 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { setUser(null); return false; } - try { const { data } = await checkToken({ variables: { input: { token: storedToken } }, }); - if (data?.checkToken) { setToken(storedToken); return true; @@ -91,11 +89,9 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { logout(); return false; } - const { data } = await refreshTokenMutation({ variables: { refreshToken }, }); - if (data?.refreshToken) { const newAccess = data.refreshToken.accessToken; const newRefresh = data.refreshToken.refreshToken; @@ -104,7 +100,6 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { if (newRefresh) { localStorage.setItem(LocalStore.refreshToken, newRefresh); } - setToken(newAccess); setIsAuthorized(true); return newAccess; @@ -125,6 +120,9 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { localStorage.setItem(LocalStore.refreshToken, refreshToken); setToken(accessToken); + if (process.env.NODE_ENV !== 'production') { + console.log('Token saved successfully'); + } setIsAuthorized(true); fetchUserInfo(); }, @@ -143,6 +141,15 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { async function initAuth() { setIsLoading(true); + const storedToken = localStorage.getItem(LocalStore.accessToken); + if (!storedToken) { + console.log('No stored token found, skip checkToken'); + setIsAuthorized(false); + setUser(null); + setIsLoading(false); + return; + } + let isValid = await validateToken(); if (!isValid) { From a4f1b2c9f66f1586261abc7be19dba0a4052ab6e Mon Sep 17 00:00:00 2001 From: PengyuChen01 <121841974+PengyuChen01@users.noreply.github.com> Date: Tue, 4 Mar 2025 22:49:28 -0500 Subject: [PATCH 12/13] feat(frontend): css optimization in frontend (#156) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Refactor** - Streamlined the project creation process for a smoother workflow. - **Style** - Redesigned sidebar elements with refined spacing and improved responsiveness. - **New Features** - Introduced new account management capabilities, including email confirmation and project limit querying. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: pengyu <pengyuchen01@gmail.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: NarwhalChen <125920907+NarwhalChen@users.noreply.github.com> --- frontend/src/app/(main)/page.tsx | 12 +- frontend/src/components/sidebar-item.tsx | 6 +- frontend/src/components/sidebar.tsx | 148 ++++++++++++----------- frontend/src/graphql/schema.gql | 21 +++- frontend/src/providers/AuthProvider.tsx | 2 + 5 files changed, 103 insertions(+), 86 deletions(-) diff --git a/frontend/src/app/(main)/page.tsx b/frontend/src/app/(main)/page.tsx index 1f942a76..accf5439 100644 --- a/frontend/src/app/(main)/page.tsx +++ b/frontend/src/app/(main)/page.tsx @@ -29,16 +29,8 @@ export default function HomePage() { if (!message.trim()) return; try { - // Create the project - const result = await createProjectFromPrompt(message, isPublic, model); - - // If successful, clear the input - if (result) { - promptFormRef.current.clearMessage(); - - // Note: No need to navigate here as the ProjectContext's onCompleted handler - // in the createProject mutation will handle navigation to the chat page - } + await createProjectFromPrompt(message, isPublic, model); + promptFormRef.current.clearMessage(); } catch (error) { console.error('Error creating project:', error); } diff --git a/frontend/src/components/sidebar-item.tsx b/frontend/src/components/sidebar-item.tsx index 91b1897a..d405dca3 100644 --- a/frontend/src/components/sidebar-item.tsx +++ b/frontend/src/components/sidebar-item.tsx @@ -104,11 +104,11 @@ export function SideBarItem({ buttonVariants({ variant, }), - 'flex justify-between w-full h-14 text-base font-normal items-center group' + 'relative flex w-full h-14 text-base font-normal items-center group px-2' )} onClick={handleChatClick} > - <div className="flex-1 flex gap-3 items-center truncate ml-2"> + <div className="flex-1 flex items-center truncate ml-2 mr-12 min-w-0"> <div className="flex flex-col"> <span className="text-xs font-normal">{title || 'New Chat'}</span> </div> @@ -119,7 +119,7 @@ export function SideBarItem({ <DropdownMenuTrigger asChild> <Button variant="ghost" - className="flex justify-end items-center dropdown-trigger mr-2" + className="absolute right-2 top-1/2 transform -translate-y-1/2 w-10 h-10 rounded-md hover:bg-gray-200" onClick={(e) => { e.preventDefault(); e.stopPropagation(); diff --git a/frontend/src/components/sidebar.tsx b/frontend/src/components/sidebar.tsx index 3dc97c92..8d22d661 100644 --- a/frontend/src/components/sidebar.tsx +++ b/frontend/src/components/sidebar.tsx @@ -1,8 +1,8 @@ 'use client'; + import { Button } from '@/components/ui/button'; import Image from 'next/image'; import { memo, useCallback, useContext, useState } from 'react'; -import { SquarePen } from 'lucide-react'; import SidebarSkeleton from './sidebar-skeleton'; import UserSettings from './user-settings'; import { SideBarItem } from './sidebar-item'; @@ -22,9 +22,9 @@ import { import { ProjectContext } from './chat/code-engine/project-context'; interface SidebarProps { - setIsModalOpen: (value: boolean) => void; // Parent setter to update collapse state + setIsModalOpen: (value: boolean) => void; isCollapsed: boolean; - setIsCollapsed: (value: boolean) => void; // Parent setter to update collapse state + setIsCollapsed: (value: boolean) => void; isMobile: boolean; currentChatId?: string; chatListUpdated: boolean; @@ -44,11 +44,10 @@ export function ChatSideBar({ error, onRefetch, }: SidebarProps) { - // Use a local state only for the currently selected chat. const router = useRouter(); const [currentChatid, setCurrentChatid] = useState(''); const { setCurProject, pollChatProject } = useContext(ProjectContext); - // Handler for starting a new chat. + const handleNewChat = useCallback(() => { window.history.replaceState({}, '', '/'); setCurrentChatid(''); @@ -64,86 +63,97 @@ export function ChatSideBar({ return ( <div data-collapsed={isCollapsed} - className="relative justify-between group lg:bg-accent/0 lg:dark:bg-card/0 flex flex-col h-full" + className="relative flex flex-col h-full justify-between group lg:bg-accent/0 lg:dark:bg-card/0" > <Sidebar collapsible="icon" side="left"> - {/* Toggle button: Clicking this will toggle the collapse state */} - <SidebarTrigger - className="lg:flex items-center justify-center cursor-pointer p-2 ml-3.5 mt-2" - onClick={() => setIsCollapsed(!isCollapsed)} - /> - <Button - onClick={() => router.push('/')} - variant="ghost" - className=" - w-full - h-14 - flex - items-center - justify-start - px-4 - gap-2 - text-sm - xl:text-lg - font-normal - rounded-md - hover:bg-yellow-50 - transition-all - duration-200 - ease-in-out - " + {/* Header Row: Fox Logo (clickable) on the left, SidebarTrigger on the right */} + <div + className={`flex items-center ${isCollapsed ? 'justify-center w-full px-0' : 'justify-between px-3'} pt-3`} > - <Image - src="/codefox.svg" - alt="CodeFox Logo" - width={32} - height={32} - className="flex-shrink-0 dark:invert" - /> {!isCollapsed && ( - <span className="text-primary-500 font-semibold text-lg"> - CodeFox - </span> + <div className="flex flex-1 items-center justify-between"> + <Button + onClick={() => router.push('/')} + variant="ghost" + className="inline-flex items-center gap-2 pl-0 + rounded-md ease-in-out" + > + <Image + src="/codefox.svg" + alt="CodeFox Logo" + width={40} + height={40} + className="dark:invert" + /> + <span className="text-primary-500 font-semibold text-base"> + CodeFox + </span> + </Button> + + {/* SidebarTrigger 保证在 CodeFox 按钮的中间 */} + <SidebarTrigger + className="flex items-center justify-center w-12 h-12 " + onClick={() => setIsCollapsed(!isCollapsed)} + /> + </div> + )} + + {isCollapsed && ( + <SidebarTrigger + className="flex items-center justify-center w-full p-2 mt" + onClick={() => setIsCollapsed(!isCollapsed)} + /> )} - </Button> + </div> {/* Divider Line */} <div className="border-t border-dotted border-gray-300 my-2 w-full mx-auto" /> - <Button - onClick={() => setIsModalOpen(true)} - size="setting" - variant="ghost" - className="flex items-center justify-start w-[85%] h-14 text-xs xl:text-sm font-normal gap-2 pl-4 hover:bg-yellow-50 rounded-md transition-all duration-200 ease-in-out" + {/* New Project 按钮 - 依然占据整行 */} + <div + className={`flex ${isCollapsed ? 'justify-center items-center w-full px-0' : ''} w-full mt-4`} > - <div className="flex items-center gap-2"> + <Button + onClick={() => { + if (isCollapsed) { + router.push('/'); + } else { + setIsModalOpen(true); + } + }} + variant="ghost" + className={`h-7 w-7 flex items-center justify-center rounded-md ease-in-out ${ + !isCollapsed && 'w-full gap-2 pl-4 justify-start' + }`} + > <svg + data-name="Layer 1" + viewBox="0 0 32 32" + preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" - fill="none" - viewBox="0 0 24 24" - strokeWidth="1.5" - stroke="currentColor" - className="w-5 h-5 text-yellow-500" + className={ + isCollapsed + ? 'w-8 h-8 min-w-[32px] min-h-[32px] ml-[-5px] mt-[-10px]' + : 'w-10 h-10 min-w-[32px] min-h-[32px] mr-1' + } + strokeWidth="0.1" > - <path - strokeLinecap="round" - strokeLinejoin="round" - d="M12 18v-5.25m0 0a6.01 6.01 0 0 0 1.5-.189m-1.5.189a6.01 6.01 0 0 1-1.5-.189m3.75 7.478a12.06 12.06 0 0 1-4.5 0m3.75 2.383a14.406 14.406 0 0 1-3 0M14.25 18v-.192c0-.983.658-1.823 1.508-2.316a7.5 7.5 0 1 0-7.517 0c.85.493 1.509 1.333 1.509 2.316V18" - /> + <g transform="scale(-1,1) translate(-32,0)"> + <path + d="M5,8A1,1,0,0,0,7,8V7H8A1,1,0,0,0,8,5H7V4A1,1,0,0,0,5,4V5H4A1,1,0,0,0,4,7H5ZM18,5H12a1,1,0,0,0,0,2h6a1,1,0,0,1,1,1v9.72l-1.57-1.45a1,1,0,0,0-.68-.27H8a1,1,0,0,1-1-1V12a1,1,0,0,0-2,0v3a3,3,0,0,0,3,3h8.36l3,2.73A1,1,0,0,0,20,21a1.1,1.1,0,0,0,.4-.08A1,1,0,0,0,21,20V8A3,3,0,0,0,18,5Z" + fill="#808080" + /> + </g> </svg> - {!isCollapsed && ( - <span className="text-primary-600 hover:text-primary-800 transition-colors text-sm"> + <span className="text-gray-600 hover:text-gray-800 font-semibold text-sm relative -top-0.5"> New Project </span> )} + </Button> + </div> - {!isCollapsed && ( - <SquarePen className="text-primary-400 hover:text-primary-600 transition-colors w-4 h-4" /> - )} - </div> - </Button> - + {/* 聊天列表 */} <SidebarContent> <SidebarGroup> <SidebarGroupContent> @@ -171,12 +181,14 @@ export function ChatSideBar({ </SidebarGroup> </SidebarContent> - <SidebarFooter> + {/* 底部设置 */} + <SidebarFooter + className={`mt-auto ${isCollapsed ? 'flex justify-center px-0' : ''}`} + > <UserSettings isSimple={false} /> </SidebarFooter> <SidebarRail - // Optional: Provide a secondary trigger if needed. setIsSimple={() => setIsCollapsed(!isCollapsed)} isSimple={false} /> diff --git a/frontend/src/graphql/schema.gql b/frontend/src/graphql/schema.gql index b14c81ba..55d76b75 100644 --- a/frontend/src/graphql/schema.gql +++ b/frontend/src/graphql/schema.gql @@ -57,6 +57,11 @@ input CreateProjectInput { """Date custom scalar type""" scalar Date +type EmailConfirmationResponse { + message: String! + success: Boolean +} + input FetchPublicProjectsInputs { size: Float! strategy: String! @@ -101,6 +106,7 @@ type Message { type Mutation { clearChatHistory(chatId: String!): Boolean! + confirmEmail(token: String!): EmailConfirmationResponse! createChat(newChatInput: NewChatInput!): Chat! createProject(createProjectInput: CreateProjectInput!): Chat! deleteChat(chatId: String!): Boolean! @@ -110,6 +116,7 @@ type Mutation { refreshToken(refreshToken: String!): RefreshTokenResponse! regenerateDescription(input: String!): String! registerUser(input: RegisterUserInput!): User! + resendConfirmationEmail(input: ResendEmailInput!): EmailConfirmationResponse! subscribeToProject(projectId: ID!): Project! triggerChatStream(input: ChatInputType!): Boolean! updateChatTitle(updateChatTitleInput: UpdateChatTitleInput!): Chat @@ -137,9 +144,7 @@ type Project { projectPath: String! subNumber: Float! - """ - Projects that are copies of this project - """ + """Projects that are copies of this project""" subscribers: [Project!] uniqueProjectId: String! updatedAt: Date! @@ -171,6 +176,7 @@ type Query { getChatHistory(chatId: String!): [Message!]! getHello: String! getProject(projectId: String!): Project! + getRemainingProjectLimit: Int! getSubscribedProjects: [Project!]! getUserChats: [Chat!] getUserProjects: [Project!]! @@ -190,6 +196,10 @@ input RegisterUserInput { username: String! } +input ResendEmailInput { + email: String! +} + enum Role { Assistant System @@ -225,9 +235,10 @@ type User { id: ID! isActive: Boolean! isDeleted: Boolean! + isEmailConfirmed: Boolean! + lastEmailSendTime: Date! projects: [Project!]! - subscribedProjects: [Project!] - @deprecated(reason: "Use projects with forkedFromId instead") + subscribedProjects: [Project!] @deprecated(reason: "Use projects with forkedFromId instead") updatedAt: Date! username: String! } \ No newline at end of file diff --git a/frontend/src/providers/AuthProvider.tsx b/frontend/src/providers/AuthProvider.tsx index 20b99d25..f4239347 100644 --- a/frontend/src/providers/AuthProvider.tsx +++ b/frontend/src/providers/AuthProvider.tsx @@ -152,10 +152,12 @@ export function AuthProvider({ children }: { children: React.ReactNode }) { let isValid = await validateToken(); + // 如果验证失败,再试图刷新 if (!isValid) { isValid = (await refreshAccessToken()) ? true : false; } + // 最终判断 if (isValid) { setIsAuthorized(true); await fetchUserInfo(); From 6be83682e7af317c46bc0aebb3eefd3a38a79900 Mon Sep 17 00:00:00 2001 From: Jackson Chen <90215880+Sma1lboy@users.noreply.github.com> Date: Tue, 4 Mar 2025 21:49:41 -0600 Subject: [PATCH 13/13] feat(frontend): add email confirmation mutations and response types and adding control section bg (#157) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **Style** - Refined the prompt form's control section for improved spacing and consistent background display across light and dark themes. - **New Features** - Enhanced the email confirmation process with detailed status responses for confirmation and resend requests. - Introduced the ability to query remaining project limits and updated user account details with email confirmation status and timing information. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- frontend/src/components/root/prompt-form.tsx | 2 +- frontend/src/graphql/type.tsx | 63 ++++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/root/prompt-form.tsx b/frontend/src/components/root/prompt-form.tsx index 3fca2bff..2d469e64 100644 --- a/frontend/src/components/root/prompt-form.tsx +++ b/frontend/src/components/root/prompt-form.tsx @@ -164,7 +164,7 @@ export const PromptForm = forwardRef<PromptFormRef, PromptFormProps>( </div> {/* Controls section - now separated with a background */} - <div className="absolute bottom-0 left-0 right-0 py-3 px-3 flex justify-between items-center rounded-b-lg border-gray-100 dark:border-gray-600"> + <div className="absolute bottom-0 left-0 right-0 pb-3 px-3 flex pt-1 justify-between items-center bg-white dark:bg-gray-600 rounded-b-lg dark:border-gray-600"> <div className="flex items-center gap-2"> <Select value={visibility} diff --git a/frontend/src/graphql/type.tsx b/frontend/src/graphql/type.tsx index 9f8b8b8f..eaa9c3b3 100644 --- a/frontend/src/graphql/type.tsx +++ b/frontend/src/graphql/type.tsx @@ -96,6 +96,12 @@ export type CreateProjectInput = { public?: InputMaybe<Scalars['Boolean']['input']>; }; +export type EmailConfirmationResponse = { + __typename: 'EmailConfirmationResponse'; + message: Scalars['String']['output']; + success?: Maybe<Scalars['Boolean']['output']>; +}; + export type FetchPublicProjectsInputs = { size: Scalars['Float']['input']; strategy: Scalars['String']['input']; @@ -144,6 +150,7 @@ export type Message = { export type Mutation = { __typename: 'Mutation'; clearChatHistory: Scalars['Boolean']['output']; + confirmEmail: EmailConfirmationResponse; createChat: Chat; createProject: Chat; deleteChat: Scalars['Boolean']['output']; @@ -153,6 +160,7 @@ export type Mutation = { refreshToken: RefreshTokenResponse; regenerateDescription: Scalars['String']['output']; registerUser: User; + resendConfirmationEmail: EmailConfirmationResponse; subscribeToProject: Project; triggerChatStream: Scalars['Boolean']['output']; updateChatTitle?: Maybe<Chat>; @@ -164,6 +172,10 @@ export type MutationClearChatHistoryArgs = { chatId: Scalars['String']['input']; }; +export type MutationConfirmEmailArgs = { + token: Scalars['String']['input']; +}; + export type MutationCreateChatArgs = { newChatInput: NewChatInput; }; @@ -200,6 +212,10 @@ export type MutationRegisterUserArgs = { input: RegisterUserInput; }; +export type MutationResendConfirmationEmailArgs = { + input: ResendEmailInput; +}; + export type MutationSubscribeToProjectArgs = { projectId: Scalars['ID']['input']; }; @@ -275,6 +291,7 @@ export type Query = { getChatHistory: Array<Message>; getHello: Scalars['String']['output']; getProject: Project; + getRemainingProjectLimit: Scalars['Int']['output']; getSubscribedProjects: Array<Project>; getUserChats?: Maybe<Array<Chat>>; getUserProjects: Array<Project>; @@ -319,6 +336,10 @@ export type RegisterUserInput = { username: Scalars['String']['input']; }; +export type ResendEmailInput = { + email: Scalars['String']['input']; +}; + export type Role = 'Assistant' | 'System' | 'User'; export type StreamStatus = 'DONE' | 'STREAMING'; @@ -350,6 +371,8 @@ export type User = { id: Scalars['ID']['output']; isActive: Scalars['Boolean']['output']; isDeleted: Scalars['Boolean']['output']; + isEmailConfirmed: Scalars['Boolean']['output']; + lastEmailSendTime: Scalars['Date']['output']; projects: Array<Project>; /** @deprecated Use projects with forkedFromId instead */ subscribedProjects?: Maybe<Array<Project>>; @@ -476,9 +499,11 @@ export type ResolversTypes = ResolversObject<{ CheckTokenInput: CheckTokenInput; CreateProjectInput: CreateProjectInput; Date: ResolverTypeWrapper<Scalars['Date']['output']>; + EmailConfirmationResponse: ResolverTypeWrapper<EmailConfirmationResponse>; FetchPublicProjectsInputs: FetchPublicProjectsInputs; Float: ResolverTypeWrapper<Scalars['Float']['output']>; ID: ResolverTypeWrapper<Scalars['ID']['output']>; + Int: ResolverTypeWrapper<Scalars['Int']['output']>; IsValidProjectInput: IsValidProjectInput; LoginResponse: ResolverTypeWrapper<LoginResponse>; LoginUserInput: LoginUserInput; @@ -492,6 +517,7 @@ export type ResolversTypes = ResolversObject<{ Query: ResolverTypeWrapper<{}>; RefreshTokenResponse: ResolverTypeWrapper<RefreshTokenResponse>; RegisterUserInput: RegisterUserInput; + ResendEmailInput: ResendEmailInput; Role: Role; StreamStatus: StreamStatus; String: ResolverTypeWrapper<Scalars['String']['output']>; @@ -513,9 +539,11 @@ export type ResolversParentTypes = ResolversObject<{ CheckTokenInput: CheckTokenInput; CreateProjectInput: CreateProjectInput; Date: Scalars['Date']['output']; + EmailConfirmationResponse: EmailConfirmationResponse; FetchPublicProjectsInputs: FetchPublicProjectsInputs; Float: Scalars['Float']['output']; ID: Scalars['ID']['output']; + Int: Scalars['Int']['output']; IsValidProjectInput: IsValidProjectInput; LoginResponse: LoginResponse; LoginUserInput: LoginUserInput; @@ -529,6 +557,7 @@ export type ResolversParentTypes = ResolversObject<{ Query: {}; RefreshTokenResponse: RefreshTokenResponse; RegisterUserInput: RegisterUserInput; + ResendEmailInput: ResendEmailInput; String: Scalars['String']['output']; Subscription: {}; UpdateChatTitleInput: UpdateChatTitleInput; @@ -615,6 +644,16 @@ export interface DateScalarConfig name: 'Date'; } +export type EmailConfirmationResponseResolvers< + ContextType = any, + ParentType extends + ResolversParentTypes['EmailConfirmationResponse'] = ResolversParentTypes['EmailConfirmationResponse'], +> = ResolversObject<{ + message?: Resolver<ResolversTypes['String'], ParentType, ContextType>; + success?: Resolver<Maybe<ResolversTypes['Boolean']>, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn<ParentType, ContextType>; +}>; + export type LoginResponseResolvers< ContextType = any, ParentType extends @@ -668,6 +707,12 @@ export type MutationResolvers< ContextType, RequireFields<MutationClearChatHistoryArgs, 'chatId'> >; + confirmEmail?: Resolver< + ResolversTypes['EmailConfirmationResponse'], + ParentType, + ContextType, + RequireFields<MutationConfirmEmailArgs, 'token'> + >; createChat?: Resolver< ResolversTypes['Chat'], ParentType, @@ -722,6 +767,12 @@ export type MutationResolvers< ContextType, RequireFields<MutationRegisterUserArgs, 'input'> >; + resendConfirmationEmail?: Resolver< + ResolversTypes['EmailConfirmationResponse'], + ParentType, + ContextType, + RequireFields<MutationResendConfirmationEmailArgs, 'input'> + >; subscribeToProject?: Resolver< ResolversTypes['Project'], ParentType, @@ -861,6 +912,11 @@ export type QueryResolvers< ContextType, RequireFields<QueryGetProjectArgs, 'projectId'> >; + getRemainingProjectLimit?: Resolver< + ResolversTypes['Int'], + ParentType, + ContextType + >; getSubscribedProjects?: Resolver< Array<ResolversTypes['Project']>, ParentType, @@ -926,6 +982,12 @@ export type UserResolvers< id?: Resolver<ResolversTypes['ID'], ParentType, ContextType>; isActive?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>; isDeleted?: Resolver<ResolversTypes['Boolean'], ParentType, ContextType>; + isEmailConfirmed?: Resolver< + ResolversTypes['Boolean'], + ParentType, + ContextType + >; + lastEmailSendTime?: Resolver<ResolversTypes['Date'], ParentType, ContextType>; projects?: Resolver< Array<ResolversTypes['Project']>, ParentType, @@ -947,6 +1009,7 @@ export type Resolvers<ContextType = any> = ResolversObject<{ ChatCompletionChunkType?: ChatCompletionChunkTypeResolvers<ContextType>; ChatCompletionDeltaType?: ChatCompletionDeltaTypeResolvers<ContextType>; Date?: GraphQLScalarType; + EmailConfirmationResponse?: EmailConfirmationResponseResolvers<ContextType>; LoginResponse?: LoginResponseResolvers<ContextType>; Menu?: MenuResolvers<ContextType>; Message?: MessageResolvers<ContextType>;