From caa36648e25d123555d808054b595aede9465881 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Tue, 25 Nov 2025 23:29:55 -0500 Subject: [PATCH 01/22] Haven't checked yet --- .gitea/workflows/build-gateway.yml | 122 ++++++ .gitea/workflows/build-subgraphs.yml | 77 ++++ .gitea/workflows/build.yml | 63 +++ .gitea/workflows/release.yml | 98 +++++ AGENTS.md => Documentation/AGENTS.md | 0 Documentation/ARCHITECTURE.md | 405 +++++++++++++++++++ Documentation/CICD.md | 220 ++++++++++ Documentation/README.md | 187 +++++++++ FictionArchive.API/Dockerfile | 14 +- FictionArchive.API/FictionArchive.API.csproj | 6 +- FictionArchive.API/build_gateway.py | 20 +- README.md | 10 + docker-compose.yml | 177 ++++++++ fictionarchive-web/Dockerfile | 32 ++ fictionarchive-web/nginx.conf | 21 + 15 files changed, 1441 insertions(+), 11 deletions(-) create mode 100644 .gitea/workflows/build-gateway.yml create mode 100644 .gitea/workflows/build-subgraphs.yml create mode 100644 .gitea/workflows/build.yml create mode 100644 .gitea/workflows/release.yml rename AGENTS.md => Documentation/AGENTS.md (100%) create mode 100644 Documentation/ARCHITECTURE.md create mode 100644 Documentation/CICD.md create mode 100644 Documentation/README.md create mode 100644 README.md create mode 100644 docker-compose.yml create mode 100644 fictionarchive-web/Dockerfile create mode 100644 fictionarchive-web/nginx.conf diff --git a/.gitea/workflows/build-gateway.yml b/.gitea/workflows/build-gateway.yml new file mode 100644 index 0000000..e104d59 --- /dev/null +++ b/.gitea/workflows/build-gateway.yml @@ -0,0 +1,122 @@ +name: Build Gateway + +on: + workflow_dispatch: + push: + branches: + - master + paths: + - 'FictionArchive.API/**' + +env: + REGISTRY: ${{ gitea.server_url }} + IMAGE_NAME: ${{ gitea.repository_owner }}/fictionarchive-api + +jobs: + build-gateway: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Install Fusion CLI + run: dotnet tool install -g HotChocolate.Fusion.CommandLine + + - name: Create subgraphs directory + run: mkdir -p subgraphs + + # Download all subgraph packages from latest successful builds + - name: Download Novel Service subgraph + uses: actions/download-artifact@v4 + with: + name: novel-service-subgraph + path: subgraphs/novel + continue-on-error: true + + - name: Download Translation Service subgraph + uses: actions/download-artifact@v4 + with: + name: translation-service-subgraph + path: subgraphs/translation + continue-on-error: true + + - name: Download Scheduler Service subgraph + uses: actions/download-artifact@v4 + with: + name: scheduler-service-subgraph + path: subgraphs/scheduler + continue-on-error: true + + - name: Download User Service subgraph + uses: actions/download-artifact@v4 + with: + name: user-service-subgraph + path: subgraphs/user + continue-on-error: true + + - name: Download File Service subgraph + uses: actions/download-artifact@v4 + with: + name: file-service-subgraph + path: subgraphs/file + continue-on-error: true + + - name: Configure subgraph URLs for Docker + run: | + for fsp in subgraphs/*/*.fsp; do + if [ -f "$fsp" ]; then + dir=$(dirname "$fsp") + name=$(basename "$dir") + url="http://${name}-service:8080/graphql" + echo "Setting $name URL to $url" + fusion subgraph config set http --url "$url" -c "$fsp" + fi + done + + - name: Compose gateway + run: | + cd FictionArchive.API + rm -f gateway.fgp + for fsp in ../subgraphs/*/*.fsp; do + if [ -f "$fsp" ]; then + echo "Composing: $fsp" + fusion compose -p gateway.fgp -s "$fsp" + fi + done + + - name: Restore dependencies + run: dotnet restore FictionArchive.API/FictionArchive.API.csproj + + - name: Build gateway + run: dotnet build FictionArchive.API/FictionArchive.API.csproj -c Release --no-restore -p:SkipFusionBuild=true + + - name: Run tests + run: dotnet test FictionArchive.sln -c Release --no-build --verbosity normal + continue-on-error: true + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to Gitea Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ gitea.actor }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: FictionArchive.API/Dockerfile + push: true + tags: | + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest + ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ gitea.sha }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.gitea/workflows/build-subgraphs.yml b/.gitea/workflows/build-subgraphs.yml new file mode 100644 index 0000000..c8bc6b4 --- /dev/null +++ b/.gitea/workflows/build-subgraphs.yml @@ -0,0 +1,77 @@ +name: Build Subgraphs + +on: + push: + branches: + - master + paths: + - 'FictionArchive.Service.*/**' + - 'FictionArchive.Common/**' + - 'FictionArchive.Service.Shared/**' + +jobs: + build-subgraphs: + runs-on: ubuntu-latest + strategy: + matrix: + service: + - name: novel-service + project: FictionArchive.Service.NovelService + subgraph: Novel + - name: translation-service + project: FictionArchive.Service.TranslationService + subgraph: Translation + - name: scheduler-service + project: FictionArchive.Service.SchedulerService + subgraph: Scheduler + - name: user-service + project: FictionArchive.Service.UserService + subgraph: User + - name: file-service + project: FictionArchive.Service.FileService + subgraph: File + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Install Fusion CLI + run: dotnet tool install -g HotChocolate.Fusion.CommandLine + + - name: Restore dependencies + run: dotnet restore ${{ matrix.service.project }}/${{ matrix.service.project }}.csproj + + - name: Build + run: dotnet build ${{ matrix.service.project }}/${{ matrix.service.project }}.csproj -c Release --no-restore + + - name: Export schema + run: | + dotnet run --project ${{ matrix.service.project }}/${{ matrix.service.project }}.csproj \ + --no-build -c Release --no-launch-profile \ + -- schema export --output ${{ matrix.service.project }}/schema.graphql + + - name: Pack subgraph + run: fusion subgraph pack -w ${{ matrix.service.project }} + + - name: Upload subgraph package + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.service.name }}-subgraph + path: ${{ matrix.service.project }}/*.fsp + retention-days: 30 + + # Trigger gateway build after all subgraphs are built + trigger-gateway: + runs-on: ubuntu-latest + needs: build-subgraphs + steps: + - name: Trigger gateway workflow + run: | + curl -X POST \ + -H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \ + "${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/actions/workflows/build-gateway.yml/dispatches" \ + -d '{"ref":"master"}' diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..2d59136 --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,63 @@ +name: CI + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build-backend: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install Fusion CLI + run: dotnet tool install -g HotChocolate.Fusion.CommandLine + + - name: Restore dependencies + run: dotnet restore FictionArchive.sln + + - name: Build solution + run: dotnet build FictionArchive.sln --configuration Release --no-restore + + - name: Run tests + run: dotnet test FictionArchive.sln --configuration Release --no-build --verbosity normal + + build-frontend: + runs-on: ubuntu-latest + defaults: + run: + working-directory: fictionarchive-web + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + cache-dependency-path: fictionarchive-web/package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Lint + run: npm run lint + + - name: Build + run: npm run build diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..ca92f7d --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,98 @@ +name: Release + +on: + push: + tags: + - 'v*.*.*' + +env: + REGISTRY: ${{ gitea.server_url }} + IMAGE_PREFIX: ${{ gitea.repository_owner }}/fictionarchive + +jobs: + build-and-push: + runs-on: ubuntu-latest + strategy: + matrix: + service: + - name: api + dockerfile: FictionArchive.API/Dockerfile + - name: novel-service + dockerfile: FictionArchive.Service.NovelService/Dockerfile + - name: user-service + dockerfile: FictionArchive.Service.UserService/Dockerfile + - name: translation-service + dockerfile: FictionArchive.Service.TranslationService/Dockerfile + - name: file-service + dockerfile: FictionArchive.Service.FileService/Dockerfile + - name: scheduler-service + dockerfile: FictionArchive.Service.SchedulerService/Dockerfile + - name: authentication-service + dockerfile: FictionArchive.Service.AuthenticationService/Dockerfile + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Extract version from tag + id: version + run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT + + - name: Log in to Gitea Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ gitea.actor }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + file: ${{ matrix.service.dockerfile }} + push: true + tags: | + ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-${{ matrix.service.name }}:${{ steps.version.outputs.VERSION }} + ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-${{ matrix.service.name }}:latest + cache-from: type=gha + cache-to: type=gha,mode=max + + build-frontend: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Extract version from tag + id: version + run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT + + - name: Log in to Gitea Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ gitea.actor }} + password: ${{ secrets.REGISTRY_TOKEN }} + + - name: Build and push frontend Docker image + uses: docker/build-push-action@v6 + with: + context: ./fictionarchive-web + file: fictionarchive-web/Dockerfile + push: true + build-args: | + VITE_GRAPHQL_URI=${{ vars.VITE_GRAPHQL_URI }} + VITE_OIDC_AUTHORITY=${{ vars.VITE_OIDC_AUTHORITY }} + VITE_OIDC_CLIENT_ID=${{ vars.VITE_OIDC_CLIENT_ID }} + VITE_OIDC_REDIRECT_URI=${{ vars.VITE_OIDC_REDIRECT_URI }} + VITE_OIDC_POST_LOGOUT_REDIRECT_URI=${{ vars.VITE_OIDC_POST_LOGOUT_REDIRECT_URI }} + tags: | + ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-frontend:${{ steps.version.outputs.VERSION }} + ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-frontend:latest + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/AGENTS.md b/Documentation/AGENTS.md similarity index 100% rename from AGENTS.md rename to Documentation/AGENTS.md diff --git a/Documentation/ARCHITECTURE.md b/Documentation/ARCHITECTURE.md new file mode 100644 index 0000000..6cf034b --- /dev/null +++ b/Documentation/ARCHITECTURE.md @@ -0,0 +1,405 @@ +# FictionArchive Architecture Overview + +## High-Level Architecture + +``` +┌────────────────────────────────────────────────────────────────┐ +│ React 19 Frontend │ +│ (Apollo Client, TailwindCSS, OIDC Auth) │ +└───────────────────────────┬────────────────────────────────────┘ + │ GraphQL + ▼ +┌────────────────────────────────────────────────────────────────┐ +│ Hot Chocolate Fusion Gateway │ +│ (FictionArchive.API) │ +└──────┬────────┬────────┬────────┬────────┬─────────────────────┘ + │ │ │ │ │ + ▼ ▼ ▼ ▼ ▼ +┌──────────┐┌──────────┐┌───────────┐┌──────────┐┌──────────────┐ +│ Novel ││ User ││Translation││Scheduler ││ File │ +│ Service ││ Service ││ Service ││ Service ││ Service │ +└────┬─────┘└────┬─────┘└─────┬─────┘└────┬─────┘└──────┬───────┘ + │ │ │ │ │ + └───────────┴────────────┴───────────┴─────────────┘ + │ + ┌────────┴────────┐ + │ RabbitMQ │ + │ (Event Bus) │ + └─────────────────┘ + │ + ┌────────┴────────┐ + │ PostgreSQL │ + │ (per service) │ + └─────────────────┘ +``` + +## Technology Stack + +| Layer | Technology | Version | +|-------|------------|---------| +| Runtime | .NET | 8.0 | +| GraphQL | Hot Chocolate / Fusion | 13+ | +| Database | PostgreSQL | 12+ | +| ORM | Entity Framework Core | 8.0 | +| Message Broker | RabbitMQ | 3.12+ | +| Job Scheduler | Quartz.NET | Latest | +| Object Storage | AWS S3 / Garage | - | +| Date/Time | NodaTime | Latest | +| Frontend | React | 19.2 | +| Frontend Build | Vite | 7.2 | +| GraphQL Client | Apollo Client | 4.0 | +| Auth | OIDC Client TS | 3.4 | +| Styling | TailwindCSS | 3.4 | +| UI Components | Radix UI | Latest | + +## Project Structure + +``` +FictionArchive.sln +├── FictionArchive.Common # Shared enums and extensions +├── FictionArchive.API # GraphQL Fusion Gateway +├── FictionArchive.Service.Shared # Shared infrastructure +├── FictionArchive.Service.NovelService +├── FictionArchive.Service.UserService +├── FictionArchive.Service.TranslationService +├── FictionArchive.Service.FileService +├── FictionArchive.Service.SchedulerService +├── FictionArchive.Service.AuthenticationService +├── FictionArchive.Service.NovelService.Tests +└── fictionarchive-web # React frontend +``` + +## Services + +### FictionArchive.API - GraphQL Fusion Gateway + +- **Role**: Single entry point for all GraphQL queries +- **Port**: 5001 (HTTPS) +- **Endpoints**: + - `/graphql` - GraphQL endpoint + - `/healthz` - Health check +- **Responsibilities**: + - Compose GraphQL schemas from all subgraphs + - Route queries to appropriate services + - CORS policy management + +### FictionArchive.Service.NovelService + +- **Role**: Novel/fiction content management +- **Port**: 8081 (HTTPS) +- **Database**: `FictionArchive_NovelService` +- **GraphQL Operations**: + - `GetNovels` - Paginated, filterable novel listing + - `ImportNovel` - Trigger novel import + - `FetchChapterContents` - Fetch chapter content +- **Models**: Novel, Chapter, Source, NovelTag, Image, LocalizationKey +- **External Integration**: Novelpia adapter +- **Events Published**: `TranslationRequestCreatedEvent`, `FileUploadRequestCreatedEvent` +- **Events Subscribed**: `TranslationRequestCompletedEvent`, `NovelUpdateRequestedEvent`, `ChapterPullRequestedEvent`, `FileUploadRequestStatusUpdateEvent` + +### FictionArchive.Service.UserService + +- **Role**: User identity and profile management +- **Port**: 8081 (HTTPS) +- **Database**: `FictionArchive_UserService` +- **Models**: User (with OAuth provider linking) +- **Events Subscribed**: `AuthUserAddedEvent` + +### FictionArchive.Service.TranslationService + +- **Role**: Text translation orchestration +- **Port**: 8081 (HTTPS) +- **Database**: `FictionArchive_TranslationService` +- **External Integration**: DeepL API +- **Models**: TranslationRequest +- **Events Published**: `TranslationRequestCompletedEvent` +- **Events Subscribed**: `TranslationRequestCreatedEvent` + +### FictionArchive.Service.FileService + +- **Role**: File storage and S3 proxy +- **Port**: 8080 (HTTP) +- **Protocol**: REST only (not GraphQL) +- **Endpoints**: `GET /api/{*path}` - S3 file proxy +- **External Integration**: S3-compatible storage (AWS S3 / Garage) +- **Events Published**: `FileUploadRequestStatusUpdateEvent` +- **Events Subscribed**: `FileUploadRequestCreatedEvent` + +### FictionArchive.Service.SchedulerService + +- **Role**: Job scheduling and automation +- **Port**: 8081 (HTTPS) +- **Database**: `FictionArchive_SchedulerService` +- **Scheduler**: Quartz.NET with persistent job store +- **GraphQL Operations**: `ScheduleEventJob`, `GetScheduledJobs` +- **Models**: SchedulerJob, EventJobTemplate + +### FictionArchive.Service.AuthenticationService + +- **Role**: OAuth/OIDC webhook receiver +- **Port**: 8080 (HTTP) +- **Protocol**: REST only +- **Endpoints**: `POST /api/AuthenticationWebhook/UserRegistered` +- **Events Published**: `AuthUserAddedEvent` +- **No Database** - Stateless webhook handler + +## Communication Patterns + +### GraphQL Federation + +- Hot Chocolate Fusion Gateway composes subgraph schemas +- Schema export automated via `build_gateway.py` +- Each service defines its own Query/Mutation types + +### Event-Driven Architecture (RabbitMQ) + +- Direct exchange: `fiction-archive-event-bus` +- Per-service queues based on `ClientIdentifier` +- Routing key = event class name +- Headers: `X-Created-At`, `X-Event-Id` +- NodaTime JSON serialization + +### Event Flow Examples + +**Novel Import:** +``` +1. Frontend → importNovel mutation +2. NovelService publishes NovelUpdateRequestedEvent +3. NovelUpdateRequestedEventHandler processes +4. Fetches metadata via NovelpiaAdapter +5. Publishes FileUploadRequestCreatedEvent (for cover) +6. FileService uploads to S3 +7. FileService publishes FileUploadRequestStatusUpdateEvent +8. NovelService updates image path +``` + +**Translation:** +``` +1. NovelService publishes TranslationRequestCreatedEvent +2. TranslationService translates via DeepL +3. TranslationService publishes TranslationRequestCompletedEvent +4. NovelService updates chapter translation +``` + +## Data Storage + +### Database Pattern +- Database per service (PostgreSQL) +- Connection string format: `Host=localhost;Database=FictionArchive_{ServiceName};...` +- Auto-migration on startup via `dbContext.UpdateDatabase()` + +### Audit Trail +- `AuditInterceptor` auto-sets `CreatedTime` and `LastUpdatedTime` +- `IAuditable` interface with NodaTime `Instant` fields +- `BaseEntity` abstract base class + +### Object Storage +- S3-compatible (AWS S3 or Garage) +- Path-style URLs for Garage compatibility +- Proxied through FileService + +## Frontend Architecture + +### Structure +``` +fictionarchive-web/ +├── src/ +│ ├── auth/ # OIDC authentication +│ ├── components/ # React components +│ │ └── ui/ # Radix-based primitives +│ ├── pages/ # Route pages +│ ├── layouts/ # Layout components +│ ├── graphql/ # GraphQL queries +│ ├── __generated__/ # Codegen output +│ └── lib/ # Utilities +└── codegen.ts # GraphQL Codegen config +``` + +### Authentication +- OIDC via `oidc-client-ts` +- Environment variables for configuration +- `useAuth` hook for state access + +### State Management +- Apollo Client for GraphQL state +- React Context for auth state + +## Infrastructure + +### Docker +- Multi-stage builds +- Base: `mcr.microsoft.com/dotnet/aspnet:8.0` +- Non-root user for security +- Ports: 8080 (HTTP) or 8081 (HTTPS) + +### Health Checks +- All services expose `/healthz` + +### Configuration +- `appsettings.json` - Default settings +- `appsettings.Development.json` - Dev overrides +- `appsettings.Local.json` - Local secrets (not committed) + +--- + +# Improvement Recommendations + +## Critical + +### 1. Event Bus - No Dead Letter Queue or Retry Logic +**Location**: `FictionArchive.Service.Shared/Services/EventBus/Implementations/RabbitMQEventBus.cs:126-133` + +**Issue**: Events are always ACK'd even on failure. No DLQ configuration for poison messages. Failed events are lost forever. + +**Recommendation**: Implement retry with exponential backoff, dead-letter exchange, and poison message handling. + +```csharp +// Example: Add retry and DLQ +catch (Exception e) +{ + _logger.LogError(e, "Error handling event"); + if (retryCount < maxRetries) + { + await channel.BasicNackAsync(@event.DeliveryTag, false, true); // requeue + } + else + { + // Send to DLQ + await channel.BasicNackAsync(@event.DeliveryTag, false, false); + } +} +``` + +### 2. CORS Configuration is Insecure +**Location**: `FictionArchive.API/Program.cs:24-33` + +**Issue**: `AllowAnyOrigin()` allows requests from any domain, unsuitable for production. + +**Recommendation**: Configure specific allowed origins via appsettings: +```csharp +builder.Services.AddCors(options => +{ + options.AddPolicy("Production", policy => + { + policy.WithOrigins(builder.Configuration.GetSection("Cors:AllowedOrigins").Get()) + .AllowAnyMethod() + .AllowAnyHeader(); + }); +}); +``` + +### 3. Auto-Migration on Startup +**Location**: `FictionArchive.Service.Shared/Services/Database/FictionArchiveDbContext.cs:23-38` + +**Issue**: Running migrations at startup can cause race conditions with multiple instances and potential data corruption during rolling deployments. + +**Recommendation**: Use a migration job, init container, or CLI tool instead of startup code. + +## Important + +### 4. No Circuit Breaker Pattern +**Issue**: External service calls (DeepL, Novelpia, S3) lack resilience patterns. + +**Recommendation**: Add Polly for circuit breaker, retry, and timeout policies: +```csharp +builder.Services.AddHttpClient() + .AddPolicyHandler(GetRetryPolicy()) + .AddPolicyHandler(GetCircuitBreakerPolicy()); +``` + +### 5. Missing Request Validation/Rate Limiting +**Issue**: No visible rate limiting on GraphQL mutations. `ImportNovel` could be abused. + +**Recommendation**: Add rate limiting middleware and input validation. + +### 6. Hardcoded Exchange Name +**Location**: `RabbitMQEventBus.cs:24` + +**Issue**: `fiction-archive-event-bus` is hardcoded. + +**Recommendation**: Move to configuration for environment flexibility. + +### 7. No Distributed Tracing +**Issue**: Event correlation exists (`X-Event-Id` header) but not integrated with tracing. + +**Recommendation**: Add OpenTelemetry for end-to-end request tracing across services. + +### 8. Singleton AuditInterceptor +**Location**: `FictionArchiveDbContext.cs:20` + +**Issue**: `new AuditInterceptor()` created per DbContext instance. + +**Recommendation**: Register as singleton in DI and inject. + +## Minor / Code Quality + +### 9. Limited Test Coverage +**Issue**: Only `NovelService.Tests` exists. No integration tests for event handlers. + +**Recommendation**: Add unit and integration tests for each service, especially event handlers. + +### 10. Inconsistent Port Configuration +**Issue**: Some services use 8080 (HTTP), others 8081 (HTTPS). + +**Recommendation**: Standardize on HTTPS with proper cert management. + +### 11. No API Versioning +**Issue**: GraphQL schemas have no versioning strategy. + +**Recommendation**: Consider schema versioning or deprecation annotations for breaking changes. + +### 12. Frontend - No Error Boundary +**Issue**: React app lacks error boundaries for graceful failure handling. + +**Recommendation**: Add React Error Boundaries around routes. + +### 13. Missing Health Check Depth +**Issue**: Health checks only verify service is running, not dependencies. + +**Recommendation**: Add database, RabbitMQ, and S3 health checks: +```csharp +builder.Services.AddHealthChecks() + .AddNpgSql(connectionString) + .AddRabbitMQ() + .AddS3(options => { }); +``` + +### 14. Synchronous File Operations in Event Handlers +**Issue**: File uploads may block event handling thread for large files. + +**Recommendation**: Consider async streaming for large files. + +## Architectural Suggestions + +### 15. Consider Outbox Pattern +**Issue**: Publishing events and saving to DB aren't transactional, could lead to inconsistent state. + +**Recommendation**: Implement transactional outbox pattern for guaranteed delivery: +``` +1. Save entity + outbox message in same transaction +2. Background worker publishes from outbox +3. Delete outbox message after successful publish +``` + +### 16. Gateway Schema Build Process +**Issue**: Python script (`build_gateway.py`) for schema composition requires manual execution. + +**Recommendation**: Integrate into CI/CD pipeline or consider runtime schema polling. + +### 17. Secret Management +**Issue**: Credentials in appsettings files. + +**Recommendation**: Use Azure Key Vault, AWS Secrets Manager, HashiCorp Vault, or similar secret management solution. + +--- + +## Key Files Reference + +| File | Purpose | +|------|---------| +| `FictionArchive.API/Program.cs` | Gateway setup | +| `FictionArchive.API/build_gateway.py` | Schema composition script | +| `FictionArchive.Service.Shared/Services/EventBus/` | Event bus implementation | +| `FictionArchive.Service.Shared/Extensions/` | Service registration helpers | +| `FictionArchive.Service.Shared/Services/Database/` | DB infrastructure | +| `fictionarchive-web/src/auth/AuthContext.tsx` | Frontend auth state | diff --git a/Documentation/CICD.md b/Documentation/CICD.md new file mode 100644 index 0000000..a2f9c6e --- /dev/null +++ b/Documentation/CICD.md @@ -0,0 +1,220 @@ +# CI/CD Configuration + +This document describes the CI/CD pipeline configuration for FictionArchive using Gitea Actions. + +## Workflows Overview + +| Workflow | File | Trigger | Purpose | +|----------|------|---------|---------| +| CI | `build.yml` | Push/PR to master | Build and test all projects | +| Build Subgraphs | `build-subgraphs.yml` | Push to master (service changes) | Build GraphQL subgraph packages | +| Build Gateway | `build-gateway.yml` | Manual or triggered by subgraphs | Compose gateway and build Docker image | +| Release | `release.yml` | Tag `v*.*.*` | Build and push all Docker images | + +## Pipeline Architecture + +``` +┌─────────────────────────────────────────────────────────────────────┐ +│ Push to master │ +└─────────────────────────────┬───────────────────────────────────────┘ + │ + ┌───────────────┴───────────────┐ + ▼ ▼ +┌─────────────────────────┐ ┌─────────────────────────┐ +│ build.yml │ │ build-subgraphs.yml │ +│ (CI checks - always) │ │ (if service changes) │ +└─────────────────────────┘ └────────────┬────────────┘ + │ + ▼ + ┌─────────────────────────┐ + │ build-gateway.yml │ + │ (compose & push API) │ + └─────────────────────────┘ + +┌─────────────────────────────────────────────────────────────────────┐ +│ Push tag v*.*.* │ +└─────────────────────────────┬───────────────────────────────────────┘ + │ + ▼ + ┌─────────────────────────┐ + │ release.yml │ + │ (build & push all) │ + └─────────────────────────┘ +``` + +## Required Configuration + +### Repository Secrets + +Configure these in **Settings → Actions → Secrets**: + +| Secret | Description | Required By | +|--------|-------------|-------------| +| `REGISTRY_TOKEN` | Gitea access token with `write:package` scope | `release.yml`, `build-gateway.yml` | +| `GITEA_TOKEN` | Gitea access token for API calls | `build-subgraphs.yml` | + +#### Creating Access Tokens + +1. Go to **Settings → Applications → Access Tokens** +2. Create a new token with the following scopes: + - `write:package` - Push container images + - `write:repository` - Trigger workflows via API +3. Copy the token and add it as a repository secret + +### Repository Variables + +Configure these in **Settings → Actions → Variables**: + +| Variable | Description | Example | Required By | +|----------|-------------|---------|-------------| +| `VITE_GRAPHQL_URI` | GraphQL API endpoint URL | `https://api.fictionarchive.example.com/graphql/` | `release.yml` | +| `VITE_OIDC_AUTHORITY` | OIDC provider authority URL | `https://auth.example.com/application/o/fiction-archive/` | `release.yml` | +| `VITE_OIDC_CLIENT_ID` | OIDC client identifier | `your-client-id` | `release.yml` | +| `VITE_OIDC_REDIRECT_URI` | Post-login redirect URL | `https://fictionarchive.example.com/` | `release.yml` | +| `VITE_OIDC_POST_LOGOUT_REDIRECT_URI` | Post-logout redirect URL | `https://fictionarchive.example.com/` | `release.yml` | + +## Workflow Details + +### CI (`build.yml`) + +**Trigger:** Push or pull request to `master` + +**Jobs:** +1. `build-backend` - Builds .NET solution and runs tests +2. `build-frontend` - Builds React application with linting + +**Requirements:** +- .NET 8.0 SDK +- Python 3.12 +- Node.js 20 +- HotChocolate Fusion CLI + +### Build Subgraphs (`build-subgraphs.yml`) + +**Trigger:** Push to `master` with changes in: +- `FictionArchive.Service.*/**` +- `FictionArchive.Common/**` +- `FictionArchive.Service.Shared/**` + +**Jobs:** +1. `build-subgraphs` - Matrix job building each service's `.fsp` package +2. `trigger-gateway` - Triggers gateway rebuild via API + +**Subgraphs Built:** +- Novel Service +- Translation Service +- Scheduler Service +- User Service +- File Service + +**Artifacts:** Each subgraph produces a `.fsp` file retained for 30 days. + +### Build Gateway (`build-gateway.yml`) + +**Trigger:** +- Manual dispatch (`workflow_dispatch`) +- Push to `master` with changes in `FictionArchive.API/**` +- Triggered by `build-subgraphs.yml` completion + +**Process:** +1. Downloads all subgraph `.fsp` artifacts +2. Configures Docker-internal URLs for each subgraph +3. Composes gateway schema using Fusion CLI +4. Builds and pushes API Docker image + +**Image Tags:** +- `//fictionarchive-api:latest` +- `//fictionarchive-api:` + +### Release (`release.yml`) + +**Trigger:** Push tag matching `v*.*.*` (e.g., `v1.0.0`) + +**Jobs:** +1. `build-and-push` - Matrix job building all backend service images +2. `build-frontend` - Builds and pushes frontend image + +**Services Built:** +- `fictionarchive-api` +- `fictionarchive-novel-service` +- `fictionarchive-user-service` +- `fictionarchive-translation-service` +- `fictionarchive-file-service` +- `fictionarchive-scheduler-service` +- `fictionarchive-authentication-service` +- `fictionarchive-frontend` + +**Image Tags:** +- `//fictionarchive-:` +- `//fictionarchive-:latest` + +## Container Registry + +Images are pushed to the Gitea Container Registry at: +``` +//fictionarchive-: +``` + +### Pulling Images + +```bash +# Login to registry +docker login -u -p + +# Pull an image +docker pull //fictionarchive-api:latest +``` + +## Creating a Release + +1. Ensure all changes are committed and pushed to `master` +2. Create and push a version tag: + ```bash + git tag v1.0.0 + git push origin v1.0.0 + ``` +3. The release workflow will automatically build and push all images +4. Monitor progress in **Actions** tab + +## Troubleshooting + +### Build Failures + +**"REGISTRY_TOKEN secret not found"** +- Ensure the `REGISTRY_TOKEN` secret is configured in repository settings +- Verify the token has `write:package` scope + +**"Failed to trigger gateway workflow"** +- Ensure `GITEA_TOKEN` secret is configured +- Verify the token has `write:repository` scope + +**"No subgraph artifacts found"** +- The gateway build requires subgraph artifacts from a previous `build-subgraphs` run +- Trigger `build-subgraphs.yml` manually or push a change to a service + +### Frontend Build Failures + +**"VITE_* variables are empty"** +- Ensure all required variables are configured in repository settings +- Variables use `vars.*` context, not `secrets.*` + +### Docker Push Failures + +**"unauthorized: authentication required"** +- Verify `REGISTRY_TOKEN` has correct permissions +- Check that the token hasn't expired + +## Local Testing + +To test workflows locally before pushing: + +```bash +# Install act (GitHub Actions local runner) +# Note: act has partial Gitea Actions compatibility + +# Run CI workflow +act push -W .gitea/workflows/build.yml + +# Run with specific event +act push --eventpath .gitea/test-event.json +``` diff --git a/Documentation/README.md b/Documentation/README.md new file mode 100644 index 0000000..1b80ddb --- /dev/null +++ b/Documentation/README.md @@ -0,0 +1,187 @@ +# FictionArchive + +A distributed microservices-based web application for managing fiction and novel content. Features include importing from external sources, multi-language translation, file storage, and user management. + +## Architecture + +FictionArchive uses a GraphQL Fusion gateway pattern to orchestrate multiple domain services with event-driven communication via RabbitMQ. +More information available in [ARCHITECTURE.md](ARCHITECTURE.md) + +## Prerequisites + +- .NET SDK 8.0+ +- Node.js 20+ +- Python 3 (for gateway build script) +- Docker & Docker Compose +- PostgreSQL 16+ +- RabbitMQ 3+ + +**Required CLI Tools** +```bash +# Hot Chocolate Fusion CLI +dotnet tool install -g HotChocolate.Fusion.CommandLine +``` + +## Getting Started + +### Local Development + +1. **Clone the repository** + ```bash + git clone + cd FictionArchive + ``` + +2. **Start infrastructure** (PostgreSQL, RabbitMQ) + ```bash + docker compose up -d postgres rabbitmq + ``` + +3. **Build and run backend** + ```bash + dotnet restore + dotnet build FictionArchive.sln + + # Start services (in separate terminals or use a process manager) + dotnet run --project FictionArchive.Service.NovelService + dotnet run --project FictionArchive.Service.UserService + dotnet run --project FictionArchive.Service.TranslationService + dotnet run --project FictionArchive.Service.FileService + dotnet run --project FictionArchive.Service.SchedulerService + dotnet run --project FictionArchive.Service.AuthenticationService + + # Start the gateway (builds fusion schema automatically) + dotnet run --project FictionArchive.API + ``` + +4. **Build and run frontend** + ```bash + cd fictionarchive-web + npm install + npm run codegen # Generate GraphQL types + npm run dev # Start dev server at http://localhost:5173 + ``` + +### Docker Deployment + +1. **Create environment file** + ```bash + cp .env.example .env + # Edit .env with your configuration + ``` + +2. **Start all services** + ```bash + docker compose up -d + ``` + +## Configuration + +### Environment Variables + +Create a `.env` file in the project root: + +```bash +# PostgreSQL +POSTGRES_USER=postgres +POSTGRES_PASSWORD=your-secure-password + +# RabbitMQ +RABBITMQ_USER=guest +RABBITMQ_PASSWORD=your-secure-password + +# External Services +NOVELPIA_USERNAME=your-username +NOVELPIA_PASSWORD=your-password +DEEPL_API_KEY=your-api-key + +# S3 Storage +S3_ENDPOINT=https://s3.example.com +S3_BUCKET=fictionarchive +S3_ACCESS_KEY=your-access-key +S3_SECRET_KEY=your-secret-key + +# OIDC Authentication +OIDC_AUTHORITY=https://auth.example.com/application/o/fiction-archive/ +OIDC_CLIENT_ID=your-client-id +``` + +### Frontend Environment + +Create `fictionarchive-web/.env.local`: + +```bash +VITE_GRAPHQL_URI=http://localhost:5234/graphql/ +VITE_OIDC_AUTHORITY=https://auth.example.com/application/o/fiction-archive/ +VITE_OIDC_CLIENT_ID=your-client-id +VITE_OIDC_REDIRECT_URI=http://localhost:5173/ +VITE_OIDC_POST_LOGOUT_REDIRECT_URI=http://localhost:5173/ +``` + +## Building the GraphQL Gateway + +The API gateway uses Hot Chocolate Fusion to compose schemas from all subgraphs. The gateway schema is rebuilt automatically when building the API project. + +**Manual rebuild:** +```bash +cd FictionArchive.API +python build_gateway.py +``` + +**Skip specific services** by adding them to `FictionArchive.API/gateway_skip.txt`: +``` +FictionArchive.Service.NovelService.Tests +``` + +## CI/CD + +The project uses Gitea Actions with the following workflows: + +| Workflow | Trigger | Description | +|----------|---------|-------------| +| `build.yml` | Push/PR to master | CI checks - builds and tests | +| `build-subgraphs.yml` | Service changes on master | Builds subgraph `.fsp` packages | +| `build-gateway.yml` | Gateway changes or subgraph builds | Composes gateway and builds Docker image | +| `release.yml` | Tag `v*.*.*` | Builds and pushes all Docker images | + +### Release Process + +```bash +git tag v1.0.0 +git push origin v1.0.0 +``` + +## Project Structure + +``` +FictionArchive/ +├── FictionArchive.sln +├── FictionArchive.Common/ # Shared enums and extensions +├── FictionArchive.Service.Shared/ # Shared infrastructure (EventBus, DB) +├── FictionArchive.API/ # GraphQL Fusion Gateway +├── FictionArchive.Service.NovelService/ +├── FictionArchive.Service.UserService/ +├── FictionArchive.Service.TranslationService/ +├── FictionArchive.Service.FileService/ +├── FictionArchive.Service.SchedulerService/ +├── FictionArchive.Service.AuthenticationService/ +├── FictionArchive.Service.NovelService.Tests/ +├── fictionarchive-web/ # React frontend +├── docker-compose.yml +└── .gitea/workflows/ # CI/CD workflows +``` + +## Testing + +```bash +# Run all tests +dotnet test FictionArchive.sln + +# Run specific test project +dotnet test FictionArchive.Service.NovelService.Tests +``` + +## Documentation + +- [ARCHITECTURE.md](ARCHITECTURE.md) - Detailed architecture documentation +- [AGENTS.md](AGENTS.md) - Development guidelines and coding standards diff --git a/FictionArchive.API/Dockerfile b/FictionArchive.API/Dockerfile index 18cacd4..e5b1972 100644 --- a/FictionArchive.API/Dockerfile +++ b/FictionArchive.API/Dockerfile @@ -7,15 +7,23 @@ EXPOSE 8081 FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build ARG BUILD_CONFIGURATION=Release WORKDIR /src + COPY ["FictionArchive.API/FictionArchive.API.csproj", "FictionArchive.API/"] +COPY ["FictionArchive.Common/FictionArchive.Common.csproj", "FictionArchive.Common/"] +COPY ["FictionArchive.Service.Shared/FictionArchive.Service.Shared.csproj", "FictionArchive.Service.Shared/"] RUN dotnet restore "FictionArchive.API/FictionArchive.API.csproj" -COPY . . + +COPY FictionArchive.API/ FictionArchive.API/ +COPY FictionArchive.Common/ FictionArchive.Common/ +COPY FictionArchive.Service.Shared/ FictionArchive.Service.Shared/ + WORKDIR "/src/FictionArchive.API" -RUN dotnet build "./FictionArchive.API.csproj" -c $BUILD_CONFIGURATION -o /app/build +# Skip fusion build - gateway.fgp should be pre-composed in CI +RUN dotnet build "./FictionArchive.API.csproj" -c $BUILD_CONFIGURATION -o /app/build -p:SkipFusionBuild=true FROM build AS publish ARG BUILD_CONFIGURATION=Release -RUN dotnet publish "./FictionArchive.API.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false +RUN dotnet publish "./FictionArchive.API.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false /p:SkipFusionBuild=true FROM base AS final WORKDIR /app diff --git a/FictionArchive.API/FictionArchive.API.csproj b/FictionArchive.API/FictionArchive.API.csproj index 752aa54..73638d7 100644 --- a/FictionArchive.API/FictionArchive.API.csproj +++ b/FictionArchive.API/FictionArchive.API.csproj @@ -22,9 +22,9 @@ - - - + + + diff --git a/FictionArchive.API/build_gateway.py b/FictionArchive.API/build_gateway.py index cb6f68e..a345342 100644 --- a/FictionArchive.API/build_gateway.py +++ b/FictionArchive.API/build_gateway.py @@ -1,12 +1,22 @@ #!/usr/bin/env python3 +""" +Local development script for building the Fusion gateway. + +This script is used for local development only. In CI/CD, subgraphs are built +separately and the gateway is composed from pre-built .fsp artifacts. + +Usage: + python build_gateway.py + +Requirements: + - .NET 8.0 SDK + - HotChocolate Fusion CLI (dotnet tool install -g HotChocolate.Fusion.CommandLine) +""" import subprocess import sys import os from pathlib import Path -# ---------------------------------------- -# Helpers -# ---------------------------------------- def run(cmd, cwd=None): """Run a command and exit on failure.""" @@ -19,7 +29,7 @@ def run(cmd, cwd=None): def load_skip_list(skip_file: Path): if not skip_file.exists(): - print(f"WARNING: skip-projects.txt not found at {skip_file}") + print(f"WARNING: gateway_skip.txt not found at {skip_file}") return set() lines = skip_file.read_text().splitlines() @@ -53,7 +63,7 @@ print("----------------------------------------") service_dirs = [ d for d in services_dir.glob("FictionArchive.Service.*") - if d.is_dir() + if d.is_dir() and (d / "subgraph-config.json").exists() ] selected_services = [] diff --git a/README.md b/README.md new file mode 100644 index 0000000..bec8f84 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# FictionArchive + +A distributed microservices-based web application for managing fiction and novel content. + +## Documentation + +- [README](Documentation/README.md) - Getting started and project overview +- [ARCHITECTURE](Documentation/ARCHITECTURE.md) - System architecture and design +- [CICD](Documentation/CICD.md) - CI/CD pipeline configuration +- [AGENTS](Documentation/AGENTS.md) - Development guidelines and coding standards diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..91f0e5c --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,177 @@ +services: + # =========================================== + # Infrastructure + # =========================================== + postgres: + image: postgres:16-alpine + environment: + POSTGRES_USER: ${POSTGRES_USER:-postgres} + POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-postgres} + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 5s + timeout: 5s + retries: 5 + restart: unless-stopped + + rabbitmq: + image: rabbitmq:3-management-alpine + environment: + RABBITMQ_DEFAULT_USER: ${RABBITMQ_USER:-guest} + RABBITMQ_DEFAULT_PASS: ${RABBITMQ_PASSWORD:-guest} + volumes: + - rabbitmq_data:/var/lib/rabbitmq + healthcheck: + test: ["CMD", "rabbitmq-diagnostics", "check_running"] + interval: 10s + timeout: 5s + retries: 5 + restart: unless-stopped + + # =========================================== + # Backend Services + # =========================================== + novel-service: + build: + context: . + dockerfile: FictionArchive.Service.NovelService/Dockerfile + environment: + ConnectionStrings__DefaultConnection: Host=postgres;Database=FictionArchive_NovelService;Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-postgres} + ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq + Novelpia__Username: ${NOVELPIA_USERNAME} + Novelpia__Password: ${NOVELPIA_PASSWORD} + NovelUpdateService__PendingImageUrl: https://files.fictionarchive.orfl.xyz/api/pendingupload.png + depends_on: + postgres: + condition: service_healthy + rabbitmq: + condition: service_healthy + restart: unless-stopped + + translation-service: + build: + context: . + dockerfile: FictionArchive.Service.TranslationService/Dockerfile + environment: + ConnectionStrings__DefaultConnection: Host=postgres;Database=FictionArchive_TranslationService;Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-postgres} + ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq + DeepL__ApiKey: ${DEEPL_API_KEY} + depends_on: + postgres: + condition: service_healthy + rabbitmq: + condition: service_healthy + restart: unless-stopped + + scheduler-service: + build: + context: . + dockerfile: FictionArchive.Service.SchedulerService/Dockerfile + environment: + ConnectionStrings__DefaultConnection: Host=postgres;Database=FictionArchive_SchedulerService;Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-postgres} + ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq + depends_on: + postgres: + condition: service_healthy + rabbitmq: + condition: service_healthy + restart: unless-stopped + + user-service: + build: + context: . + dockerfile: FictionArchive.Service.UserService/Dockerfile + environment: + ConnectionStrings__DefaultConnection: Host=postgres;Database=FictionArchive_UserService;Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-postgres} + ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq + depends_on: + postgres: + condition: service_healthy + rabbitmq: + condition: service_healthy + restart: unless-stopped + + authentication-service: + build: + context: . + dockerfile: FictionArchive.Service.AuthenticationService/Dockerfile + environment: + ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq + depends_on: + rabbitmq: + condition: service_healthy + restart: unless-stopped + + file-service: + build: + context: . + dockerfile: FictionArchive.Service.FileService/Dockerfile + environment: + ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq + S3__Endpoint: ${S3_ENDPOINT:-https://s3.orfl.xyz} + S3__Bucket: ${S3_BUCKET:-fictionarchive} + S3__AccessKey: ${S3_ACCESS_KEY} + S3__SecretKey: ${S3_SECRET_KEY} + Proxy__BaseUrl: https://files.orfl.xyz/api + labels: + - "traefik.enable=true" + - "traefik.http.routers.file-service.rule=Host(`files.orfl.xyz`)" + - "traefik.http.routers.file-service.entrypoints=websecure" + - "traefik.http.routers.file-service.tls.certresolver=letsencrypt" + - "traefik.http.services.file-service.loadbalancer.server.port=8080" + depends_on: + rabbitmq: + condition: service_healthy + restart: unless-stopped + + # =========================================== + # API Gateway + # =========================================== + api-gateway: + build: + context: . + dockerfile: FictionArchive.API/Dockerfile + environment: + ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq + labels: + - "traefik.enable=true" + - "traefik.http.routers.api-gateway.rule=Host(`api.fictionarchive.orfl.xyz`)" + - "traefik.http.routers.api-gateway.entrypoints=websecure" + - "traefik.http.routers.api-gateway.tls.certresolver=letsencrypt" + - "traefik.http.services.api-gateway.loadbalancer.server.port=8080" + depends_on: + - novel-service + - translation-service + - scheduler-service + - user-service + - authentication-service + - file-service + restart: unless-stopped + + # =========================================== + # Frontend + # =========================================== + frontend: + build: + context: ./fictionarchive-web + dockerfile: Dockerfile + args: + VITE_GRAPHQL_URI: https://api.fictionarchive.orfl.xyz/graphql/ + VITE_OIDC_AUTHORITY: ${OIDC_AUTHORITY:-https://auth.orfl.xyz/application/o/fiction-archive/} + VITE_OIDC_CLIENT_ID: ${OIDC_CLIENT_ID} + VITE_OIDC_REDIRECT_URI: https://fictionarchive.orfl.xyz/ + VITE_OIDC_POST_LOGOUT_REDIRECT_URI: https://fictionarchive.orfl.xyz/ + labels: + - "traefik.enable=true" + - "traefik.http.routers.frontend.rule=Host(`fictionarchive.orfl.xyz`)" + - "traefik.http.routers.frontend.entrypoints=websecure" + - "traefik.http.routers.frontend.tls.certresolver=letsencrypt" + - "traefik.http.services.frontend.loadbalancer.server.port=80" + restart: unless-stopped + +volumes: + postgres_data: + rabbitmq_data: + letsencrypt: diff --git a/fictionarchive-web/Dockerfile b/fictionarchive-web/Dockerfile new file mode 100644 index 0000000..9b9a813 --- /dev/null +++ b/fictionarchive-web/Dockerfile @@ -0,0 +1,32 @@ +FROM node:20-alpine AS build + +WORKDIR /app + +# Build arguments for Vite environment variables +ARG VITE_GRAPHQL_URI +ARG VITE_OIDC_AUTHORITY +ARG VITE_OIDC_CLIENT_ID +ARG VITE_OIDC_REDIRECT_URI +ARG VITE_OIDC_POST_LOGOUT_REDIRECT_URI + +# Set environment variables for build +ENV VITE_GRAPHQL_URI=$VITE_GRAPHQL_URI +ENV VITE_OIDC_AUTHORITY=$VITE_OIDC_AUTHORITY +ENV VITE_OIDC_CLIENT_ID=$VITE_OIDC_CLIENT_ID +ENV VITE_OIDC_REDIRECT_URI=$VITE_OIDC_REDIRECT_URI +ENV VITE_OIDC_POST_LOGOUT_REDIRECT_URI=$VITE_OIDC_POST_LOGOUT_REDIRECT_URI + +COPY package*.json ./ +RUN npm ci + +COPY . . +RUN npm run build + +FROM nginx:alpine + +COPY --from=build /app/dist /usr/share/nginx/html +COPY nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 80 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/fictionarchive-web/nginx.conf b/fictionarchive-web/nginx.conf new file mode 100644 index 0000000..da7ddcb --- /dev/null +++ b/fictionarchive-web/nginx.conf @@ -0,0 +1,21 @@ +server { + listen 80; + server_name localhost; + root /usr/share/nginx/html; + index index.html; + + # Gzip compression + gzip on; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + + # Handle SPA routing - serve index.html for all routes + location / { + try_files $uri $uri/ /index.html; + } + + # Cache static assets + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } +} -- 2.49.1 From 200bdaabed538561eb912fdec0966412cdb07ca8 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Tue, 25 Nov 2025 23:45:53 -0500 Subject: [PATCH 02/22] [FA-11] Try to add Claude assistant --- .gitea/workflows/claude_assistant.yml | 43 +++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .gitea/workflows/claude_assistant.yml diff --git a/.gitea/workflows/claude_assistant.yml b/.gitea/workflows/claude_assistant.yml new file mode 100644 index 0000000..dcb1158 --- /dev/null +++ b/.gitea/workflows/claude_assistant.yml @@ -0,0 +1,43 @@ +name: Claude Assistant for Gitea + +on: + # Trigger on issue comments (works on both issues and pull requests in Gitea) + issue_comment: + types: [created] + # Trigger on issues being opened or assigned + issues: + types: [opened, assigned] + # Note: pull_request_review_comment has limited support in Gitea + # Use issue_comment instead which covers PR comments + +jobs: + claude-assistant: + # Basic trigger detection - check for @claude in comments or issue body + if: | + (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || github.event.action == 'assigned')) + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + issues: write + # Note: Gitea Actions may not require id-token: write for basic functionality + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Run Claude Assistant + uses: markwylde/claude-code-gitea-action + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + gitea_token: ${{ secrets.CLAUDE_GITEA_TOKEN }} + timeout_minutes: "60" + trigger_phrase: "@claude" + # Optional: Customize for Gitea environment + custom_instructions: | + You are working in a Gitea environment. Be aware that: + - Some GitHub Actions features may behave differently + - Focus on core functionality and avoid advanced GitHub-specific features + - Use standard git operations when possible \ No newline at end of file -- 2.49.1 From 62e7e20f943ceb5594f32d6178bccb8294d2d624 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 00:18:33 -0500 Subject: [PATCH 03/22] [FA-11] Fix issue with local package reference --- FictionArchive.Common/FictionArchive.Common.csproj | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/FictionArchive.Common/FictionArchive.Common.csproj b/FictionArchive.Common/FictionArchive.Common.csproj index a4cbc9a..bf3ccde 100644 --- a/FictionArchive.Common/FictionArchive.Common.csproj +++ b/FictionArchive.Common/FictionArchive.Common.csproj @@ -9,14 +9,8 @@ + - - - - ..\..\..\..\..\..\Program Files\dotnet\shared\Microsoft.AspNetCore.App\8.0.15\Microsoft.Extensions.Hosting.Abstractions.dll - - - -- 2.49.1 From ba99642e973ba937019046e1a9c4fcad33793852 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 00:40:07 -0500 Subject: [PATCH 04/22] [FA-11] Fix build errors, try to fix cache miss on node build --- .gitea/workflows/build-subgraphs.yml | 4 ++-- .gitea/workflows/build.yml | 2 +- fictionarchive-web/eslint.config.js | 2 +- fictionarchive-web/src/apolloClient.ts | 2 +- fictionarchive-web/src/auth/AuthContext.tsx | 4 ++-- fictionarchive-web/src/components/ui/badge.tsx | 1 + fictionarchive-web/src/components/ui/button.tsx | 1 + fictionarchive-web/src/components/ui/input.tsx | 3 +-- fictionarchive-web/src/pages/NovelsPage.tsx | 5 ++--- 9 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.gitea/workflows/build-subgraphs.yml b/.gitea/workflows/build-subgraphs.yml index c8bc6b4..54f6751 100644 --- a/.gitea/workflows/build-subgraphs.yml +++ b/.gitea/workflows/build-subgraphs.yml @@ -50,8 +50,8 @@ jobs: - name: Export schema run: | - dotnet run --project ${{ matrix.service.project }}/${{ matrix.service.project }}.csproj \ - --no-build -c Release --no-launch-profile \ + dotnet run -c Release --no-build --no-launch-profile \ + --project ${{ matrix.service.project }}/${{ matrix.service.project }}.csproj \ -- schema export --output ${{ matrix.service.project }}/schema.graphql - name: Pack subgraph diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 2d59136..e71146a 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -47,7 +47,7 @@ jobs: uses: actions/checkout@v4 - name: Setup Node.js - uses: actions/setup-node@v4 + uses: actions/setup-node@v6.0.0 with: node-version: '20' cache: 'npm' diff --git a/fictionarchive-web/eslint.config.js b/fictionarchive-web/eslint.config.js index 5e6b472..7dd669d 100644 --- a/fictionarchive-web/eslint.config.js +++ b/fictionarchive-web/eslint.config.js @@ -6,7 +6,7 @@ import tseslint from 'typescript-eslint' import { defineConfig, globalIgnores } from 'eslint/config' export default defineConfig([ - globalIgnores(['dist']), + globalIgnores(['dist', 'src/__generated__']), { files: ['**/*.{ts,tsx}'], extends: [ diff --git a/fictionarchive-web/src/apolloClient.ts b/fictionarchive-web/src/apolloClient.ts index 1bc87ed..058e4e0 100644 --- a/fictionarchive-web/src/apolloClient.ts +++ b/fictionarchive-web/src/apolloClient.ts @@ -6,7 +6,7 @@ const uri = import.meta.env.VITE_GRAPHQL_URI ?? 'https://localhost:5001/graphql' const httpLink = new HttpLink({ uri }) -const authLink = new SetContextLink(async ({ headers }, _) => { +const authLink = new SetContextLink(async ({ headers }) => { if (!userManager) return { headers } try { const user = await userManager.getUser() diff --git a/fictionarchive-web/src/auth/AuthContext.tsx b/fictionarchive-web/src/auth/AuthContext.tsx index d3d781d..6ccb637 100644 --- a/fictionarchive-web/src/auth/AuthContext.tsx +++ b/fictionarchive-web/src/auth/AuthContext.tsx @@ -14,12 +14,11 @@ const AuthContext = createContext(undefined) export function AuthProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState(null) - const [isLoading, setIsLoading] = useState(true) + const [isLoading, setIsLoading] = useState(!!userManager) const callbackHandledRef = useRef(false) useEffect(() => { if (!userManager) { - setIsLoading(false) return } @@ -121,6 +120,7 @@ export function AuthProvider({ children }: { children: ReactNode }) { return {children} } +// eslint-disable-next-line react-refresh/only-export-components export function useAuth() { const context = useContext(AuthContext) if (!context) { diff --git a/fictionarchive-web/src/components/ui/badge.tsx b/fictionarchive-web/src/components/ui/badge.tsx index 8cf516f..fa47ef3 100644 --- a/fictionarchive-web/src/components/ui/badge.tsx +++ b/fictionarchive-web/src/components/ui/badge.tsx @@ -31,4 +31,5 @@ function Badge({ className, variant, ...props }: BadgeProps) { ) } +// eslint-disable-next-line react-refresh/only-export-components export { Badge, badgeVariants } diff --git a/fictionarchive-web/src/components/ui/button.tsx b/fictionarchive-web/src/components/ui/button.tsx index fb25275..ab026f2 100644 --- a/fictionarchive-web/src/components/ui/button.tsx +++ b/fictionarchive-web/src/components/ui/button.tsx @@ -51,4 +51,5 @@ const Button = React.forwardRef( ) Button.displayName = 'Button' +// eslint-disable-next-line react-refresh/only-export-components export { Button, buttonVariants } diff --git a/fictionarchive-web/src/components/ui/input.tsx b/fictionarchive-web/src/components/ui/input.tsx index dfff7a2..3e75349 100644 --- a/fictionarchive-web/src/components/ui/input.tsx +++ b/fictionarchive-web/src/components/ui/input.tsx @@ -2,8 +2,7 @@ import * as React from 'react' import { cn } from '../../lib/utils' -export interface InputProps - extends React.InputHTMLAttributes {} +export type InputProps = React.InputHTMLAttributes const Input = React.forwardRef( ({ className, type, ...props }, ref) => { diff --git a/fictionarchive-web/src/pages/NovelsPage.tsx b/fictionarchive-web/src/pages/NovelsPage.tsx index 1b56ef5..21171ca 100644 --- a/fictionarchive-web/src/pages/NovelsPage.tsx +++ b/fictionarchive-web/src/pages/NovelsPage.tsx @@ -13,14 +13,13 @@ export function NovelsPage() { notifyOnNetworkStatusChange: true, }) - const edges = data?.novels?.edges ?? [] const pageInfo = data?.novels?.pageInfo const hasNextPage = pageInfo?.hasNextPage ?? false const endCursor = pageInfo?.endCursor ?? null const novels = useMemo( - () => edges.map((edge) => edge?.node).filter(Boolean), - [edges] + () => (data?.novels?.edges ?? []).map((edge) => edge?.node).filter(Boolean), + [data?.novels?.edges] ) async function handleLoadMore() { -- 2.49.1 From e9eaf1569b266bfc4d2652d183bd338e19890d55 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 00:49:27 -0500 Subject: [PATCH 05/22] [FA-11] Disable Node caching all together and let backend rebuild if needed --- .gitea/workflows/build-subgraphs.yml | 2 +- .gitea/workflows/build.yml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/build-subgraphs.yml b/.gitea/workflows/build-subgraphs.yml index 54f6751..4c945f6 100644 --- a/.gitea/workflows/build-subgraphs.yml +++ b/.gitea/workflows/build-subgraphs.yml @@ -50,7 +50,7 @@ jobs: - name: Export schema run: | - dotnet run -c Release --no-build --no-launch-profile \ + dotnet run -c Release --no-launch-profile \ --project ${{ matrix.service.project }}/${{ matrix.service.project }}.csproj \ -- schema export --output ${{ matrix.service.project }}/schema.graphql diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index e71146a..7680f0c 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -52,6 +52,7 @@ jobs: node-version: '20' cache: 'npm' cache-dependency-path: fictionarchive-web/package-lock.json + package-manager-cache: false - name: Install dependencies run: npm ci -- 2.49.1 From cdc2176e35a554bb169c63fc3ca045b709aedc88 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 07:08:32 -0500 Subject: [PATCH 06/22] [FA-11] Try and disable the caching again, forgot a step like an idiot --- .gitea/workflows/build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 7680f0c..084dc33 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -50,8 +50,7 @@ jobs: uses: actions/setup-node@v6.0.0 with: node-version: '20' - cache: 'npm' - cache-dependency-path: fictionarchive-web/package-lock.json + cache: 'none' package-manager-cache: false - name: Install dependencies -- 2.49.1 From 573f3fc7b006434292f15c0cbb79e4f4c9e16cb3 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 07:11:40 -0500 Subject: [PATCH 07/22] [FA-11] That causes an error so fingers crossed this time --- .gitea/workflows/build.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 084dc33..83eecff 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -50,7 +50,6 @@ jobs: uses: actions/setup-node@v6.0.0 with: node-version: '20' - cache: 'none' package-manager-cache: false - name: Install dependencies -- 2.49.1 From 0180a580844ff6edbb59f9a72e1ea8e00215aa22 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 07:26:57 -0500 Subject: [PATCH 08/22] [FA-11] Hopefully resolves build issues, although I don't know why the build_gateway was necessarily failing in build.yml and trying to access Debug bins --- .gitea/workflows/build.yml | 2 +- fictionarchive-web/.gitignore | 3 - fictionarchive-web/README.md | 6 +- fictionarchive-web/package.json | 1 - .../src/__generated__/graphql.ts | 838 ++++++++++++++++++ 5 files changed, 842 insertions(+), 8 deletions(-) create mode 100644 fictionarchive-web/src/__generated__/graphql.ts diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 83eecff..5aafb71 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -32,7 +32,7 @@ jobs: run: dotnet restore FictionArchive.sln - name: Build solution - run: dotnet build FictionArchive.sln --configuration Release --no-restore + run: dotnet build FictionArchive.sln --configuration Release --no-restore /p:SkipFusionBuild=true - name: Run tests run: dotnet test FictionArchive.sln --configuration Release --no-build --verbosity normal diff --git a/fictionarchive-web/.gitignore b/fictionarchive-web/.gitignore index 5e65573..a547bf3 100644 --- a/fictionarchive-web/.gitignore +++ b/fictionarchive-web/.gitignore @@ -12,9 +12,6 @@ dist dist-ssr *.local -# Generated GraphQL artifacts -src/__generated__/ - # Editor directories and files .vscode/* !.vscode/extensions.json diff --git a/fictionarchive-web/README.md b/fictionarchive-web/README.md index ff73def..03b9e20 100644 --- a/fictionarchive-web/README.md +++ b/fictionarchive-web/README.md @@ -25,8 +25,8 @@ VITE_CODEGEN_TOKEN=your_api_token ## Scripts - `npm run dev`: start Vite dev server. -- `npm run build`: type-check + build (runs codegen first via `prebuild`). -- `npm run codegen`: generate typed hooks from `src/**/*.graphql` into `src/__generated__/graphql.ts`. +- `npm run build`: type-check + production build. +- `npm run codegen`: generate typed hooks from `src/**/*.graphql` into `src/__generated__/graphql.ts`. **Run this manually after changing GraphQL operations or when the gateway schema changes.** ## Project notes @@ -39,4 +39,4 @@ VITE_CODEGEN_TOKEN=your_api_token - Default schema URL: `CODEGEN_SCHEMA_URL` (falls back to `VITE_GRAPHQL_URI`, then `https://localhost:5001/graphql`). - Add `VITE_CODEGEN_TOKEN` (or `CODEGEN_TOKEN`) if your gateway requires a bearer token during introspection. -- Generated outputs land in `src/__generated__/graphql.ts` (git-ignored). Run `npm run codegen` after schema/operation changes or rely on `npm run build` (runs `prebuild`). +- Generated outputs land in `src/__generated__/graphql.ts` (committed to git). Run `npm run codegen` after schema/operation changes. diff --git a/fictionarchive-web/package.json b/fictionarchive-web/package.json index b314ed6..85f49c7 100644 --- a/fictionarchive-web/package.json +++ b/fictionarchive-web/package.json @@ -6,7 +6,6 @@ "scripts": { "dev": "vite", "build": "tsc -b && vite build", - "prebuild": "npm run codegen", "codegen": "graphql-codegen --config codegen.ts -r dotenv/config --use-system-ca", "lint": "eslint .", "preview": "vite preview" diff --git a/fictionarchive-web/src/__generated__/graphql.ts b/fictionarchive-web/src/__generated__/graphql.ts new file mode 100644 index 0000000..691f49f --- /dev/null +++ b/fictionarchive-web/src/__generated__/graphql.ts @@ -0,0 +1,838 @@ +import { gql } from '@apollo/client'; +import * as Apollo from '@apollo/client'; +import * as ApolloReactHooks from '@apollo/client/react'; +export type Maybe = T | null; +export type InputMaybe = T | null; +export type Exact = { [K in keyof T]: T[K] }; +export type MakeOptional = Omit & { [SubKey in K]?: Maybe }; +export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; +export type MakeEmpty = { [_ in K]?: never }; +export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; +const defaultOptions = {} as const; +/** All built-in and custom scalars, mapped to their actual values */ +export type Scalars = { + ID: { input: string; output: string; } + String: { input: string; output: string; } + Boolean: { input: boolean; output: boolean; } + Int: { input: number; output: number; } + Float: { input: number; output: number; } + Instant: { input: any; output: any; } + UUID: { input: any; output: any; } + UnsignedInt: { input: any; output: any; } +}; + +export type Chapter = { + body: LocalizationKey; + createdTime: Scalars['Instant']['output']; + id: Scalars['UnsignedInt']['output']; + images: Array; + lastUpdatedTime: Scalars['Instant']['output']; + name: LocalizationKey; + order: Scalars['UnsignedInt']['output']; + revision: Scalars['UnsignedInt']['output']; + url: Maybe; +}; + +export type ChapterFilterInput = { + and: InputMaybe>; + body: InputMaybe; + createdTime: InputMaybe; + id: InputMaybe; + images: InputMaybe; + lastUpdatedTime: InputMaybe; + name: InputMaybe; + or: InputMaybe>; + order: InputMaybe; + revision: InputMaybe; + url: InputMaybe; +}; + +export type ChapterPullRequestedEvent = { + chapterNumber: Scalars['UnsignedInt']['output']; + novelId: Scalars['UnsignedInt']['output']; +}; + +export type ChapterSortInput = { + body: InputMaybe; + createdTime: InputMaybe; + id: InputMaybe; + lastUpdatedTime: InputMaybe; + name: InputMaybe; + order: InputMaybe; + revision: InputMaybe; + url: InputMaybe; +}; + +export type DeleteJobError = KeyNotFoundError; + +export type DeleteJobInput = { + jobKey: Scalars['String']['input']; +}; + +export type DeleteJobPayload = { + boolean: Maybe; + errors: Maybe>; +}; + +export type DuplicateNameError = Error & { + message: Scalars['String']['output']; +}; + +export type Error = { + message: Scalars['String']['output']; +}; + +export type FetchChapterContentsInput = { + chapterNumber: Scalars['UnsignedInt']['input']; + novelId: Scalars['UnsignedInt']['input']; +}; + +export type FetchChapterContentsPayload = { + chapterPullRequestedEvent: Maybe; +}; + +export type FormatError = Error & { + message: Scalars['String']['output']; +}; + +export type Image = { + chapter: Maybe; + createdTime: Scalars['Instant']['output']; + id: Scalars['UUID']['output']; + lastUpdatedTime: Scalars['Instant']['output']; + newPath: Maybe; + originalPath: Scalars['String']['output']; +}; + +export type ImageFilterInput = { + and: InputMaybe>; + chapter: InputMaybe; + createdTime: InputMaybe; + id: InputMaybe; + lastUpdatedTime: InputMaybe; + newPath: InputMaybe; + or: InputMaybe>; + originalPath: InputMaybe; +}; + +export type ImageSortInput = { + chapter: InputMaybe; + createdTime: InputMaybe; + id: InputMaybe; + lastUpdatedTime: InputMaybe; + newPath: InputMaybe; + originalPath: InputMaybe; +}; + +export type ImportNovelInput = { + novelUrl: Scalars['String']['input']; +}; + +export type ImportNovelPayload = { + novelUpdateRequestedEvent: Maybe; +}; + +export type InstantFilterInput = { + and: InputMaybe>; + or: InputMaybe>; +}; + +export type JobKey = { + group: Scalars['String']['output']; + name: Scalars['String']['output']; +}; + +export type JobPersistenceError = Error & { + message: Scalars['String']['output']; +}; + +export type KeyNotFoundError = Error & { + message: Scalars['String']['output']; +}; + +export type KeyValuePairOfStringAndString = { + key: Scalars['String']['output']; + value: Scalars['String']['output']; +}; + +export enum Language { + Ch = 'CH', + En = 'EN', + Ja = 'JA', + Kr = 'KR' +} + +export type LanguageOperationFilterInput = { + eq: InputMaybe; + in: InputMaybe>; + neq: InputMaybe; + nin: InputMaybe>; +}; + +export type ListFilterInputTypeOfChapterFilterInput = { + all: InputMaybe; + any: InputMaybe; + none: InputMaybe; + some: InputMaybe; +}; + +export type ListFilterInputTypeOfImageFilterInput = { + all: InputMaybe; + any: InputMaybe; + none: InputMaybe; + some: InputMaybe; +}; + +export type ListFilterInputTypeOfLocalizationTextFilterInput = { + all: InputMaybe; + any: InputMaybe; + none: InputMaybe; + some: InputMaybe; +}; + +export type ListFilterInputTypeOfNovelFilterInput = { + all: InputMaybe; + any: InputMaybe; + none: InputMaybe; + some: InputMaybe; +}; + +export type ListFilterInputTypeOfNovelTagFilterInput = { + all: InputMaybe; + any: InputMaybe; + none: InputMaybe; + some: InputMaybe; +}; + +export type LocalizationKey = { + createdTime: Scalars['Instant']['output']; + id: Scalars['UUID']['output']; + lastUpdatedTime: Scalars['Instant']['output']; + texts: Array; +}; + +export type LocalizationKeyFilterInput = { + and: InputMaybe>; + createdTime: InputMaybe; + id: InputMaybe; + lastUpdatedTime: InputMaybe; + or: InputMaybe>; + texts: InputMaybe; +}; + +export type LocalizationKeySortInput = { + createdTime: InputMaybe; + id: InputMaybe; + lastUpdatedTime: InputMaybe; +}; + +export type LocalizationText = { + createdTime: Scalars['Instant']['output']; + id: Scalars['UUID']['output']; + language: Language; + lastUpdatedTime: Scalars['Instant']['output']; + text: Scalars['String']['output']; + translationEngine: Maybe; +}; + +export type LocalizationTextFilterInput = { + and: InputMaybe>; + createdTime: InputMaybe; + id: InputMaybe; + language: InputMaybe; + lastUpdatedTime: InputMaybe; + or: InputMaybe>; + text: InputMaybe; + translationEngine: InputMaybe; +}; + +export type Mutation = { + deleteJob: DeleteJobPayload; + fetchChapterContents: FetchChapterContentsPayload; + importNovel: ImportNovelPayload; + registerUser: RegisterUserPayload; + runJob: RunJobPayload; + scheduleEventJob: ScheduleEventJobPayload; + translateText: TranslateTextPayload; +}; + + +export type MutationDeleteJobArgs = { + input: DeleteJobInput; +}; + + +export type MutationFetchChapterContentsArgs = { + input: FetchChapterContentsInput; +}; + + +export type MutationImportNovelArgs = { + input: ImportNovelInput; +}; + + +export type MutationRegisterUserArgs = { + input: RegisterUserInput; +}; + + +export type MutationRunJobArgs = { + input: RunJobInput; +}; + + +export type MutationScheduleEventJobArgs = { + input: ScheduleEventJobInput; +}; + + +export type MutationTranslateTextArgs = { + input: TranslateTextInput; +}; + +export type Novel = { + author: Person; + chapters: Array; + coverImage: Maybe; + createdTime: Scalars['Instant']['output']; + description: LocalizationKey; + externalId: Scalars['String']['output']; + id: Scalars['UnsignedInt']['output']; + lastUpdatedTime: Scalars['Instant']['output']; + name: LocalizationKey; + rawLanguage: Language; + rawStatus: NovelStatus; + source: Source; + statusOverride: Maybe; + tags: Array; + url: Scalars['String']['output']; +}; + +export type NovelFilterInput = { + and: InputMaybe>; + author: InputMaybe; + chapters: InputMaybe; + coverImage: InputMaybe; + createdTime: InputMaybe; + description: InputMaybe; + externalId: InputMaybe; + id: InputMaybe; + lastUpdatedTime: InputMaybe; + name: InputMaybe; + or: InputMaybe>; + rawLanguage: InputMaybe; + rawStatus: InputMaybe; + source: InputMaybe; + statusOverride: InputMaybe; + tags: InputMaybe; + url: InputMaybe; +}; + +export type NovelSortInput = { + author: InputMaybe; + coverImage: InputMaybe; + createdTime: InputMaybe; + description: InputMaybe; + externalId: InputMaybe; + id: InputMaybe; + lastUpdatedTime: InputMaybe; + name: InputMaybe; + rawLanguage: InputMaybe; + rawStatus: InputMaybe; + source: InputMaybe; + statusOverride: InputMaybe; + url: InputMaybe; +}; + +export enum NovelStatus { + Abandoned = 'ABANDONED', + Completed = 'COMPLETED', + Hiatus = 'HIATUS', + InProgress = 'IN_PROGRESS', + Unknown = 'UNKNOWN' +} + +export type NovelStatusOperationFilterInput = { + eq: InputMaybe; + in: InputMaybe>; + neq: InputMaybe; + nin: InputMaybe>; +}; + +export type NovelTag = { + createdTime: Scalars['Instant']['output']; + displayName: LocalizationKey; + id: Scalars['UnsignedInt']['output']; + key: Scalars['String']['output']; + lastUpdatedTime: Scalars['Instant']['output']; + novels: Array; + source: Maybe; + tagType: TagType; +}; + +export type NovelTagFilterInput = { + and: InputMaybe>; + createdTime: InputMaybe; + displayName: InputMaybe; + id: InputMaybe; + key: InputMaybe; + lastUpdatedTime: InputMaybe; + novels: InputMaybe; + or: InputMaybe>; + source: InputMaybe; + tagType: InputMaybe; +}; + +export type NovelUpdateRequestedEvent = { + novelUrl: Scalars['String']['output']; +}; + +/** A connection to a list of items. */ +export type NovelsConnection = { + /** A list of edges. */ + edges: Maybe>; + /** A flattened list of the nodes. */ + nodes: Maybe>; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +}; + +/** An edge in a connection. */ +export type NovelsEdge = { + /** A cursor for use in pagination. */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge. */ + node: Novel; +}; + +export type NullableOfNovelStatusOperationFilterInput = { + eq: InputMaybe; + in: InputMaybe>>; + neq: InputMaybe; + nin: InputMaybe>>; +}; + +/** Information about pagination in a connection. */ +export type PageInfo = { + /** When paginating forwards, the cursor to continue. */ + endCursor: Maybe; + /** Indicates whether more edges exist following the set defined by the clients arguments. */ + hasNextPage: Scalars['Boolean']['output']; + /** Indicates whether more edges exist prior the set defined by the clients arguments. */ + hasPreviousPage: Scalars['Boolean']['output']; + /** When paginating backwards, the cursor to continue. */ + startCursor: Maybe; +}; + +export type Person = { + createdTime: Scalars['Instant']['output']; + externalUrl: Maybe; + id: Scalars['UnsignedInt']['output']; + lastUpdatedTime: Scalars['Instant']['output']; + name: LocalizationKey; +}; + +export type PersonFilterInput = { + and: InputMaybe>; + createdTime: InputMaybe; + externalUrl: InputMaybe; + id: InputMaybe; + lastUpdatedTime: InputMaybe; + name: InputMaybe; + or: InputMaybe>; +}; + +export type PersonSortInput = { + createdTime: InputMaybe; + externalUrl: InputMaybe; + id: InputMaybe; + lastUpdatedTime: InputMaybe; + name: InputMaybe; +}; + +export type Query = { + jobs: Array; + novels: Maybe; + translationEngines: Array; + translationRequests: Maybe; + users: Array; +}; + + +export type QueryNovelsArgs = { + after: InputMaybe; + before: InputMaybe; + first: InputMaybe; + last: InputMaybe; + order: InputMaybe>; + where: InputMaybe; +}; + + +export type QueryTranslationEnginesArgs = { + order: InputMaybe>; + where: InputMaybe; +}; + + +export type QueryTranslationRequestsArgs = { + after: InputMaybe; + before: InputMaybe; + first: InputMaybe; + last: InputMaybe; + order: InputMaybe>; + where: InputMaybe; +}; + +export type RegisterUserInput = { + email: Scalars['String']['input']; + inviterOAuthProviderId: InputMaybe; + oAuthProviderId: Scalars['String']['input']; + username: Scalars['String']['input']; +}; + +export type RegisterUserPayload = { + user: Maybe; +}; + +export type RunJobError = JobPersistenceError; + +export type RunJobInput = { + jobKey: Scalars['String']['input']; +}; + +export type RunJobPayload = { + boolean: Maybe; + errors: Maybe>; +}; + +export type ScheduleEventJobError = DuplicateNameError | FormatError; + +export type ScheduleEventJobInput = { + cronSchedule: Scalars['String']['input']; + description: Scalars['String']['input']; + eventData: Scalars['String']['input']; + eventType: Scalars['String']['input']; + key: Scalars['String']['input']; +}; + +export type ScheduleEventJobPayload = { + errors: Maybe>; + schedulerJob: Maybe; +}; + +export type SchedulerJob = { + cronSchedule: Array; + description: Scalars['String']['output']; + jobData: Array; + jobKey: JobKey; + jobTypeName: Scalars['String']['output']; +}; + +export enum SortEnumType { + Asc = 'ASC', + Desc = 'DESC' +} + +export type Source = { + createdTime: Scalars['Instant']['output']; + id: Scalars['UnsignedInt']['output']; + key: Scalars['String']['output']; + lastUpdatedTime: Scalars['Instant']['output']; + name: Scalars['String']['output']; + url: Scalars['String']['output']; +}; + +export type SourceFilterInput = { + and: InputMaybe>; + createdTime: InputMaybe; + id: InputMaybe; + key: InputMaybe; + lastUpdatedTime: InputMaybe; + name: InputMaybe; + or: InputMaybe>; + url: InputMaybe; +}; + +export type SourceSortInput = { + createdTime: InputMaybe; + id: InputMaybe; + key: InputMaybe; + lastUpdatedTime: InputMaybe; + name: InputMaybe; + url: InputMaybe; +}; + +export type StringOperationFilterInput = { + and: InputMaybe>; + contains: InputMaybe; + endsWith: InputMaybe; + eq: InputMaybe; + in: InputMaybe>>; + ncontains: InputMaybe; + nendsWith: InputMaybe; + neq: InputMaybe; + nin: InputMaybe>>; + nstartsWith: InputMaybe; + or: InputMaybe>; + startsWith: InputMaybe; +}; + +export enum TagType { + External = 'EXTERNAL', + Genre = 'GENRE', + System = 'SYSTEM', + UserDefined = 'USER_DEFINED' +} + +export type TagTypeOperationFilterInput = { + eq: InputMaybe; + in: InputMaybe>; + neq: InputMaybe; + nin: InputMaybe>; +}; + +export type TranslateTextInput = { + from: Language; + text: Scalars['String']['input']; + to: Language; + translationEngineKey: Scalars['String']['input']; +}; + +export type TranslateTextPayload = { + translationResult: Maybe; +}; + +export type TranslationEngine = { + createdTime: Scalars['Instant']['output']; + id: Scalars['UnsignedInt']['output']; + key: Scalars['String']['output']; + lastUpdatedTime: Scalars['Instant']['output']; +}; + +export type TranslationEngineDescriptor = { + displayName: Scalars['String']['output']; + key: Scalars['String']['output']; +}; + +export type TranslationEngineDescriptorFilterInput = { + and: InputMaybe>; + displayName: InputMaybe; + key: InputMaybe; + or: InputMaybe>; +}; + +export type TranslationEngineDescriptorSortInput = { + displayName: InputMaybe; + key: InputMaybe; +}; + +export type TranslationEngineFilterInput = { + and: InputMaybe>; + createdTime: InputMaybe; + id: InputMaybe; + key: InputMaybe; + lastUpdatedTime: InputMaybe; + or: InputMaybe>; +}; + +export type TranslationRequest = { + billedCharacterCount: Scalars['UnsignedInt']['output']; + createdTime: Scalars['Instant']['output']; + from: Language; + id: Scalars['UUID']['output']; + lastUpdatedTime: Scalars['Instant']['output']; + originalText: Scalars['String']['output']; + status: TranslationRequestStatus; + to: Language; + translatedText: Maybe; + translationEngineKey: Scalars['String']['output']; +}; + +export type TranslationRequestFilterInput = { + and: InputMaybe>; + billedCharacterCount: InputMaybe; + createdTime: InputMaybe; + from: InputMaybe; + id: InputMaybe; + lastUpdatedTime: InputMaybe; + or: InputMaybe>; + originalText: InputMaybe; + status: InputMaybe; + to: InputMaybe; + translatedText: InputMaybe; + translationEngineKey: InputMaybe; +}; + +export type TranslationRequestSortInput = { + billedCharacterCount: InputMaybe; + createdTime: InputMaybe; + from: InputMaybe; + id: InputMaybe; + lastUpdatedTime: InputMaybe; + originalText: InputMaybe; + status: InputMaybe; + to: InputMaybe; + translatedText: InputMaybe; + translationEngineKey: InputMaybe; +}; + +export enum TranslationRequestStatus { + Failed = 'FAILED', + Pending = 'PENDING', + Success = 'SUCCESS' +} + +export type TranslationRequestStatusOperationFilterInput = { + eq: InputMaybe; + in: InputMaybe>; + neq: InputMaybe; + nin: InputMaybe>; +}; + +/** A connection to a list of items. */ +export type TranslationRequestsConnection = { + /** A list of edges. */ + edges: Maybe>; + /** A flattened list of the nodes. */ + nodes: Maybe>; + /** Information to aid in pagination. */ + pageInfo: PageInfo; +}; + +/** An edge in a connection. */ +export type TranslationRequestsEdge = { + /** A cursor for use in pagination. */ + cursor: Scalars['String']['output']; + /** The item at the end of the edge. */ + node: TranslationRequest; +}; + +export type TranslationResult = { + billedCharacterCount: Scalars['UnsignedInt']['output']; + from: Language; + originalText: Scalars['String']['output']; + status: TranslationRequestStatus; + to: Language; + translatedText: Maybe; + translationEngineKey: Scalars['String']['output']; +}; + +export type UnsignedIntOperationFilterInputType = { + eq: InputMaybe; + gt: InputMaybe; + gte: InputMaybe; + in: InputMaybe>>; + lt: InputMaybe; + lte: InputMaybe; + neq: InputMaybe; + ngt: InputMaybe; + ngte: InputMaybe; + nin: InputMaybe>>; + nlt: InputMaybe; + nlte: InputMaybe; +}; + +export type User = { + createdTime: Scalars['Instant']['output']; + disabled: Scalars['Boolean']['output']; + email: Scalars['String']['output']; + id: Scalars['UUID']['output']; + inviter: Maybe; + lastUpdatedTime: Scalars['Instant']['output']; + oAuthProviderId: Scalars['String']['output']; + username: Scalars['String']['output']; +}; + +export type UuidOperationFilterInput = { + eq: InputMaybe; + gt: InputMaybe; + gte: InputMaybe; + in: InputMaybe>>; + lt: InputMaybe; + lte: InputMaybe; + neq: InputMaybe; + ngt: InputMaybe; + ngte: InputMaybe; + nin: InputMaybe>>; + nlt: InputMaybe; + nlte: InputMaybe; +}; + +export type NovelsQueryVariables = Exact<{ + first: InputMaybe; + after: InputMaybe; +}>; + + +export type NovelsQuery = { novels: { edges: Array<{ cursor: string, node: { id: any, url: string, name: { texts: Array<{ language: Language, text: string }> }, description: { texts: Array<{ language: Language, text: string }> }, coverImage: { originalPath: string, newPath: string | null } | null } }> | null, pageInfo: { hasNextPage: boolean, endCursor: string | null } } | null }; + + +export const NovelsDocument = gql` + query Novels($first: Int, $after: String) { + novels(first: $first, after: $after) { + edges { + cursor + node { + id + url + name { + texts { + language + text + } + } + description { + texts { + language + text + } + } + coverImage { + originalPath + newPath + } + } + } + pageInfo { + hasNextPage + endCursor + } + } +} + `; + +/** + * __useNovelsQuery__ + * + * To run a query within a React component, call `useNovelsQuery` and pass it any options that fit your needs. + * When your component renders, `useNovelsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useNovelsQuery({ + * variables: { + * first: // value for 'first' + * after: // value for 'after' + * }, + * }); + */ +export function useNovelsQuery(baseOptions?: ApolloReactHooks.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useQuery(NovelsDocument, options); + } +export function useNovelsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useLazyQuery(NovelsDocument, options); + } +export function useNovelsSuspenseQuery(baseOptions?: ApolloReactHooks.SkipToken | ApolloReactHooks.SuspenseQueryHookOptions) { + const options = baseOptions === ApolloReactHooks.skipToken ? baseOptions : {...defaultOptions, ...baseOptions} + return ApolloReactHooks.useSuspenseQuery(NovelsDocument, options); + } +export type NovelsQueryHookResult = ReturnType; +export type NovelsLazyQueryHookResult = ReturnType; +export type NovelsSuspenseQueryHookResult = ReturnType; +export type NovelsQueryResult = Apollo.QueryResult; \ No newline at end of file -- 2.49.1 From 15a8185621d967ca4073e93473336d7ac04f552c Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 08:48:00 -0500 Subject: [PATCH 09/22] [FA-11] Fix react build issues --- fictionarchive-web/codegen.ts | 12 +- fictionarchive-web/package-lock.json | 1538 +++++------------ fictionarchive-web/package.json | 6 +- .../src/__generated__/graphql.ts | 624 +++---- .../src/components/NovelCard.tsx | 8 +- fictionarchive-web/src/pages/NovelsPage.tsx | 5 +- 6 files changed, 756 insertions(+), 1437 deletions(-) diff --git a/fictionarchive-web/codegen.ts b/fictionarchive-web/codegen.ts index c4d3d4b..89136b3 100644 --- a/fictionarchive-web/codegen.ts +++ b/fictionarchive-web/codegen.ts @@ -22,15 +22,17 @@ const config: CodegenConfig = { plugins: [ 'typescript', 'typescript-operations', - 'typescript-react-apollo', + 'typed-document-node', ], config: { - withHooks: true, - avoidOptionals: true, - dedupeFragments: true, + avoidOptionals: { + field: true, + inputValue: false, + }, + enumsAsConst: true, maybeValue: 'T | null', skipTypename: true, - apolloReactHooksImportFrom: '@apollo/client/react', + useTypeImports: true, }, }, }, diff --git a/fictionarchive-web/package-lock.json b/fictionarchive-web/package-lock.json index 2248c06..f01efe1 100644 --- a/fictionarchive-web/package-lock.json +++ b/fictionarchive-web/package-lock.json @@ -22,9 +22,9 @@ "devDependencies": { "@eslint/js": "^9.39.1", "@graphql-codegen/cli": "^5.0.3", + "@graphql-codegen/typed-document-node": "^6.1.1", "@graphql-codegen/typescript": "^4.0.9", "@graphql-codegen/typescript-operations": "^4.0.9", - "@graphql-codegen/typescript-react-apollo": "^4.0.9", "@types/node": "^24.10.1", "@types/react": "^19.2.5", "@types/react-dom": "^19.2.3", @@ -60,6 +60,12 @@ "version": "4.0.9", "resolved": "https://registry.npmjs.org/@apollo/client/-/client-4.0.9.tgz", "integrity": "sha512-Lh2drMzFE9x5jVS8RKmlGL5SORkvpyUJIT+wTErxDUR2HpWePiBfhhcHHRSlZFiCR866ewCv4euTc4IDF0GWxw==", + "license": "MIT", + "workspaces": [ + "dist", + "codegen", + "scripts/codemods/ac3-to-ac4" + ], "dependencies": { "@graphql-typed-document-node/core": "^3.1.1", "@wry/caches": "^1.0.0", @@ -122,6 +128,7 @@ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", @@ -136,6 +143,7 @@ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -145,6 +153,7 @@ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", @@ -176,6 +185,7 @@ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/parser": "^7.28.5", "@babel/types": "^7.28.5", @@ -187,24 +197,12 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-compilation-targets": { "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", @@ -216,47 +214,12 @@ "node": ">=6.9.0" } }, - "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.5.tgz", - "integrity": "sha512-q3WC4JfdODypvxArsJQROfupPBq9+lMwjKq7C33GhbFYJsufD0yd/ziwD+hJucLeWsnFPWZjsU2DNFqBPE7jwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-member-expression-to-functions": "^7.28.5", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.28.5", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, "node_modules/@babel/helper-globals": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz", - "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==", - "dev": true, "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5" - }, "engines": { "node": ">=6.9.0" } @@ -266,6 +229,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dev": true, + "license": "MIT", "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" @@ -279,6 +243,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", @@ -291,56 +256,12 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-plugin-utils": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", - "dev": true, "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, "engines": { "node": ">=6.9.0" } @@ -350,6 +271,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -359,6 +281,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -368,6 +291,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -377,6 +301,7 @@ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, + "license": "MIT", "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.28.4" @@ -390,6 +315,7 @@ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.28.5" }, @@ -400,74 +326,6 @@ "node": ">=6.0.0" } }, - "node_modules/@babel/plugin-proposal-class-properties": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", - "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.18.6", - "@babel/helper-plugin-utils": "^7.18.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-proposal-object-rest-spread": { - "version": "7.20.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.20.7.tgz", - "integrity": "sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==", - "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-object-rest-spread instead.", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.20.5", - "@babel/helper-compilation-targets": "^7.20.7", - "@babel/helper-plugin-utils": "^7.20.2", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-transform-parameters": "^7.20.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-flow": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.27.1.tgz", - "integrity": "sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-syntax-import-assertions": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", @@ -484,329 +342,12 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", - "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.28.5.tgz", - "integrity": "sha512-45DmULpySVvmq9Pj3X9B+62Xe+DJGov27QravQJU1LLcapR6/10i+gYVAucGGJpHBp5mYxIMK4nDAT/QDLr47g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.28.4.tgz", - "integrity": "sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-globals": "^7.28.0", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.28.4" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", - "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.28.5.tgz", - "integrity": "sha512-Kl9Bc6D0zTUcFUvkNuQh4eGXPKKNDOJQXVyyM4ZAQPMveniJdxi8XMJwLo+xSoW3MIq81bD33lcUe9kZpl0MCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-flow-strip-types": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.27.1.tgz", - "integrity": "sha512-G5eDKsu50udECw7DL2AcsysXiQyB7Nfg521t2OAJ4tbfTJ27doHLeF/vlI1NZGlLdbb/v+ibvtL1YBQqYOwJGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-syntax-flow": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", - "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", - "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.27.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.7.tgz", - "integrity": "sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", - "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.28.0.tgz", - "integrity": "sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz", - "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, "node_modules/@babel/plugin-transform-react-jsx-self": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, @@ -822,54 +363,6 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", - "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" @@ -896,6 +389,7 @@ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", @@ -910,6 +404,7 @@ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -928,6 +423,7 @@ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" @@ -988,6 +484,7 @@ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" @@ -1004,6 +501,7 @@ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -1020,6 +518,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -1036,6 +535,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -1052,6 +552,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1068,6 +569,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -1084,6 +586,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -1100,6 +603,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -1116,6 +620,7 @@ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1132,6 +637,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1148,6 +654,7 @@ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1164,6 +671,7 @@ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1180,6 +688,7 @@ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1196,6 +705,7 @@ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1212,6 +722,7 @@ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1228,6 +739,7 @@ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1244,6 +756,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -1260,6 +773,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -1276,6 +790,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" @@ -1292,6 +807,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -1308,6 +824,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" @@ -1324,6 +841,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openharmony" @@ -1340,6 +858,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" @@ -1356,6 +875,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -1372,6 +892,7 @@ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -1388,6 +909,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -1401,6 +923,7 @@ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", "dev": true, + "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, @@ -1419,6 +942,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -1431,6 +955,7 @@ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } @@ -1440,6 +965,7 @@ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", @@ -1454,6 +980,7 @@ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@eslint/core": "^0.17.0" }, @@ -1466,6 +993,7 @@ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, @@ -1478,6 +1006,7 @@ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -1501,6 +1030,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -1513,6 +1043,7 @@ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.1.tgz", "integrity": "sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -1525,6 +1056,7 @@ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -1534,6 +1066,7 @@ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" @@ -1666,6 +1199,26 @@ } } }, + "node_modules/@graphql-codegen/client-preset/node_modules/@graphql-codegen/typed-document-node": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-5.1.2.tgz", + "integrity": "sha512-jaxfViDqFRbNQmfKwUY8hDyjnLTw2Z7DhGutxoOiiAI0gE/LfPe0LYaVFKVmVOOD7M3bWxoWfu4slrkbWbUbEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@graphql-codegen/plugin-helpers": "^5.1.0", + "@graphql-codegen/visitor-plugin-common": "5.8.0", + "auto-bind": "~4.0.0", + "change-case-all": "1.0.15", + "tslib": "~2.6.0" + }, + "engines": { + "node": ">=16" + }, + "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" + } + }, "node_modules/@graphql-codegen/client-preset/node_modules/tslib": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", @@ -1774,14 +1327,14 @@ "license": "0BSD" }, "node_modules/@graphql-codegen/typed-document-node": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-5.1.2.tgz", - "integrity": "sha512-jaxfViDqFRbNQmfKwUY8hDyjnLTw2Z7DhGutxoOiiAI0gE/LfPe0LYaVFKVmVOOD7M3bWxoWfu4slrkbWbUbEw==", + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/@graphql-codegen/typed-document-node/-/typed-document-node-6.1.3.tgz", + "integrity": "sha512-U1+16S3EWnR4T5b9219pu/47InfDB3UZ2E/CihJXgkeSklNteNBtw3D8m9R+ZanIq/GIsEin2hYpR++PO6neLQ==", "dev": true, "license": "MIT", "dependencies": { - "@graphql-codegen/plugin-helpers": "^5.1.0", - "@graphql-codegen/visitor-plugin-common": "5.8.0", + "@graphql-codegen/plugin-helpers": "^6.1.0", + "@graphql-codegen/visitor-plugin-common": "6.2.0", "auto-bind": "~4.0.0", "change-case-all": "1.0.15", "tslib": "~2.6.0" @@ -1793,6 +1346,62 @@ "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" } }, + "node_modules/@graphql-codegen/typed-document-node/node_modules/@graphql-codegen/plugin-helpers": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-6.1.0.tgz", + "integrity": "sha512-JJypehWTcty9kxKiqH7TQOetkGdOYjY78RHlI+23qB59cV2wxjFFVf8l7kmuXS4cpGVUNfIjFhVr7A1W7JMtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@graphql-tools/utils": "^10.0.0", + "change-case-all": "1.0.15", + "common-tags": "1.8.2", + "import-from": "4.0.0", + "lodash": "~4.17.0", + "tslib": "~2.6.0" + }, + "engines": { + "node": ">=16" + }, + "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" + } + }, + "node_modules/@graphql-codegen/typed-document-node/node_modules/@graphql-codegen/visitor-plugin-common": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-6.2.0.tgz", + "integrity": "sha512-8mx5uDDEwhP3G1jdeBdvVm4QhbEdkwXQa1hAXBWGofL87ePs5PUhz4IuQpwiS/CfFa4cqxcAWbe0k74x8iWfKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@graphql-codegen/plugin-helpers": "^6.1.0", + "@graphql-tools/optimize": "^2.0.0", + "@graphql-tools/relay-operation-optimizer": "^7.0.0", + "@graphql-tools/utils": "^10.0.0", + "auto-bind": "~4.0.0", + "change-case-all": "1.0.15", + "dependency-graph": "^1.0.0", + "graphql-tag": "^2.11.0", + "parse-filepath": "^1.0.2", + "tslib": "~2.6.0" + }, + "engines": { + "node": ">=16" + }, + "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" + } + }, + "node_modules/@graphql-codegen/typed-document-node/node_modules/dependency-graph": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz", + "integrity": "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/@graphql-codegen/typed-document-node/node_modules/tslib": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", @@ -1853,266 +1462,6 @@ "dev": true, "license": "0BSD" }, - "node_modules/@graphql-codegen/typescript-react-apollo": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@graphql-codegen/typescript-react-apollo/-/typescript-react-apollo-4.3.3.tgz", - "integrity": "sha512-ecuzzqoZEHCtlxaEXL1LQTrfzVYwNNtbVUBHc/KQDfkJIQZon+dG5ZXOoJ4BpbRA2L99yTx+TZc2VkpOVfSypw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@graphql-codegen/plugin-helpers": "^3.0.0", - "@graphql-codegen/visitor-plugin-common": "2.13.8", - "auto-bind": "~4.0.0", - "change-case-all": "1.0.15", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">= 16.0.0" - }, - "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" - } - }, - "node_modules/@graphql-codegen/typescript-react-apollo/node_modules/@ardatan/relay-compiler": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/@ardatan/relay-compiler/-/relay-compiler-12.0.0.tgz", - "integrity": "sha512-9anThAaj1dQr6IGmzBMcfzOQKTa5artjuPmw8NYK/fiGEMjADbSguBY2FMDykt+QhilR3wc9VA/3yVju7JHg7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.14.0", - "@babel/generator": "^7.14.0", - "@babel/parser": "^7.14.0", - "@babel/runtime": "^7.0.0", - "@babel/traverse": "^7.14.0", - "@babel/types": "^7.0.0", - "babel-preset-fbjs": "^3.4.0", - "chalk": "^4.0.0", - "fb-watchman": "^2.0.0", - "fbjs": "^3.0.0", - "glob": "^7.1.1", - "immutable": "~3.7.6", - "invariant": "^2.2.4", - "nullthrows": "^1.1.1", - "relay-runtime": "12.0.0", - "signedsource": "^1.0.0", - "yargs": "^15.3.1" - }, - "bin": { - "relay-compiler": "bin/relay-compiler" - }, - "peerDependencies": { - "graphql": "*" - } - }, - "node_modules/@graphql-codegen/typescript-react-apollo/node_modules/@graphql-codegen/plugin-helpers": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@graphql-codegen/plugin-helpers/-/plugin-helpers-3.1.2.tgz", - "integrity": "sha512-emOQiHyIliVOIjKVKdsI5MXj312zmRDwmHpyUTZMjfpvxq/UVAHUJIVdVf+lnjjrI+LXBTgMlTWTgHQfmICxjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@graphql-tools/utils": "^9.0.0", - "change-case-all": "1.0.15", - "common-tags": "1.8.2", - "import-from": "4.0.0", - "lodash": "~4.17.0", - "tslib": "~2.4.0" - }, - "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" - } - }, - "node_modules/@graphql-codegen/typescript-react-apollo/node_modules/@graphql-codegen/plugin-helpers/node_modules/tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", - "dev": true, - "license": "0BSD" - }, - "node_modules/@graphql-codegen/typescript-react-apollo/node_modules/@graphql-codegen/visitor-plugin-common": { - "version": "2.13.8", - "resolved": "https://registry.npmjs.org/@graphql-codegen/visitor-plugin-common/-/visitor-plugin-common-2.13.8.tgz", - "integrity": "sha512-IQWu99YV4wt8hGxIbBQPtqRuaWZhkQRG2IZKbMoSvh0vGeWb3dB0n0hSgKaOOxDY+tljtOf9MTcUYvJslQucMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@graphql-codegen/plugin-helpers": "^3.1.2", - "@graphql-tools/optimize": "^1.3.0", - "@graphql-tools/relay-operation-optimizer": "^6.5.0", - "@graphql-tools/utils": "^9.0.0", - "auto-bind": "~4.0.0", - "change-case-all": "1.0.15", - "dependency-graph": "^0.11.0", - "graphql-tag": "^2.11.0", - "parse-filepath": "^1.0.2", - "tslib": "~2.4.0" - }, - "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" - } - }, - "node_modules/@graphql-codegen/typescript-react-apollo/node_modules/@graphql-codegen/visitor-plugin-common/node_modules/tslib": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", - "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", - "dev": true, - "license": "0BSD" - }, - "node_modules/@graphql-codegen/typescript-react-apollo/node_modules/@graphql-tools/optimize": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@graphql-tools/optimize/-/optimize-1.4.0.tgz", - "integrity": "sha512-dJs/2XvZp+wgHH8T5J2TqptT9/6uVzIYvA6uFACha+ufvdMBedkfR4b4GbT8jAKLRARiqRTxy3dctnwkTM2tdw==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.4.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" - } - }, - "node_modules/@graphql-codegen/typescript-react-apollo/node_modules/@graphql-tools/relay-operation-optimizer": { - "version": "6.5.18", - "resolved": "https://registry.npmjs.org/@graphql-tools/relay-operation-optimizer/-/relay-operation-optimizer-6.5.18.tgz", - "integrity": "sha512-mc5VPyTeV+LwiM+DNvoDQfPqwQYhPV/cl5jOBjTgSniyaq8/86aODfMkrE2OduhQ5E00hqrkuL2Fdrgk0w1QJg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ardatan/relay-compiler": "12.0.0", - "@graphql-tools/utils": "^9.2.1", - "tslib": "^2.4.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" - } - }, - "node_modules/@graphql-codegen/typescript-react-apollo/node_modules/@graphql-tools/utils": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz", - "integrity": "sha512-WUw506Ql6xzmOORlriNrD6Ugx+HjVgYxt9KCXD9mHAak+eaXSwuGGPyE60hy9xaDEoXKBsG7SkG69ybitaVl6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@graphql-typed-document-node/core": "^3.1.1", - "tslib": "^2.4.0" - }, - "peerDependencies": { - "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" - } - }, - "node_modules/@graphql-codegen/typescript-react-apollo/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/@graphql-codegen/typescript-react-apollo/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@graphql-codegen/typescript-react-apollo/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@graphql-codegen/typescript-react-apollo/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@graphql-codegen/typescript-react-apollo/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@graphql-codegen/typescript-react-apollo/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/@graphql-codegen/typescript-react-apollo/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@graphql-codegen/typescript-react-apollo/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@graphql-codegen/typescript/node_modules/tslib": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", @@ -2486,16 +1835,6 @@ "graphql": "^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, - "node_modules/@graphql-tools/import/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/@graphql-tools/json-file-loader": { "version": "8.0.24", "resolved": "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-8.0.24.tgz", @@ -2705,6 +2044,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz", "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", + "license": "MIT", "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 || ^17.0.0" } @@ -2714,6 +2054,7 @@ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18.0" } @@ -2723,6 +2064,7 @@ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" @@ -2736,6 +2078,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=12.22" }, @@ -2749,6 +2092,7 @@ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -2784,6 +2128,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" @@ -2794,6 +2139,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" @@ -2804,6 +2150,7 @@ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.0.0" } @@ -2812,13 +2159,15 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -2829,6 +2178,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" @@ -2842,6 +2192,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } @@ -2851,6 +2202,7 @@ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" @@ -2912,7 +2264,8 @@ "version": "1.0.0-beta.47", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.47.tgz", "integrity": "sha512-8QagwMH3kNCuzD8EWL8R2YPW5e4OrHNSAHRFDdmFqEwEaD/KcNKjVoumo+gP2vW5eKB2UPbM6vTYiGZX0ixLnw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.53.3", @@ -2922,6 +2275,7 @@ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -2935,6 +2289,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" @@ -2948,6 +2303,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -2961,6 +2317,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -2974,6 +2331,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -2987,6 +2345,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" @@ -3000,6 +2359,7 @@ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3013,6 +2373,7 @@ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3026,6 +2387,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3039,6 +2401,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3052,6 +2415,7 @@ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3065,6 +2429,7 @@ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3078,6 +2443,7 @@ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3091,6 +2457,7 @@ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3104,6 +2471,7 @@ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3117,6 +2485,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3130,6 +2499,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" @@ -3143,6 +2513,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openharmony" @@ -3156,6 +2527,7 @@ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -3169,6 +2541,7 @@ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -3182,6 +2555,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -3195,6 +2569,7 @@ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" @@ -3224,6 +2599,7 @@ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", @@ -3237,6 +2613,7 @@ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.0.0" } @@ -3246,6 +2623,7 @@ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" @@ -3256,6 +2634,7 @@ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.28.2" } @@ -3264,7 +2643,8 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/js-yaml": { "version": "4.0.9", @@ -3277,23 +2657,26 @@ "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { "version": "24.10.1", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.1.tgz", "integrity": "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "undici-types": "~7.16.0" } }, "node_modules/@types/react": { - "version": "19.2.6", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.6.tgz", - "integrity": "sha512-p/jUvulfgU7oKtj6Xpk8cA2Y1xKTtICGpJYeJXz2YVO2UcvjQgeRMLDGfDeqeRW2Ta+0QNFwcc8X3GH8SxZz6w==", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", + "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "devOptional": true, + "license": "MIT", "peer": true, "dependencies": { "csstype": "^3.2.2" @@ -3304,6 +2687,7 @@ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "dev": true, + "license": "MIT", "peerDependencies": { "@types/react": "^19.2.0" } @@ -3319,16 +2703,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.47.0.tgz", - "integrity": "sha512-fe0rz9WJQ5t2iaLfdbDc9T80GJy0AeO453q8C3YCilnGozvOyCG5t+EZtg7j7D88+c3FipfP/x+wzGnh1xp8ZA==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.48.0.tgz", + "integrity": "sha512-XxXP5tL1txl13YFtrECECQYeZjBZad4fyd3cFV4a19LkAY/bIp9fev3US4S5fDVV2JaYFiKAZ/GRTOLer+mbyQ==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.47.0", - "@typescript-eslint/type-utils": "8.47.0", - "@typescript-eslint/utils": "8.47.0", - "@typescript-eslint/visitor-keys": "8.47.0", + "@typescript-eslint/scope-manager": "8.48.0", + "@typescript-eslint/type-utils": "8.48.0", + "@typescript-eslint/utils": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -3342,7 +2727,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.47.0", + "@typescript-eslint/parser": "^8.48.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -3352,21 +2737,23 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/@typescript-eslint/parser": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.47.0.tgz", - "integrity": "sha512-lJi3PfxVmo0AkEY93ecfN+r8SofEqZNGByvHAI3GBLrvt1Cw6H5k1IM02nSzu0RfUafr2EvFSw0wAsZgubNplQ==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.48.0.tgz", + "integrity": "sha512-jCzKdm/QK0Kg4V4IK/oMlRZlY+QOcdjv89U2NgKHZk1CYTj82/RVSx1mV/0gqCVMJ/DA+Zf/S4NBWNF8GQ+eqQ==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.47.0", - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/typescript-estree": "8.47.0", - "@typescript-eslint/visitor-keys": "8.47.0", + "@typescript-eslint/scope-manager": "8.48.0", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/typescript-estree": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0", "debug": "^4.3.4" }, "engines": { @@ -3382,13 +2769,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.47.0.tgz", - "integrity": "sha512-2X4BX8hUeB5JcA1TQJ7GjcgulXQ+5UkNb0DL8gHsHUHdFoiCTJoYLTpib3LtSDPZsRET5ygN4qqIWrHyYIKERA==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.48.0.tgz", + "integrity": "sha512-Ne4CTZyRh1BecBf84siv42wv5vQvVmgtk8AuiEffKTUo3DrBaGYZueJSxxBZ8fjk/N3DrgChH4TOdIOwOwiqqw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.47.0", - "@typescript-eslint/types": "^8.47.0", + "@typescript-eslint/tsconfig-utils": "^8.48.0", + "@typescript-eslint/types": "^8.48.0", "debug": "^4.3.4" }, "engines": { @@ -3403,13 +2791,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.47.0.tgz", - "integrity": "sha512-a0TTJk4HXMkfpFkL9/WaGTNuv7JWfFTQFJd6zS9dVAjKsojmv9HT55xzbEpnZoY+VUb+YXLMp+ihMLz/UlZfDg==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.48.0.tgz", + "integrity": "sha512-uGSSsbrtJrLduti0Q1Q9+BF1/iFKaxGoQwjWOIVNJv0o6omrdyR8ct37m4xIl5Zzpkp69Kkmvom7QFTtue89YQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/visitor-keys": "8.47.0" + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3420,10 +2809,11 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.47.0.tgz", - "integrity": "sha512-ybUAvjy4ZCL11uryalkKxuT3w3sXJAuWhOoGS3T/Wu+iUu1tGJmk5ytSY8gbdACNARmcYEB0COksD2j6hfGK2g==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.48.0.tgz", + "integrity": "sha512-WNebjBdFdyu10sR1M4OXTt2OkMd5KWIL+LLfeH9KhgP+jzfDV/LI3eXzwJ1s9+Yc0Kzo2fQCdY/OpdusCMmh6w==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -3436,14 +2826,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.47.0.tgz", - "integrity": "sha512-QC9RiCmZ2HmIdCEvhd1aJELBlD93ErziOXXlHEZyuBo3tBiAZieya0HLIxp+DoDWlsQqDawyKuNEhORyku+P8A==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.48.0.tgz", + "integrity": "sha512-zbeVaVqeXhhab6QNEKfK96Xyc7UQuoFWERhEnj3mLVnUWrQnv15cJNseUni7f3g557gm0e46LZ6IJ4NJVOgOpw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/typescript-estree": "8.47.0", - "@typescript-eslint/utils": "8.47.0", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/typescript-estree": "8.48.0", + "@typescript-eslint/utils": "8.48.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -3460,10 +2851,11 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.47.0.tgz", - "integrity": "sha512-nHAE6bMKsizhA2uuYZbEbmp5z2UpffNrPEqiKIeN7VsV6UY/roxanWfoRrf6x/k9+Obf+GQdkm0nPU+vnMXo9A==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.48.0.tgz", + "integrity": "sha512-cQMcGQQH7kwKoVswD1xdOytxQR60MWKM1di26xSUtxehaDs/32Zpqsu5WJlXTtTTqyAVK8R7hvsUnIXRS+bjvA==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -3473,20 +2865,20 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.47.0.tgz", - "integrity": "sha512-k6ti9UepJf5NpzCjH31hQNLHQWupTRPhZ+KFF8WtTuTpy7uHPfeg2NM7cP27aCGajoEplxJDFVCEm9TGPYyiVg==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.48.0.tgz", + "integrity": "sha512-ljHab1CSO4rGrQIAyizUS6UGHHCiAYhbfcIZ1zVJr5nMryxlXMVWS3duFPSKvSUbFPwkXMFk1k0EMIjub4sRRQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.47.0", - "@typescript-eslint/tsconfig-utils": "8.47.0", - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/visitor-keys": "8.47.0", + "@typescript-eslint/project-service": "8.48.0", + "@typescript-eslint/tsconfig-utils": "8.48.0", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/visitor-keys": "8.48.0", "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", + "tinyglobby": "^0.2.15", "ts-api-utils": "^2.1.0" }, "engines": { @@ -3505,6 +2897,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -3514,6 +2907,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, @@ -3529,6 +2923,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -3537,15 +2932,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.47.0.tgz", - "integrity": "sha512-g7XrNf25iL4TJOiPqatNuaChyqt49a/onq5YsJ9+hXeugK+41LVg7AxikMfM02PC6jbNtZLCJj6AUcQXJS/jGQ==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.48.0.tgz", + "integrity": "sha512-yTJO1XuGxCsSfIVt1+1UrLHtue8xz16V8apzPYI06W0HbEbEWHxHXgZaAgavIkoh+GeV6hKKd5jm0sS6OYxWXQ==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.47.0", - "@typescript-eslint/types": "8.47.0", - "@typescript-eslint/typescript-estree": "8.47.0" + "@typescript-eslint/scope-manager": "8.48.0", + "@typescript-eslint/types": "8.48.0", + "@typescript-eslint/typescript-estree": "8.48.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3560,12 +2956,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.47.0.tgz", - "integrity": "sha512-SIV3/6eftCy1bNzCQoPmbWsRLujS8t5iDIZ4spZOBHqrM+yfX2ogg8Tt3PDTAVKw3sSCiUgg30uOAvK2r9zGjQ==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.48.0.tgz", + "integrity": "sha512-T0XJMaRPOH3+LBbAfzR2jalckP1MSG/L9eUtY0DEzUyVaXJ/t6zN0nR7co5kz0Jko/nkSYCBRkz1djvjajVTTg==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.47.0", + "@typescript-eslint/types": "8.48.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -3581,6 +2978,7 @@ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.1.tgz", "integrity": "sha512-WQfkSw0QbQ5aJ2CHYw23ZGkqnRwqKHD/KYsMeTkZzPT4Jcf0DcBxBtwMJxnu6E7oxw5+JC6ZAiePgh28uJ1HBA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.28.5", "@babel/plugin-transform-react-jsx-self": "^7.27.1", @@ -3657,6 +3055,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@wry/caches/-/caches-1.0.1.tgz", "integrity": "sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -3668,6 +3067,7 @@ "version": "0.7.4", "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.7.4.tgz", "integrity": "sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -3679,6 +3079,7 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.5.7.tgz", "integrity": "sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -3690,6 +3091,7 @@ "version": "0.5.0", "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.5.0.tgz", "integrity": "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==", + "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, @@ -3702,6 +3104,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, + "license": "MIT", "peer": true, "bin": { "acorn": "bin/acorn" @@ -3715,6 +3118,7 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -3748,6 +3152,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -3790,6 +3195,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, @@ -3832,7 +3238,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/array-union": { "version": "2.1.0", @@ -3912,57 +3319,12 @@ "postcss": "^8.1.0" } }, - "node_modules/babel-plugin-syntax-trailing-function-commas": { - "version": "7.0.0-beta.0", - "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz", - "integrity": "sha512-Xj9XuRuz3nTSbaTXWv3itLOcxyF4oPD8douBBmj7U9BBC6nEBYfyOJYQMf/8PJAFotC62UY5dFfIGEPr7WswzQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/babel-preset-fbjs": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/babel-preset-fbjs/-/babel-preset-fbjs-3.4.0.tgz", - "integrity": "sha512-9ywCsCvo1ojrw0b+XYk7aFvTH6D9064t0RIL1rtMf3nsa02Xw41MS7sZw216Im35xj/UY0PDBQsa1brUDDF1Ow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-proposal-class-properties": "^7.0.0", - "@babel/plugin-proposal-object-rest-spread": "^7.0.0", - "@babel/plugin-syntax-class-properties": "^7.0.0", - "@babel/plugin-syntax-flow": "^7.0.0", - "@babel/plugin-syntax-jsx": "^7.0.0", - "@babel/plugin-syntax-object-rest-spread": "^7.0.0", - "@babel/plugin-transform-arrow-functions": "^7.0.0", - "@babel/plugin-transform-block-scoped-functions": "^7.0.0", - "@babel/plugin-transform-block-scoping": "^7.0.0", - "@babel/plugin-transform-classes": "^7.0.0", - "@babel/plugin-transform-computed-properties": "^7.0.0", - "@babel/plugin-transform-destructuring": "^7.0.0", - "@babel/plugin-transform-flow-strip-types": "^7.0.0", - "@babel/plugin-transform-for-of": "^7.0.0", - "@babel/plugin-transform-function-name": "^7.0.0", - "@babel/plugin-transform-literals": "^7.0.0", - "@babel/plugin-transform-member-expression-literals": "^7.0.0", - "@babel/plugin-transform-modules-commonjs": "^7.0.0", - "@babel/plugin-transform-object-super": "^7.0.0", - "@babel/plugin-transform-parameters": "^7.0.0", - "@babel/plugin-transform-property-literals": "^7.0.0", - "@babel/plugin-transform-react-display-name": "^7.0.0", - "@babel/plugin-transform-react-jsx": "^7.0.0", - "@babel/plugin-transform-shorthand-properties": "^7.0.0", - "@babel/plugin-transform-spread": "^7.0.0", - "@babel/plugin-transform-template-literals": "^7.0.0", - "babel-plugin-syntax-trailing-function-commas": "^7.0.0-beta.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", @@ -3990,6 +3352,7 @@ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.31.tgz", "integrity": "sha512-a28v2eWrrRWPpJSzxc+mKwm0ZtVx/G8SepdQZDArnXYU/XS+IF6mp8aB/4E+hH1tyGCoDo3KlUCdlSxGDsRkAw==", "dev": true, + "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.js" } @@ -4024,6 +3387,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4034,6 +3398,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -4060,6 +3425,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", @@ -4115,6 +3481,7 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -4130,16 +3497,6 @@ "tslib": "^2.0.3" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", @@ -4151,9 +3508,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001756", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001756.tgz", - "integrity": "sha512-4HnCNKbMLkLdhJz3TToeVWHSnfJvPaq6vu/eRP0Ahub/07n484XHhBF5AJoSGHdVrS8tKFauUQz8Bp9P7LVx7A==", + "version": "1.0.30001757", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001757.tgz", + "integrity": "sha512-r0nnL/I28Zi/yjk1el6ilj27tKcdjLsNqAOZr0yVjWPrSQyHgKI2INaEWw21bAQSv2LXRt1XuCS/GomNpWOxsQ==", "dev": true, "funding": [ { @@ -4168,7 +3525,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/capital-case": { "version": "1.0.4", @@ -4187,6 +3545,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -4415,6 +3774,7 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, @@ -4426,7 +3786,8 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/colorette": { "version": "2.0.20", @@ -4459,7 +3820,8 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/constant-case": { "version": "3.0.4", @@ -4477,7 +3839,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cosmiconfig": { "version": "8.3.6", @@ -4534,6 +3897,7 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4560,7 +3924,8 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "devOptional": true + "devOptional": true, + "license": "MIT" }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", @@ -4591,6 +3956,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.3" }, @@ -4603,21 +3969,12 @@ } } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/defaults": { "version": "1.0.4", @@ -4714,10 +4071,11 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.259", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.259.tgz", - "integrity": "sha512-I+oLXgpEJzD6Cwuwt1gYjxsDmu/S/Kd41mmLA3O+/uH2pFRO/DvOjUyGozL8j3KeLV6WyZ7ssPwELMsXCcsJAQ==", - "dev": true + "version": "1.5.260", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.260.tgz", + "integrity": "sha512-ov8rBoOBhVawpzdre+Cmz4FB+y66Eqrk6Gwqd8NGxuhv99GQ8XqMAr351KEkOt7gukXWDg6gJWEMKgL2RLMPtA==", + "dev": true, + "license": "ISC" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -4742,6 +4100,7 @@ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, @@ -4782,6 +4141,7 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -4791,6 +4151,7 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -4803,6 +4164,7 @@ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.1.tgz", "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", @@ -4863,6 +4225,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.24.4", "@babel/parser": "^7.24.4", @@ -4882,6 +4245,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.24.tgz", "integrity": "sha512-nLHIW7TEq3aLrEYWpVaJ1dRgFR+wLDPN8e8FpYAql/bMV2oBEfC37K0gLEGgv9fy66juNShSMV8OkTqzltcG/w==", "dev": true, + "license": "MIT", "peerDependencies": { "eslint": ">=8.40" } @@ -4891,6 +4255,7 @@ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -4907,6 +4272,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -4919,6 +4285,7 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", @@ -4936,6 +4303,7 @@ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -4948,6 +4316,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -4960,6 +4329,7 @@ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -4969,6 +4339,7 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -4977,13 +4348,15 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -5000,6 +4373,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -5011,19 +4385,22 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fastq": { "version": "1.19.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -5116,6 +4493,7 @@ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, + "license": "MIT", "dependencies": { "flat-cache": "^4.0.0" }, @@ -5128,6 +4506,7 @@ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -5140,6 +4519,7 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -5156,6 +4536,7 @@ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" @@ -5168,7 +4549,8 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/formdata-polyfill": { "version": "4.0.10", @@ -5197,19 +4579,13 @@ "url": "https://github.com/sponsors/rawify" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -5233,6 +4609,7 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -5247,33 +4624,12 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, @@ -5286,6 +4642,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -5318,12 +4675,14 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/graphql": { "version": "16.12.0", "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.12.0.tgz", "integrity": "sha512-DKKrynuQRne0PNpEbzuEdHlYOMksHSUI8Zc9Unei5gTsMNA2/vMpoMz/yKba50pejK56qj98qM0SjYxAKi13gQ==", + "license": "MIT", "peer": true, "engines": { "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" @@ -5415,6 +4774,7 @@ "version": "2.12.6", "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz", "integrity": "sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==", + "license": "MIT", "dependencies": { "tslib": "^2.1.0" }, @@ -5462,6 +4822,7 @@ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -5494,13 +4855,15 @@ "version": "0.25.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/hermes-parser": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", "dev": true, + "license": "MIT", "dependencies": { "hermes-estree": "0.25.1" } @@ -5576,6 +4939,7 @@ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -5595,6 +4959,7 @@ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -5606,6 +4971,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-fresh/node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/import-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/import-from/-/import-from-4.0.0.tgz", @@ -5624,6 +4999,7 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } @@ -5638,18 +5014,6 @@ "node": ">=8" } }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -5749,6 +5113,7 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5768,6 +5133,7 @@ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -5800,6 +5166,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -5867,7 +5234,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/isomorphic-ws": { "version": "5.0.0", @@ -5903,13 +5271,15 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -5922,6 +5292,7 @@ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -5933,7 +5304,8 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", @@ -5946,13 +5318,15 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-to-pretty-yaml": { "version": "1.2.2", @@ -5973,6 +5347,7 @@ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -5994,6 +5369,7 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } @@ -6003,6 +5379,7 @@ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" @@ -6082,6 +5459,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -6103,7 +5481,8 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.sortby": { "version": "4.7.0", @@ -6204,6 +5583,7 @@ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, + "license": "ISC", "dependencies": { "yallist": "^3.0.2" } @@ -6223,6 +5603,7 @@ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } @@ -6250,6 +5631,7 @@ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" @@ -6273,6 +5655,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -6284,7 +5667,8 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/mute-stream": { "version": "0.0.8", @@ -6316,6 +5700,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -6327,7 +5712,8 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/no-case": { "version": "3.0.4", @@ -6393,7 +5779,8 @@ "version": "2.0.27", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -6454,16 +5841,6 @@ "node": ">=18" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -6484,6 +5861,7 @@ "version": "0.18.1", "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.18.1.tgz", "integrity": "sha512-mLXNwWPa9dgFyDqkNi54sjDyNJ9/fTI6WGBLgnXku1vdKY/jovHfZT5r+aiVeFFLOz+foPNOm5YJ4mqgld2GBQ==", + "license": "MIT", "dependencies": { "@wry/caches": "^1.0.0", "@wry/context": "^0.7.0", @@ -6496,6 +5874,7 @@ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", @@ -6537,6 +5916,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -6552,6 +5932,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -6578,16 +5959,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/param-case": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", @@ -6604,6 +5975,7 @@ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -6672,18 +6044,9 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, "node_modules/path-key": { @@ -6691,6 +6054,7 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -6739,13 +6103,15 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -6792,6 +6158,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "peer": true, "dependencies": { "nanoid": "^3.3.11", @@ -6941,6 +6308,7 @@ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8.0" } @@ -6960,6 +6328,7 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -6982,12 +6351,14 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/react": { "version": "19.2.0", "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz", "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==", + "license": "MIT", "peer": true, "engines": { "node": ">=0.10.0" @@ -6997,6 +6368,7 @@ "version": "19.2.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz", "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==", + "license": "MIT", "peer": true, "dependencies": { "scheduler": "^0.27.0" @@ -7010,6 +6382,7 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -7130,13 +6503,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true, - "license": "ISC" - }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -7159,12 +6525,13 @@ } }, "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/restore-cursor": { @@ -7186,6 +6553,7 @@ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, + "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" @@ -7203,6 +6571,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "1.0.8" }, @@ -7268,6 +6637,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "queue-microtask": "^1.2.2" } @@ -7276,6 +6646,7 @@ "version": "7.8.2", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", "peer": true, "dependencies": { "tslib": "^2.1.0" @@ -7312,7 +6683,8 @@ "node_modules/scheduler": { "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==" + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" }, "node_modules/scuid": { "version": "1.1.0", @@ -7326,6 +6698,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -7342,13 +6715,6 @@ "upper-case-first": "^2.0.2" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true, - "license": "ISC" - }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -7361,6 +6727,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -7373,6 +6740,7 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -7445,6 +6813,7 @@ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -7509,6 +6878,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -7544,6 +6914,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -7712,6 +7083,7 @@ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, + "license": "MIT", "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" @@ -7728,6 +7100,7 @@ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.0.0" }, @@ -7745,6 +7118,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=12" @@ -7768,6 +7142,7 @@ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -7787,6 +7162,7 @@ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18.12" }, @@ -7811,13 +7187,15 @@ "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, + "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, @@ -7843,6 +7221,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, + "license": "Apache-2.0", "peer": true, "bin": { "tsc": "bin/tsc", @@ -7853,15 +7232,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.47.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.47.0.tgz", - "integrity": "sha512-Lwe8i2XQ3WoMjua/r1PHrCTpkubPYJCAfOurtn+mtTzqB6jNd+14n9UN1bJ4s3F49x9ixAm0FLflB/JzQ57M8Q==", + "version": "8.48.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.48.0.tgz", + "integrity": "sha512-fcKOvQD9GUn3Xw63EgiDqhvWJ5jsyZUaekl3KVpGsDJnN46WJTe3jWxtQP9lMZm1LJNkFLlTaWAxK2vUQR+cqw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.47.0", - "@typescript-eslint/parser": "8.47.0", - "@typescript-eslint/typescript-estree": "8.47.0", - "@typescript-eslint/utils": "8.47.0" + "@typescript-eslint/eslint-plugin": "8.48.0", + "@typescript-eslint/parser": "8.48.0", + "@typescript-eslint/typescript-estree": "8.48.0", + "@typescript-eslint/utils": "8.48.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -7916,7 +7296,8 @@ "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/unixify": { "version": "1.0.0", @@ -7963,6 +7344,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" @@ -7999,6 +7381,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -8022,6 +7405,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-7.2.4.tgz", "integrity": "sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==", "dev": true, + "license": "MIT", "peer": true, "dependencies": { "esbuild": "^0.25.0", @@ -8097,6 +7481,7 @@ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.0.0" }, @@ -8114,6 +7499,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, + "license": "MIT", "peer": true, "engines": { "node": ">=12" @@ -8175,6 +7561,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -8185,18 +7572,12 @@ "node": ">= 8" } }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true, - "license": "ISC" - }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -8216,13 +7597,6 @@ "node": ">=8" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, "node_modules/ws": { "version": "8.18.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", @@ -8260,7 +7634,8 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/yaml": { "version": "2.8.1", @@ -8316,6 +7691,7 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -8324,10 +7700,11 @@ } }, "node_modules/zod": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.12.tgz", - "integrity": "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==", + "version": "4.1.13", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.13.tgz", + "integrity": "sha512-AvvthqfqrAhNH9dnfmrfKzX5upOdjUVJYFqNSlkmGf64gRaTzlPwz99IHYnVs28qYAybvAlBV+H7pn0saFY4Ig==", "dev": true, + "license": "MIT", "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" @@ -8338,6 +7715,7 @@ "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18.0.0" }, diff --git a/fictionarchive-web/package.json b/fictionarchive-web/package.json index 85f49c7..a7863bc 100644 --- a/fictionarchive-web/package.json +++ b/fictionarchive-web/package.json @@ -16,18 +16,18 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "graphql": "^16.12.0", - "react-router-dom": "^6.27.0", "oidc-client-ts": "^3.4.1", "react": "^19.2.0", "react-dom": "^19.2.0", + "react-router-dom": "^6.27.0", "tailwind-merge": "^2.5.4" }, "devDependencies": { + "@eslint/js": "^9.39.1", "@graphql-codegen/cli": "^5.0.3", + "@graphql-codegen/typed-document-node": "^6.1.1", "@graphql-codegen/typescript": "^4.0.9", "@graphql-codegen/typescript-operations": "^4.0.9", - "@graphql-codegen/typescript-react-apollo": "^4.0.9", - "@eslint/js": "^9.39.1", "@types/node": "^24.10.1", "@types/react": "^19.2.5", "@types/react-dom": "^19.2.3", diff --git a/fictionarchive-web/src/__generated__/graphql.ts b/fictionarchive-web/src/__generated__/graphql.ts index 691f49f..1780522 100644 --- a/fictionarchive-web/src/__generated__/graphql.ts +++ b/fictionarchive-web/src/__generated__/graphql.ts @@ -1,6 +1,4 @@ -import { gql } from '@apollo/client'; -import * as Apollo from '@apollo/client'; -import * as ApolloReactHooks from '@apollo/client/react'; +import type { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/core'; export type Maybe = T | null; export type InputMaybe = T | null; export type Exact = { [K in keyof T]: T[K] }; @@ -8,7 +6,6 @@ export type MakeOptional = Omit & { [SubKey in K]?: export type MakeMaybe = Omit & { [SubKey in K]: Maybe }; export type MakeEmpty = { [_ in K]?: never }; export type Incremental = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never }; -const defaultOptions = {} as const; /** All built-in and custom scalars, mapped to their actual values */ export type Scalars = { ID: { input: string; output: string; } @@ -34,17 +31,17 @@ export type Chapter = { }; export type ChapterFilterInput = { - and: InputMaybe>; - body: InputMaybe; - createdTime: InputMaybe; - id: InputMaybe; - images: InputMaybe; - lastUpdatedTime: InputMaybe; - name: InputMaybe; - or: InputMaybe>; - order: InputMaybe; - revision: InputMaybe; - url: InputMaybe; + and?: InputMaybe>; + body?: InputMaybe; + createdTime?: InputMaybe; + id?: InputMaybe; + images?: InputMaybe; + lastUpdatedTime?: InputMaybe; + name?: InputMaybe; + or?: InputMaybe>; + order?: InputMaybe; + revision?: InputMaybe; + url?: InputMaybe; }; export type ChapterPullRequestedEvent = { @@ -53,14 +50,14 @@ export type ChapterPullRequestedEvent = { }; export type ChapterSortInput = { - body: InputMaybe; - createdTime: InputMaybe; - id: InputMaybe; - lastUpdatedTime: InputMaybe; - name: InputMaybe; - order: InputMaybe; - revision: InputMaybe; - url: InputMaybe; + body?: InputMaybe; + createdTime?: InputMaybe; + id?: InputMaybe; + lastUpdatedTime?: InputMaybe; + name?: InputMaybe; + order?: InputMaybe; + revision?: InputMaybe; + url?: InputMaybe; }; export type DeleteJobError = KeyNotFoundError; @@ -105,23 +102,23 @@ export type Image = { }; export type ImageFilterInput = { - and: InputMaybe>; - chapter: InputMaybe; - createdTime: InputMaybe; - id: InputMaybe; - lastUpdatedTime: InputMaybe; - newPath: InputMaybe; - or: InputMaybe>; - originalPath: InputMaybe; + and?: InputMaybe>; + chapter?: InputMaybe; + createdTime?: InputMaybe; + id?: InputMaybe; + lastUpdatedTime?: InputMaybe; + newPath?: InputMaybe; + or?: InputMaybe>; + originalPath?: InputMaybe; }; export type ImageSortInput = { - chapter: InputMaybe; - createdTime: InputMaybe; - id: InputMaybe; - lastUpdatedTime: InputMaybe; - newPath: InputMaybe; - originalPath: InputMaybe; + chapter?: InputMaybe; + createdTime?: InputMaybe; + id?: InputMaybe; + lastUpdatedTime?: InputMaybe; + newPath?: InputMaybe; + originalPath?: InputMaybe; }; export type ImportNovelInput = { @@ -133,8 +130,8 @@ export type ImportNovelPayload = { }; export type InstantFilterInput = { - and: InputMaybe>; - or: InputMaybe>; + and?: InputMaybe>; + or?: InputMaybe>; }; export type JobKey = { @@ -155,53 +152,54 @@ export type KeyValuePairOfStringAndString = { value: Scalars['String']['output']; }; -export enum Language { - Ch = 'CH', - En = 'EN', - Ja = 'JA', - Kr = 'KR' -} +export const Language = { + Ch: 'CH', + En: 'EN', + Ja: 'JA', + Kr: 'KR' +} as const; +export type Language = typeof Language[keyof typeof Language]; export type LanguageOperationFilterInput = { - eq: InputMaybe; - in: InputMaybe>; - neq: InputMaybe; - nin: InputMaybe>; + eq?: InputMaybe; + in?: InputMaybe>; + neq?: InputMaybe; + nin?: InputMaybe>; }; export type ListFilterInputTypeOfChapterFilterInput = { - all: InputMaybe; - any: InputMaybe; - none: InputMaybe; - some: InputMaybe; + all?: InputMaybe; + any?: InputMaybe; + none?: InputMaybe; + some?: InputMaybe; }; export type ListFilterInputTypeOfImageFilterInput = { - all: InputMaybe; - any: InputMaybe; - none: InputMaybe; - some: InputMaybe; + all?: InputMaybe; + any?: InputMaybe; + none?: InputMaybe; + some?: InputMaybe; }; export type ListFilterInputTypeOfLocalizationTextFilterInput = { - all: InputMaybe; - any: InputMaybe; - none: InputMaybe; - some: InputMaybe; + all?: InputMaybe; + any?: InputMaybe; + none?: InputMaybe; + some?: InputMaybe; }; export type ListFilterInputTypeOfNovelFilterInput = { - all: InputMaybe; - any: InputMaybe; - none: InputMaybe; - some: InputMaybe; + all?: InputMaybe; + any?: InputMaybe; + none?: InputMaybe; + some?: InputMaybe; }; export type ListFilterInputTypeOfNovelTagFilterInput = { - all: InputMaybe; - any: InputMaybe; - none: InputMaybe; - some: InputMaybe; + all?: InputMaybe; + any?: InputMaybe; + none?: InputMaybe; + some?: InputMaybe; }; export type LocalizationKey = { @@ -212,18 +210,18 @@ export type LocalizationKey = { }; export type LocalizationKeyFilterInput = { - and: InputMaybe>; - createdTime: InputMaybe; - id: InputMaybe; - lastUpdatedTime: InputMaybe; - or: InputMaybe>; - texts: InputMaybe; + and?: InputMaybe>; + createdTime?: InputMaybe; + id?: InputMaybe; + lastUpdatedTime?: InputMaybe; + or?: InputMaybe>; + texts?: InputMaybe; }; export type LocalizationKeySortInput = { - createdTime: InputMaybe; - id: InputMaybe; - lastUpdatedTime: InputMaybe; + createdTime?: InputMaybe; + id?: InputMaybe; + lastUpdatedTime?: InputMaybe; }; export type LocalizationText = { @@ -236,14 +234,14 @@ export type LocalizationText = { }; export type LocalizationTextFilterInput = { - and: InputMaybe>; - createdTime: InputMaybe; - id: InputMaybe; - language: InputMaybe; - lastUpdatedTime: InputMaybe; - or: InputMaybe>; - text: InputMaybe; - translationEngine: InputMaybe; + and?: InputMaybe>; + createdTime?: InputMaybe; + id?: InputMaybe; + language?: InputMaybe; + lastUpdatedTime?: InputMaybe; + or?: InputMaybe>; + text?: InputMaybe; + translationEngine?: InputMaybe; }; export type Mutation = { @@ -310,54 +308,55 @@ export type Novel = { }; export type NovelFilterInput = { - and: InputMaybe>; - author: InputMaybe; - chapters: InputMaybe; - coverImage: InputMaybe; - createdTime: InputMaybe; - description: InputMaybe; - externalId: InputMaybe; - id: InputMaybe; - lastUpdatedTime: InputMaybe; - name: InputMaybe; - or: InputMaybe>; - rawLanguage: InputMaybe; - rawStatus: InputMaybe; - source: InputMaybe; - statusOverride: InputMaybe; - tags: InputMaybe; - url: InputMaybe; + and?: InputMaybe>; + author?: InputMaybe; + chapters?: InputMaybe; + coverImage?: InputMaybe; + createdTime?: InputMaybe; + description?: InputMaybe; + externalId?: InputMaybe; + id?: InputMaybe; + lastUpdatedTime?: InputMaybe; + name?: InputMaybe; + or?: InputMaybe>; + rawLanguage?: InputMaybe; + rawStatus?: InputMaybe; + source?: InputMaybe; + statusOverride?: InputMaybe; + tags?: InputMaybe; + url?: InputMaybe; }; export type NovelSortInput = { - author: InputMaybe; - coverImage: InputMaybe; - createdTime: InputMaybe; - description: InputMaybe; - externalId: InputMaybe; - id: InputMaybe; - lastUpdatedTime: InputMaybe; - name: InputMaybe; - rawLanguage: InputMaybe; - rawStatus: InputMaybe; - source: InputMaybe; - statusOverride: InputMaybe; - url: InputMaybe; + author?: InputMaybe; + coverImage?: InputMaybe; + createdTime?: InputMaybe; + description?: InputMaybe; + externalId?: InputMaybe; + id?: InputMaybe; + lastUpdatedTime?: InputMaybe; + name?: InputMaybe; + rawLanguage?: InputMaybe; + rawStatus?: InputMaybe; + source?: InputMaybe; + statusOverride?: InputMaybe; + url?: InputMaybe; }; -export enum NovelStatus { - Abandoned = 'ABANDONED', - Completed = 'COMPLETED', - Hiatus = 'HIATUS', - InProgress = 'IN_PROGRESS', - Unknown = 'UNKNOWN' -} +export const NovelStatus = { + Abandoned: 'ABANDONED', + Completed: 'COMPLETED', + Hiatus: 'HIATUS', + InProgress: 'IN_PROGRESS', + Unknown: 'UNKNOWN' +} as const; +export type NovelStatus = typeof NovelStatus[keyof typeof NovelStatus]; export type NovelStatusOperationFilterInput = { - eq: InputMaybe; - in: InputMaybe>; - neq: InputMaybe; - nin: InputMaybe>; + eq?: InputMaybe; + in?: InputMaybe>; + neq?: InputMaybe; + nin?: InputMaybe>; }; export type NovelTag = { @@ -372,16 +371,16 @@ export type NovelTag = { }; export type NovelTagFilterInput = { - and: InputMaybe>; - createdTime: InputMaybe; - displayName: InputMaybe; - id: InputMaybe; - key: InputMaybe; - lastUpdatedTime: InputMaybe; - novels: InputMaybe; - or: InputMaybe>; - source: InputMaybe; - tagType: InputMaybe; + and?: InputMaybe>; + createdTime?: InputMaybe; + displayName?: InputMaybe; + id?: InputMaybe; + key?: InputMaybe; + lastUpdatedTime?: InputMaybe; + novels?: InputMaybe; + or?: InputMaybe>; + source?: InputMaybe; + tagType?: InputMaybe; }; export type NovelUpdateRequestedEvent = { @@ -407,10 +406,10 @@ export type NovelsEdge = { }; export type NullableOfNovelStatusOperationFilterInput = { - eq: InputMaybe; - in: InputMaybe>>; - neq: InputMaybe; - nin: InputMaybe>>; + eq?: InputMaybe; + in?: InputMaybe>>; + neq?: InputMaybe; + nin?: InputMaybe>>; }; /** Information about pagination in a connection. */ @@ -434,21 +433,21 @@ export type Person = { }; export type PersonFilterInput = { - and: InputMaybe>; - createdTime: InputMaybe; - externalUrl: InputMaybe; - id: InputMaybe; - lastUpdatedTime: InputMaybe; - name: InputMaybe; - or: InputMaybe>; + and?: InputMaybe>; + createdTime?: InputMaybe; + externalUrl?: InputMaybe; + id?: InputMaybe; + lastUpdatedTime?: InputMaybe; + name?: InputMaybe; + or?: InputMaybe>; }; export type PersonSortInput = { - createdTime: InputMaybe; - externalUrl: InputMaybe; - id: InputMaybe; - lastUpdatedTime: InputMaybe; - name: InputMaybe; + createdTime?: InputMaybe; + externalUrl?: InputMaybe; + id?: InputMaybe; + lastUpdatedTime?: InputMaybe; + name?: InputMaybe; }; export type Query = { @@ -461,33 +460,33 @@ export type Query = { export type QueryNovelsArgs = { - after: InputMaybe; - before: InputMaybe; - first: InputMaybe; - last: InputMaybe; - order: InputMaybe>; - where: InputMaybe; + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; + order?: InputMaybe>; + where?: InputMaybe; }; export type QueryTranslationEnginesArgs = { - order: InputMaybe>; - where: InputMaybe; + order?: InputMaybe>; + where?: InputMaybe; }; export type QueryTranslationRequestsArgs = { - after: InputMaybe; - before: InputMaybe; - first: InputMaybe; - last: InputMaybe; - order: InputMaybe>; - where: InputMaybe; + after?: InputMaybe; + before?: InputMaybe; + first?: InputMaybe; + last?: InputMaybe; + order?: InputMaybe>; + where?: InputMaybe; }; export type RegisterUserInput = { email: Scalars['String']['input']; - inviterOAuthProviderId: InputMaybe; + inviterOAuthProviderId?: InputMaybe; oAuthProviderId: Scalars['String']['input']; username: Scalars['String']['input']; }; @@ -530,11 +529,12 @@ export type SchedulerJob = { jobTypeName: Scalars['String']['output']; }; -export enum SortEnumType { - Asc = 'ASC', - Desc = 'DESC' -} +export const SortEnumType = { + Asc: 'ASC', + Desc: 'DESC' +} as const; +export type SortEnumType = typeof SortEnumType[keyof typeof SortEnumType]; export type Source = { createdTime: Scalars['Instant']['output']; id: Scalars['UnsignedInt']['output']; @@ -545,52 +545,53 @@ export type Source = { }; export type SourceFilterInput = { - and: InputMaybe>; - createdTime: InputMaybe; - id: InputMaybe; - key: InputMaybe; - lastUpdatedTime: InputMaybe; - name: InputMaybe; - or: InputMaybe>; - url: InputMaybe; + and?: InputMaybe>; + createdTime?: InputMaybe; + id?: InputMaybe; + key?: InputMaybe; + lastUpdatedTime?: InputMaybe; + name?: InputMaybe; + or?: InputMaybe>; + url?: InputMaybe; }; export type SourceSortInput = { - createdTime: InputMaybe; - id: InputMaybe; - key: InputMaybe; - lastUpdatedTime: InputMaybe; - name: InputMaybe; - url: InputMaybe; + createdTime?: InputMaybe; + id?: InputMaybe; + key?: InputMaybe; + lastUpdatedTime?: InputMaybe; + name?: InputMaybe; + url?: InputMaybe; }; export type StringOperationFilterInput = { - and: InputMaybe>; - contains: InputMaybe; - endsWith: InputMaybe; - eq: InputMaybe; - in: InputMaybe>>; - ncontains: InputMaybe; - nendsWith: InputMaybe; - neq: InputMaybe; - nin: InputMaybe>>; - nstartsWith: InputMaybe; - or: InputMaybe>; - startsWith: InputMaybe; + and?: InputMaybe>; + contains?: InputMaybe; + endsWith?: InputMaybe; + eq?: InputMaybe; + in?: InputMaybe>>; + ncontains?: InputMaybe; + nendsWith?: InputMaybe; + neq?: InputMaybe; + nin?: InputMaybe>>; + nstartsWith?: InputMaybe; + or?: InputMaybe>; + startsWith?: InputMaybe; }; -export enum TagType { - External = 'EXTERNAL', - Genre = 'GENRE', - System = 'SYSTEM', - UserDefined = 'USER_DEFINED' -} +export const TagType = { + External: 'EXTERNAL', + Genre: 'GENRE', + System: 'SYSTEM', + UserDefined: 'USER_DEFINED' +} as const; +export type TagType = typeof TagType[keyof typeof TagType]; export type TagTypeOperationFilterInput = { - eq: InputMaybe; - in: InputMaybe>; - neq: InputMaybe; - nin: InputMaybe>; + eq?: InputMaybe; + in?: InputMaybe>; + neq?: InputMaybe; + nin?: InputMaybe>; }; export type TranslateTextInput = { @@ -617,24 +618,24 @@ export type TranslationEngineDescriptor = { }; export type TranslationEngineDescriptorFilterInput = { - and: InputMaybe>; - displayName: InputMaybe; - key: InputMaybe; - or: InputMaybe>; + and?: InputMaybe>; + displayName?: InputMaybe; + key?: InputMaybe; + or?: InputMaybe>; }; export type TranslationEngineDescriptorSortInput = { - displayName: InputMaybe; - key: InputMaybe; + displayName?: InputMaybe; + key?: InputMaybe; }; export type TranslationEngineFilterInput = { - and: InputMaybe>; - createdTime: InputMaybe; - id: InputMaybe; - key: InputMaybe; - lastUpdatedTime: InputMaybe; - or: InputMaybe>; + and?: InputMaybe>; + createdTime?: InputMaybe; + id?: InputMaybe; + key?: InputMaybe; + lastUpdatedTime?: InputMaybe; + or?: InputMaybe>; }; export type TranslationRequest = { @@ -651,44 +652,45 @@ export type TranslationRequest = { }; export type TranslationRequestFilterInput = { - and: InputMaybe>; - billedCharacterCount: InputMaybe; - createdTime: InputMaybe; - from: InputMaybe; - id: InputMaybe; - lastUpdatedTime: InputMaybe; - or: InputMaybe>; - originalText: InputMaybe; - status: InputMaybe; - to: InputMaybe; - translatedText: InputMaybe; - translationEngineKey: InputMaybe; + and?: InputMaybe>; + billedCharacterCount?: InputMaybe; + createdTime?: InputMaybe; + from?: InputMaybe; + id?: InputMaybe; + lastUpdatedTime?: InputMaybe; + or?: InputMaybe>; + originalText?: InputMaybe; + status?: InputMaybe; + to?: InputMaybe; + translatedText?: InputMaybe; + translationEngineKey?: InputMaybe; }; export type TranslationRequestSortInput = { - billedCharacterCount: InputMaybe; - createdTime: InputMaybe; - from: InputMaybe; - id: InputMaybe; - lastUpdatedTime: InputMaybe; - originalText: InputMaybe; - status: InputMaybe; - to: InputMaybe; - translatedText: InputMaybe; - translationEngineKey: InputMaybe; + billedCharacterCount?: InputMaybe; + createdTime?: InputMaybe; + from?: InputMaybe; + id?: InputMaybe; + lastUpdatedTime?: InputMaybe; + originalText?: InputMaybe; + status?: InputMaybe; + to?: InputMaybe; + translatedText?: InputMaybe; + translationEngineKey?: InputMaybe; }; -export enum TranslationRequestStatus { - Failed = 'FAILED', - Pending = 'PENDING', - Success = 'SUCCESS' -} +export const TranslationRequestStatus = { + Failed: 'FAILED', + Pending: 'PENDING', + Success: 'SUCCESS' +} as const; +export type TranslationRequestStatus = typeof TranslationRequestStatus[keyof typeof TranslationRequestStatus]; export type TranslationRequestStatusOperationFilterInput = { - eq: InputMaybe; - in: InputMaybe>; - neq: InputMaybe; - nin: InputMaybe>; + eq?: InputMaybe; + in?: InputMaybe>; + neq?: InputMaybe; + nin?: InputMaybe>; }; /** A connection to a list of items. */ @@ -720,18 +722,18 @@ export type TranslationResult = { }; export type UnsignedIntOperationFilterInputType = { - eq: InputMaybe; - gt: InputMaybe; - gte: InputMaybe; - in: InputMaybe>>; - lt: InputMaybe; - lte: InputMaybe; - neq: InputMaybe; - ngt: InputMaybe; - ngte: InputMaybe; - nin: InputMaybe>>; - nlt: InputMaybe; - nlte: InputMaybe; + eq?: InputMaybe; + gt?: InputMaybe; + gte?: InputMaybe; + in?: InputMaybe>>; + lt?: InputMaybe; + lte?: InputMaybe; + neq?: InputMaybe; + ngt?: InputMaybe; + ngte?: InputMaybe; + nin?: InputMaybe>>; + nlt?: InputMaybe; + nlte?: InputMaybe; }; export type User = { @@ -746,93 +748,27 @@ export type User = { }; export type UuidOperationFilterInput = { - eq: InputMaybe; - gt: InputMaybe; - gte: InputMaybe; - in: InputMaybe>>; - lt: InputMaybe; - lte: InputMaybe; - neq: InputMaybe; - ngt: InputMaybe; - ngte: InputMaybe; - nin: InputMaybe>>; - nlt: InputMaybe; - nlte: InputMaybe; + eq?: InputMaybe; + gt?: InputMaybe; + gte?: InputMaybe; + in?: InputMaybe>>; + lt?: InputMaybe; + lte?: InputMaybe; + neq?: InputMaybe; + ngt?: InputMaybe; + ngte?: InputMaybe; + nin?: InputMaybe>>; + nlt?: InputMaybe; + nlte?: InputMaybe; }; export type NovelsQueryVariables = Exact<{ - first: InputMaybe; - after: InputMaybe; + first?: InputMaybe; + after?: InputMaybe; }>; export type NovelsQuery = { novels: { edges: Array<{ cursor: string, node: { id: any, url: string, name: { texts: Array<{ language: Language, text: string }> }, description: { texts: Array<{ language: Language, text: string }> }, coverImage: { originalPath: string, newPath: string | null } | null } }> | null, pageInfo: { hasNextPage: boolean, endCursor: string | null } } | null }; -export const NovelsDocument = gql` - query Novels($first: Int, $after: String) { - novels(first: $first, after: $after) { - edges { - cursor - node { - id - url - name { - texts { - language - text - } - } - description { - texts { - language - text - } - } - coverImage { - originalPath - newPath - } - } - } - pageInfo { - hasNextPage - endCursor - } - } -} - `; - -/** - * __useNovelsQuery__ - * - * To run a query within a React component, call `useNovelsQuery` and pass it any options that fit your needs. - * When your component renders, `useNovelsQuery` returns an object from Apollo Client that contains loading, error, and data properties - * you can use to render your UI. - * - * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; - * - * @example - * const { data, loading, error } = useNovelsQuery({ - * variables: { - * first: // value for 'first' - * after: // value for 'after' - * }, - * }); - */ -export function useNovelsQuery(baseOptions?: ApolloReactHooks.QueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return ApolloReactHooks.useQuery(NovelsDocument, options); - } -export function useNovelsLazyQuery(baseOptions?: ApolloReactHooks.LazyQueryHookOptions) { - const options = {...defaultOptions, ...baseOptions} - return ApolloReactHooks.useLazyQuery(NovelsDocument, options); - } -export function useNovelsSuspenseQuery(baseOptions?: ApolloReactHooks.SkipToken | ApolloReactHooks.SuspenseQueryHookOptions) { - const options = baseOptions === ApolloReactHooks.skipToken ? baseOptions : {...defaultOptions, ...baseOptions} - return ApolloReactHooks.useSuspenseQuery(NovelsDocument, options); - } -export type NovelsQueryHookResult = ReturnType; -export type NovelsLazyQueryHookResult = ReturnType; -export type NovelsSuspenseQueryHookResult = ReturnType; -export type NovelsQueryResult = Apollo.QueryResult; \ No newline at end of file +export const NovelsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"Novels"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"first"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"after"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"novels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"first"},"value":{"kind":"Variable","name":{"kind":"Name","value":"first"}}},{"kind":"Argument","name":{"kind":"Name","value":"after"},"value":{"kind":"Variable","name":{"kind":"Name","value":"after"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"edges"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"cursor"}},{"kind":"Field","name":{"kind":"Name","value":"node"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"url"}},{"kind":"Field","name":{"kind":"Name","value":"name"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"texts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"language"}},{"kind":"Field","name":{"kind":"Name","value":"text"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"description"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"texts"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"language"}},{"kind":"Field","name":{"kind":"Name","value":"text"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"coverImage"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"originalPath"}},{"kind":"Field","name":{"kind":"Name","value":"newPath"}}]}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"pageInfo"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"hasNextPage"}},{"kind":"Field","name":{"kind":"Name","value":"endCursor"}}]}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/fictionarchive-web/src/components/NovelCard.tsx b/fictionarchive-web/src/components/NovelCard.tsx index 4a5db75..5f3a807 100644 --- a/fictionarchive-web/src/components/NovelCard.tsx +++ b/fictionarchive-web/src/components/NovelCard.tsx @@ -1,11 +1,13 @@ -import type { Novel } from '../__generated__/graphql' +import type { NovelsQuery } from '../__generated__/graphql' import { Card, CardContent, CardHeader, CardTitle } from './ui/card' +type NovelNode = NonNullable['edges']>[number]['node'] + type NovelCardProps = { - novel: Novel + novel: NovelNode } -function pickText(novelText?: Novel['name'] | Novel['description']) { +function pickText(novelText?: NovelNode['name'] | NovelNode['description']) { const texts = novelText?.texts ?? [] const english = texts.find((t) => t.language === 'EN') return (english ?? texts[0])?.text ?? 'No description available.' diff --git a/fictionarchive-web/src/pages/NovelsPage.tsx b/fictionarchive-web/src/pages/NovelsPage.tsx index 21171ca..e93208f 100644 --- a/fictionarchive-web/src/pages/NovelsPage.tsx +++ b/fictionarchive-web/src/pages/NovelsPage.tsx @@ -1,6 +1,7 @@ import { useMemo } from 'react' -import { useNovelsQuery } from '../__generated__/graphql' +import { useQuery } from '@apollo/client/react' +import { NovelsDocument } from '../__generated__/graphql' import { NovelCard } from '../components/NovelCard' import { Button } from '../components/ui/button' import { Card, CardContent, CardHeader, CardTitle } from '../components/ui/card' @@ -8,7 +9,7 @@ import { Card, CardContent, CardHeader, CardTitle } from '../components/ui/card' const PAGE_SIZE = 12 export function NovelsPage() { - const { data, loading, error, fetchMore } = useNovelsQuery({ + const { data, loading, error, fetchMore } = useQuery(NovelsDocument, { variables: { first: PAGE_SIZE, after: null }, notifyOnNetworkStatusChange: true, }) -- 2.49.1 From adc99c700093b8f40bf282fbc591a8822fc6ebf2 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 11:25:52 -0500 Subject: [PATCH 10/22] [FA-11] Updated manual dispatch --- .gitea/workflows/build-subgraphs.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitea/workflows/build-subgraphs.yml b/.gitea/workflows/build-subgraphs.yml index 4c945f6..c7878ff 100644 --- a/.gitea/workflows/build-subgraphs.yml +++ b/.gitea/workflows/build-subgraphs.yml @@ -1,6 +1,7 @@ name: Build Subgraphs on: + workflow_dispatch: push: branches: - master -- 2.49.1 From 6ebfe81ae3f5fff3676e5e853632b24dbbcca9df Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 11:37:05 -0500 Subject: [PATCH 11/22] [FA-11] Test pipelines --- .gitea/workflows/build-gateway.yml | 5 +++++ .gitea/workflows/build-subgraphs.yml | 8 +++++++- .gitea/workflows/release.yml | 3 +++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/.gitea/workflows/build-gateway.yml b/.gitea/workflows/build-gateway.yml index e104d59..4f8d723 100644 --- a/.gitea/workflows/build-gateway.yml +++ b/.gitea/workflows/build-gateway.yml @@ -7,6 +7,11 @@ on: - master paths: - 'FictionArchive.API/**' + pull_request: + branches: + - master + paths: + - 'FictionArchive.API/**' env: REGISTRY: ${{ gitea.server_url }} diff --git a/.gitea/workflows/build-subgraphs.yml b/.gitea/workflows/build-subgraphs.yml index c7878ff..108a8e7 100644 --- a/.gitea/workflows/build-subgraphs.yml +++ b/.gitea/workflows/build-subgraphs.yml @@ -1,7 +1,6 @@ name: Build Subgraphs on: - workflow_dispatch: push: branches: - master @@ -9,6 +8,13 @@ on: - 'FictionArchive.Service.*/**' - 'FictionArchive.Common/**' - 'FictionArchive.Service.Shared/**' + pull_request: + branches: + - master + paths: + - 'FictionArchive.Service.*/**' + - 'FictionArchive.Common/**' + - 'FictionArchive.Service.Shared/**' jobs: build-subgraphs: diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index ca92f7d..6578ff5 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -4,6 +4,9 @@ on: push: tags: - 'v*.*.*' + pull_request: + branches: + - master env: REGISTRY: ${{ gitea.server_url }} -- 2.49.1 From 50263109ab05e743c2ed46353a6e0ad017277f0a Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 11:54:57 -0500 Subject: [PATCH 12/22] [FA-11] More pipeline fixes --- .gitea/workflows/build-gateway.yml | 75 +++++++++++++++-- .gitea/workflows/build-subgraphs.yml | 84 ------------------- .gitea/workflows/release.yml | 16 +++- .../appsettings.json | 2 +- 4 files changed, 80 insertions(+), 97 deletions(-) delete mode 100644 .gitea/workflows/build-subgraphs.yml diff --git a/.gitea/workflows/build-gateway.yml b/.gitea/workflows/build-gateway.yml index 4f8d723..51fdad3 100644 --- a/.gitea/workflows/build-gateway.yml +++ b/.gitea/workflows/build-gateway.yml @@ -6,11 +6,17 @@ on: branches: - master paths: + - 'FictionArchive.Service.*/**' + - 'FictionArchive.Common/**' + - 'FictionArchive.Service.Shared/**' - 'FictionArchive.API/**' pull_request: branches: - master paths: + - 'FictionArchive.Service.*/**' + - 'FictionArchive.Common/**' + - 'FictionArchive.Service.Shared/**' - 'FictionArchive.API/**' env: @@ -18,8 +24,63 @@ env: IMAGE_NAME: ${{ gitea.repository_owner }}/fictionarchive-api jobs: + build-subgraphs: + runs-on: ubuntu-latest + strategy: + matrix: + service: + - name: novel-service + project: FictionArchive.Service.NovelService + subgraph: Novel + - name: translation-service + project: FictionArchive.Service.TranslationService + subgraph: Translation + - name: scheduler-service + project: FictionArchive.Service.SchedulerService + subgraph: Scheduler + - name: user-service + project: FictionArchive.Service.UserService + subgraph: User + - name: file-service + project: FictionArchive.Service.FileService + subgraph: File + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '8.0.x' + + - name: Install Fusion CLI + run: dotnet tool install -g HotChocolate.Fusion.CommandLine + + - name: Restore dependencies + run: dotnet restore ${{ matrix.service.project }}/${{ matrix.service.project }}.csproj + + - name: Build + run: dotnet build ${{ matrix.service.project }}/${{ matrix.service.project }}.csproj -c Release --no-restore + + - name: Export schema + run: | + dotnet run -c Release --no-launch-profile \ + --project ${{ matrix.service.project }}/${{ matrix.service.project }}.csproj \ + -- schema export --output ${{ matrix.service.project }}/schema.graphql + + - name: Pack subgraph + run: fusion subgraph pack -w ${{ matrix.service.project }} + + - name: Upload subgraph package + uses: actions/upload-artifact@v4 + with: + name: ${{ matrix.service.name }}-subgraph + path: ${{ matrix.service.project }}/*.fsp + retention-days: 30 + build-gateway: runs-on: ubuntu-latest + needs: build-subgraphs steps: - name: Checkout uses: actions/checkout@v4 @@ -35,41 +96,35 @@ jobs: - name: Create subgraphs directory run: mkdir -p subgraphs - # Download all subgraph packages from latest successful builds - name: Download Novel Service subgraph uses: actions/download-artifact@v4 with: name: novel-service-subgraph path: subgraphs/novel - continue-on-error: true - name: Download Translation Service subgraph uses: actions/download-artifact@v4 with: name: translation-service-subgraph path: subgraphs/translation - continue-on-error: true - name: Download Scheduler Service subgraph uses: actions/download-artifact@v4 with: name: scheduler-service-subgraph path: subgraphs/scheduler - continue-on-error: true - name: Download User Service subgraph uses: actions/download-artifact@v4 with: name: user-service-subgraph path: subgraphs/user - continue-on-error: true - name: Download File Service subgraph uses: actions/download-artifact@v4 with: name: file-service-subgraph path: subgraphs/file - continue-on-error: true - name: Configure subgraph URLs for Docker run: | @@ -107,6 +162,10 @@ jobs: - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 + - name: Extract registry hostname + id: registry + run: echo "HOST=$(echo '${{ gitea.server_url }}' | sed 's|https\?://||')" >> $GITHUB_OUTPUT + - name: Log in to Gitea Container Registry uses: docker/login-action@v3 with: @@ -121,7 +180,7 @@ jobs: file: FictionArchive.API/Dockerfile push: true tags: | - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest - ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ gitea.sha }} + ${{ steps.registry.outputs.HOST }}/${{ env.IMAGE_NAME }}:latest + ${{ steps.registry.outputs.HOST }}/${{ env.IMAGE_NAME }}:${{ gitea.sha }} cache-from: type=gha cache-to: type=gha,mode=max diff --git a/.gitea/workflows/build-subgraphs.yml b/.gitea/workflows/build-subgraphs.yml deleted file mode 100644 index 108a8e7..0000000 --- a/.gitea/workflows/build-subgraphs.yml +++ /dev/null @@ -1,84 +0,0 @@ -name: Build Subgraphs - -on: - push: - branches: - - master - paths: - - 'FictionArchive.Service.*/**' - - 'FictionArchive.Common/**' - - 'FictionArchive.Service.Shared/**' - pull_request: - branches: - - master - paths: - - 'FictionArchive.Service.*/**' - - 'FictionArchive.Common/**' - - 'FictionArchive.Service.Shared/**' - -jobs: - build-subgraphs: - runs-on: ubuntu-latest - strategy: - matrix: - service: - - name: novel-service - project: FictionArchive.Service.NovelService - subgraph: Novel - - name: translation-service - project: FictionArchive.Service.TranslationService - subgraph: Translation - - name: scheduler-service - project: FictionArchive.Service.SchedulerService - subgraph: Scheduler - - name: user-service - project: FictionArchive.Service.UserService - subgraph: User - - name: file-service - project: FictionArchive.Service.FileService - subgraph: File - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: '8.0.x' - - - name: Install Fusion CLI - run: dotnet tool install -g HotChocolate.Fusion.CommandLine - - - name: Restore dependencies - run: dotnet restore ${{ matrix.service.project }}/${{ matrix.service.project }}.csproj - - - name: Build - run: dotnet build ${{ matrix.service.project }}/${{ matrix.service.project }}.csproj -c Release --no-restore - - - name: Export schema - run: | - dotnet run -c Release --no-launch-profile \ - --project ${{ matrix.service.project }}/${{ matrix.service.project }}.csproj \ - -- schema export --output ${{ matrix.service.project }}/schema.graphql - - - name: Pack subgraph - run: fusion subgraph pack -w ${{ matrix.service.project }} - - - name: Upload subgraph package - uses: actions/upload-artifact@v4 - with: - name: ${{ matrix.service.name }}-subgraph - path: ${{ matrix.service.project }}/*.fsp - retention-days: 30 - - # Trigger gateway build after all subgraphs are built - trigger-gateway: - runs-on: ubuntu-latest - needs: build-subgraphs - steps: - - name: Trigger gateway workflow - run: | - curl -X POST \ - -H "Authorization: token ${{ secrets.GITEA_TOKEN }}" \ - "${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}/actions/workflows/build-gateway.yml/dispatches" \ - -d '{"ref":"master"}' diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 6578ff5..e86f810 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -43,6 +43,10 @@ jobs: id: version run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT + - name: Extract registry hostname + id: registry + run: echo "HOST=$(echo '${{ gitea.server_url }}' | sed 's|https\?://||')" >> $GITHUB_OUTPUT + - name: Log in to Gitea Container Registry uses: docker/login-action@v3 with: @@ -57,8 +61,8 @@ jobs: file: ${{ matrix.service.dockerfile }} push: true tags: | - ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-${{ matrix.service.name }}:${{ steps.version.outputs.VERSION }} - ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-${{ matrix.service.name }}:latest + ${{ steps.registry.outputs.HOST }}/${{ env.IMAGE_PREFIX }}-${{ matrix.service.name }}:${{ steps.version.outputs.VERSION }} + ${{ steps.registry.outputs.HOST }}/${{ env.IMAGE_PREFIX }}-${{ matrix.service.name }}:latest cache-from: type=gha cache-to: type=gha,mode=max @@ -75,6 +79,10 @@ jobs: id: version run: echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_OUTPUT + - name: Extract registry hostname + id: registry + run: echo "HOST=$(echo '${{ gitea.server_url }}' | sed 's|https\?://||')" >> $GITHUB_OUTPUT + - name: Log in to Gitea Container Registry uses: docker/login-action@v3 with: @@ -95,7 +103,7 @@ jobs: VITE_OIDC_REDIRECT_URI=${{ vars.VITE_OIDC_REDIRECT_URI }} VITE_OIDC_POST_LOGOUT_REDIRECT_URI=${{ vars.VITE_OIDC_POST_LOGOUT_REDIRECT_URI }} tags: | - ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-frontend:${{ steps.version.outputs.VERSION }} - ${{ env.REGISTRY }}/${{ env.IMAGE_PREFIX }}-frontend:latest + ${{ steps.registry.outputs.HOST }}/${{ env.IMAGE_PREFIX }}-frontend:${{ steps.version.outputs.VERSION }} + ${{ steps.registry.outputs.HOST }}/${{ env.IMAGE_PREFIX }}-frontend:latest cache-from: type=gha cache-to: type=gha,mode=max diff --git a/FictionArchive.Service.FileService/appsettings.json b/FictionArchive.Service.FileService/appsettings.json index c18d35f..0f6f7a4 100644 --- a/FictionArchive.Service.FileService/appsettings.json +++ b/FictionArchive.Service.FileService/appsettings.json @@ -9,7 +9,7 @@ "BaseUrl": "https://localhost:7247/api" }, "RabbitMQ": { - "ConnectionString": "amqp://localhost", + "ConnectionString": "amqp://localhost2", "ClientIdentifier": "FileService" }, "S3": { -- 2.49.1 From 7e94f06853e9ae8b64ddb82642366d1b9e708336 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 11:57:18 -0500 Subject: [PATCH 13/22] [FA-11] Remove FileService graphQL build --- .gitea/workflows/build-gateway.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.gitea/workflows/build-gateway.yml b/.gitea/workflows/build-gateway.yml index 51fdad3..ec27df6 100644 --- a/.gitea/workflows/build-gateway.yml +++ b/.gitea/workflows/build-gateway.yml @@ -41,9 +41,6 @@ jobs: - name: user-service project: FictionArchive.Service.UserService subgraph: User - - name: file-service - project: FictionArchive.Service.FileService - subgraph: File steps: - name: Checkout uses: actions/checkout@v4 -- 2.49.1 From b9115d78a9955cec0dbf7afd43cb2005774f5146 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 12:40:22 -0500 Subject: [PATCH 14/22] [FA-11] I'm getting sick of fusion but I dont see better alternatives --- .../GraphQL/Mutation.cs | 19 ++---- .../Program.cs | 31 ++++++---- .../Services/NovelUpdateService.cs | 22 +++++++ .../Program.cs | 61 +++++++++++++------ .../Extensions/DatabaseExtensions.cs | 25 ++++++-- .../FictionArchive.Service.Shared.csproj | 1 + .../SchemaExportDetector.cs | 22 +++++++ .../Program.cs | 25 +++++--- FictionArchive.Service.UserService/Program.cs | 25 +++++--- 9 files changed, 163 insertions(+), 68 deletions(-) create mode 100644 FictionArchive.Service.Shared/SchemaExportDetector.cs diff --git a/FictionArchive.Service.NovelService/GraphQL/Mutation.cs b/FictionArchive.Service.NovelService/GraphQL/Mutation.cs index 7b014f5..15f92a7 100644 --- a/FictionArchive.Service.NovelService/GraphQL/Mutation.cs +++ b/FictionArchive.Service.NovelService/GraphQL/Mutation.cs @@ -12,26 +12,15 @@ namespace FictionArchive.Service.NovelService.GraphQL; public class Mutation { - public async Task ImportNovel(string novelUrl, IEventBus eventBus) + public async Task ImportNovel(string novelUrl, NovelUpdateService service) { - var importNovelRequestEvent = new NovelUpdateRequestedEvent() - { - NovelUrl = novelUrl - }; - await eventBus.Publish(importNovelRequestEvent); - return importNovelRequestEvent; + return await service.QueueNovelImport(novelUrl); } public async Task FetchChapterContents(uint novelId, uint chapterNumber, - IEventBus eventBus) + NovelUpdateService service) { - var chapterPullEvent = new ChapterPullRequestedEvent() - { - NovelId = novelId, - ChapterNumber = chapterNumber - }; - await eventBus.Publish(chapterPullEvent); - return chapterPullEvent; + return await service.QueueChapterPull(novelId, chapterNumber); } } \ No newline at end of file diff --git a/FictionArchive.Service.NovelService/Program.cs b/FictionArchive.Service.NovelService/Program.cs index 68fe593..ff9fd26 100644 --- a/FictionArchive.Service.NovelService/Program.cs +++ b/FictionArchive.Service.NovelService/Program.cs @@ -6,6 +6,7 @@ using FictionArchive.Service.NovelService.Services; using FictionArchive.Service.NovelService.Services.EventHandlers; using FictionArchive.Service.NovelService.Services.SourceAdapters; using FictionArchive.Service.NovelService.Services.SourceAdapters.Novelpia; +using FictionArchive.Service.Shared; using FictionArchive.Service.Shared.Extensions; using FictionArchive.Service.Shared.Services.EventBus.Implementations; using FictionArchive.Service.Shared.Services.GraphQL; @@ -17,6 +18,8 @@ public class Program { public static void Main(string[] args) { + var isSchemaExport = SchemaExportDetector.IsSchemaExportMode(args); + var builder = WebApplication.CreateBuilder(args); builder.AddLocalAppsettings(); @@ -24,15 +27,18 @@ public class Program #region Event Bus - builder.Services.AddRabbitMQ(opt => + if (!isSchemaExport) { - builder.Configuration.GetSection("RabbitMQ").Bind(opt); - }) - .Subscribe() - .Subscribe() - .Subscribe() - .Subscribe(); - + builder.Services.AddRabbitMQ(opt => + { + builder.Configuration.GetSection("RabbitMQ").Bind(opt); + }) + .Subscribe() + .Subscribe() + .Subscribe() + .Subscribe(); + } + #endregion #region GraphQL @@ -43,7 +49,9 @@ public class Program #region Database - builder.Services.RegisterDbContext(builder.Configuration.GetConnectionString("DefaultConnection")); + builder.Services.RegisterDbContext( + builder.Configuration.GetConnectionString("DefaultConnection"), + skipInfrastructure: isSchemaExport); #endregion @@ -69,9 +77,10 @@ public class Program var app = builder.Build(); - // Update database - using (var scope = app.Services.CreateScope()) + // Update database (skip in schema export mode) + if (!isSchemaExport) { + using var scope = app.Services.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); dbContext.UpdateDatabase(); } diff --git a/FictionArchive.Service.NovelService/Services/NovelUpdateService.cs b/FictionArchive.Service.NovelService/Services/NovelUpdateService.cs index de3438b..862dcbe 100644 --- a/FictionArchive.Service.NovelService/Services/NovelUpdateService.cs +++ b/FictionArchive.Service.NovelService/Services/NovelUpdateService.cs @@ -2,6 +2,7 @@ using FictionArchive.Service.FileService.IntegrationEvents; using FictionArchive.Service.NovelService.Models.Configuration; using FictionArchive.Service.NovelService.Models.Enums; using FictionArchive.Service.NovelService.Models.Images; +using FictionArchive.Service.NovelService.Models.IntegrationEvents; using FictionArchive.Service.NovelService.Models.Localization; using FictionArchive.Service.NovelService.Models.Novels; using FictionArchive.Service.NovelService.Models.SourceAdapters; @@ -201,4 +202,25 @@ public class NovelUpdateService await _dbContext.SaveChangesAsync(); } + + public async Task QueueNovelImport(string novelUrl) + { + var importNovelRequestEvent = new NovelUpdateRequestedEvent() + { + NovelUrl = novelUrl + }; + await _eventBus.Publish(importNovelRequestEvent); + return importNovelRequestEvent; + } + + public async Task QueueChapterPull(uint novelId, uint chapterNumber) + { + var chapterPullEvent = new ChapterPullRequestedEvent() + { + NovelId = novelId, + ChapterNumber = chapterNumber + }; + await _eventBus.Publish(chapterPullEvent); + return chapterPullEvent; + } } diff --git a/FictionArchive.Service.SchedulerService/Program.cs b/FictionArchive.Service.SchedulerService/Program.cs index 9d40de1..01e47d9 100644 --- a/FictionArchive.Service.SchedulerService/Program.cs +++ b/FictionArchive.Service.SchedulerService/Program.cs @@ -1,5 +1,6 @@ using FictionArchive.Service.SchedulerService.GraphQL; using FictionArchive.Service.SchedulerService.Services; +using FictionArchive.Service.Shared; using FictionArchive.Service.Shared.Extensions; using FictionArchive.Service.Shared.Services.EventBus.Implementations; using Quartz; @@ -11,6 +12,8 @@ public class Program { public static void Main(string[] args) { + var isSchemaExport = SchemaExportDetector.IsSchemaExportMode(args); + var builder = WebApplication.CreateBuilder(args); // Services @@ -20,45 +23,63 @@ public class Program #region Database - builder.Services.RegisterDbContext(builder.Configuration.GetConnectionString("DefaultConnection")); + builder.Services.RegisterDbContext( + builder.Configuration.GetConnectionString("DefaultConnection"), + skipInfrastructure: isSchemaExport); #endregion #region Event Bus - builder.Services.AddRabbitMQ(opt => + if (!isSchemaExport) { - builder.Configuration.GetSection("RabbitMQ").Bind(opt); - }); - + builder.Services.AddRabbitMQ(opt => + { + builder.Configuration.GetSection("RabbitMQ").Bind(opt); + }); + } + #endregion #region Quartz - builder.Services.AddQuartz(opt => + if (isSchemaExport) { - opt.UsePersistentStore(pso => + // Schema export mode: use in-memory store (no DB connection needed) + builder.Services.AddQuartz(opt => { - pso.UsePostgres(pgsql => - { - pgsql.ConnectionString = builder.Configuration.GetConnectionString("DefaultConnection"); - pgsql.UseDriverDelegate(); - pgsql.TablePrefix = "quartz.qrtz_"; // Needed for Postgres due to the differing schema used - }); - pso.UseNewtonsoftJsonSerializer(); + opt.UseInMemoryStore(); }); - }); - builder.Services.AddQuartzHostedService(opt => + } + else { - opt.WaitForJobsToComplete = true; - }); - + builder.Services.AddQuartz(opt => + { + opt.UsePersistentStore(pso => + { + pso.UsePostgres(pgsql => + { + pgsql.ConnectionString = builder.Configuration.GetConnectionString("DefaultConnection"); + pgsql.UseDriverDelegate(); + pgsql.TablePrefix = "quartz.qrtz_"; // Needed for Postgres due to the differing schema used + }); + pso.UseNewtonsoftJsonSerializer(); + }); + }); + builder.Services.AddQuartzHostedService(opt => + { + opt.WaitForJobsToComplete = true; + }); + } + #endregion var app = builder.Build(); - using (var scope = app.Services.CreateScope()) + // Update database (skip in schema export mode) + if (!isSchemaExport) { + using var scope = app.Services.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); dbContext.UpdateDatabase(); } diff --git a/FictionArchive.Service.Shared/Extensions/DatabaseExtensions.cs b/FictionArchive.Service.Shared/Extensions/DatabaseExtensions.cs index c373f51..03b2a01 100644 --- a/FictionArchive.Service.Shared/Extensions/DatabaseExtensions.cs +++ b/FictionArchive.Service.Shared/Extensions/DatabaseExtensions.cs @@ -6,16 +6,29 @@ namespace FictionArchive.Service.Shared.Extensions; public static class DatabaseExtensions { - public static IServiceCollection RegisterDbContext(this IServiceCollection services, - string connectionString) where TContext : FictionArchiveDbContext + public static IServiceCollection RegisterDbContext( + this IServiceCollection services, + string connectionString, + bool skipInfrastructure = false) where TContext : FictionArchiveDbContext { - services.AddDbContext(options => + if (skipInfrastructure) { - options.UseNpgsql(connectionString, o => + // For schema export: use in-memory provider to allow EF Core entity discovery + services.AddDbContext(options => { - o.UseNodaTime(); + options.UseInMemoryDatabase($"SchemaExport_{typeof(TContext).Name}"); }); - }); + } + else + { + services.AddDbContext(options => + { + options.UseNpgsql(connectionString, o => + { + o.UseNodaTime(); + }); + }); + } return services; } } \ No newline at end of file diff --git a/FictionArchive.Service.Shared/FictionArchive.Service.Shared.csproj b/FictionArchive.Service.Shared/FictionArchive.Service.Shared.csproj index da7c156..ee7c426 100644 --- a/FictionArchive.Service.Shared/FictionArchive.Service.Shared.csproj +++ b/FictionArchive.Service.Shared/FictionArchive.Service.Shared.csproj @@ -18,6 +18,7 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + all diff --git a/FictionArchive.Service.Shared/SchemaExportDetector.cs b/FictionArchive.Service.Shared/SchemaExportDetector.cs new file mode 100644 index 0000000..024860d --- /dev/null +++ b/FictionArchive.Service.Shared/SchemaExportDetector.cs @@ -0,0 +1,22 @@ +namespace FictionArchive.Service.Shared; + +/// +/// Detects if the application is running in schema export mode (for HotChocolate CLI commands). +/// In this mode, infrastructure like RabbitMQ and databases should not be initialized. +/// +public static class SchemaExportDetector +{ + /// + /// Checks if the current run is a schema export command. + /// + /// Command line arguments passed to Main() + /// True if running schema export, false otherwise + public static bool IsSchemaExportMode(string[] args) + { + // HotChocolate CLI pattern: "schema export" after "--" delimiter + // Handles: dotnet run -- schema export --output schema.graphql + var normalizedArgs = args.SkipWhile(a => a == "--").ToArray(); + return normalizedArgs.Length > 0 && + normalizedArgs[0].Equals("schema", StringComparison.OrdinalIgnoreCase); + } +} diff --git a/FictionArchive.Service.TranslationService/Program.cs b/FictionArchive.Service.TranslationService/Program.cs index 3fb305c..c110f78 100644 --- a/FictionArchive.Service.TranslationService/Program.cs +++ b/FictionArchive.Service.TranslationService/Program.cs @@ -1,5 +1,6 @@ using DeepL; using FictionArchive.Common.Extensions; +using FictionArchive.Service.Shared; using FictionArchive.Service.Shared.Extensions; using FictionArchive.Service.Shared.Services.EventBus.Implementations; using FictionArchive.Service.Shared.Services.GraphQL; @@ -18,6 +19,8 @@ public class Program { public static void Main(string[] args) { + var isSchemaExport = SchemaExportDetector.IsSchemaExportMode(args); + var builder = WebApplication.CreateBuilder(args); builder.AddLocalAppsettings(); @@ -25,18 +28,23 @@ public class Program #region Event Bus - builder.Services.AddRabbitMQ(opt => + if (!isSchemaExport) { - builder.Configuration.GetSection("RabbitMQ").Bind(opt); - }) - .Subscribe(); - + builder.Services.AddRabbitMQ(opt => + { + builder.Configuration.GetSection("RabbitMQ").Bind(opt); + }) + .Subscribe(); + } + #endregion #region Database - builder.Services.RegisterDbContext(builder.Configuration.GetConnectionString("DefaultConnection")); + builder.Services.RegisterDbContext( + builder.Configuration.GetConnectionString("DefaultConnection"), + skipInfrastructure: isSchemaExport); #endregion @@ -60,9 +68,10 @@ public class Program var app = builder.Build(); - // Update database - using (var scope = app.Services.CreateScope()) + // Update database (skip in schema export mode) + if (!isSchemaExport) { + using var scope = app.Services.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); dbContext.UpdateDatabase(); } diff --git a/FictionArchive.Service.UserService/Program.cs b/FictionArchive.Service.UserService/Program.cs index 13c397d..efc54e9 100644 --- a/FictionArchive.Service.UserService/Program.cs +++ b/FictionArchive.Service.UserService/Program.cs @@ -1,3 +1,4 @@ +using FictionArchive.Service.Shared; using FictionArchive.Service.Shared.Extensions; using FictionArchive.Service.Shared.Services.EventBus.Implementations; using FictionArchive.Service.UserService.GraphQL; @@ -11,16 +12,21 @@ public class Program { public static void Main(string[] args) { + var isSchemaExport = SchemaExportDetector.IsSchemaExportMode(args); + var builder = WebApplication.CreateBuilder(args); #region Event Bus - builder.Services.AddRabbitMQ(opt => + if (!isSchemaExport) { - builder.Configuration.GetSection("RabbitMQ").Bind(opt); - }) - .Subscribe(); - + builder.Services.AddRabbitMQ(opt => + { + builder.Configuration.GetSection("RabbitMQ").Bind(opt); + }) + .Subscribe(); + } + #endregion #region GraphQL @@ -29,16 +35,19 @@ public class Program #endregion - builder.Services.RegisterDbContext(builder.Configuration.GetConnectionString("DefaultConnection")); + builder.Services.RegisterDbContext( + builder.Configuration.GetConnectionString("DefaultConnection"), + skipInfrastructure: isSchemaExport); builder.Services.AddTransient(); builder.Services.AddHealthChecks(); var app = builder.Build(); - // Update database - using (var scope = app.Services.CreateScope()) + // Update database (skip in schema export mode) + if (!isSchemaExport) { + using var scope = app.Services.CreateScope(); var dbContext = scope.ServiceProvider.GetRequiredService(); dbContext.UpdateDatabase(); } -- 2.49.1 From 078eaf5237d48de5fdd3e7ecc8dffaac8de7ba65 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 12:42:35 -0500 Subject: [PATCH 15/22] [FA-11] Dumb --- FictionArchive.Service.FileService/Dockerfile | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/FictionArchive.Service.FileService/Dockerfile b/FictionArchive.Service.FileService/Dockerfile index f972438..fc45bc8 100644 --- a/FictionArchive.Service.FileService/Dockerfile +++ b/FictionArchive.Service.FileService/Dockerfile @@ -7,17 +7,17 @@ EXPOSE 8081 FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build ARG BUILD_CONFIGURATION=Release WORKDIR /src -COPY ["FictionArchive.Service.ImageService/FictionArchive.Service.ImageService.csproj", "FictionArchive.Service.ImageService/"] -RUN dotnet restore "FictionArchive.Service.ImageService/FictionArchive.Service.ImageService.csproj" +COPY ["FictionArchive.Service.FileService/FictionArchive.Service.FileService.csproj", "FictionArchive.Service.FileService/"] +RUN dotnet restore "FictionArchive.Service.FileService/FictionArchive.Service.FileService.csproj" COPY . . -WORKDIR "/src/FictionArchive.Service.ImageService" -RUN dotnet build "./FictionArchive.Service.ImageService.csproj" -c $BUILD_CONFIGURATION -o /app/build +WORKDIR "/src/FictionArchive.Service.FileService" +RUN dotnet build "./FictionArchive.Service.FileService.csproj" -c $BUILD_CONFIGURATION -o /app/build FROM build AS publish ARG BUILD_CONFIGURATION=Release -RUN dotnet publish "./FictionArchive.Service.ImageService.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false +RUN dotnet publish "./FictionArchive.Service.FileService.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false FROM base AS final WORKDIR /app COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "FictionArchive.Service.ImageService.dll"] +ENTRYPOINT ["dotnet", "FictionArchive.Service.FileService.dll"] -- 2.49.1 From f25cbc1a048efd1f8725dbe8df9452190ef580d0 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 12:44:51 -0500 Subject: [PATCH 16/22] [FA-11] Dumb --- .gitea/workflows/build-gateway.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitea/workflows/build-gateway.yml b/.gitea/workflows/build-gateway.yml index ec27df6..1566b88 100644 --- a/.gitea/workflows/build-gateway.yml +++ b/.gitea/workflows/build-gateway.yml @@ -52,6 +52,9 @@ jobs: - name: Install Fusion CLI run: dotnet tool install -g HotChocolate.Fusion.CommandLine + + - name: Add .NET tools to PATH + run: echo "$HOME/.dotnet/tools" >> $GITHUB_PATH - name: Restore dependencies run: dotnet restore ${{ matrix.service.project }}/${{ matrix.service.project }}.csproj -- 2.49.1 From 0938c16a76d23425750663ec1a466716db1fd91f Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 12:49:07 -0500 Subject: [PATCH 17/22] [FA-11] Dumb & cleanup --- .gitea/workflows/build-gateway.yml | 2 +- .gitea/workflows/build.yml | 8 -------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/.gitea/workflows/build-gateway.yml b/.gitea/workflows/build-gateway.yml index 1566b88..8cb0554 100644 --- a/.gitea/workflows/build-gateway.yml +++ b/.gitea/workflows/build-gateway.yml @@ -66,7 +66,7 @@ jobs: run: | dotnet run -c Release --no-launch-profile \ --project ${{ matrix.service.project }}/${{ matrix.service.project }}.csproj \ - -- schema export --output ${{ matrix.service.project }}/schema.graphql + -- schema export --output schema.graphql - name: Pack subgraph run: fusion subgraph pack -w ${{ matrix.service.project }} diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 5aafb71..2413c95 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -20,14 +20,6 @@ jobs: with: dotnet-version: '8.0.x' - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install Fusion CLI - run: dotnet tool install -g HotChocolate.Fusion.CommandLine - - name: Restore dependencies run: dotnet restore FictionArchive.sln -- 2.49.1 From 0d9f788678fd241751a32c8295d7356ca64dd3f3 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 13:03:28 -0500 Subject: [PATCH 18/22] [FA-11] Hopefully last --- .gitea/workflows/build-gateway.yml | 4 +++- .gitea/workflows/release.yml | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitea/workflows/build-gateway.yml b/.gitea/workflows/build-gateway.yml index 8cb0554..6f3a91c 100644 --- a/.gitea/workflows/build-gateway.yml +++ b/.gitea/workflows/build-gateway.yml @@ -5,6 +5,8 @@ on: push: branches: - master + tags: + - 'v*.*.*' paths: - 'FictionArchive.Service.*/**' - 'FictionArchive.Common/**' @@ -72,7 +74,7 @@ jobs: run: fusion subgraph pack -w ${{ matrix.service.project }} - name: Upload subgraph package - uses: actions/upload-artifact@v4 + uses: christopherhx/gitea-upload-artifact@v4 with: name: ${{ matrix.service.name }}-subgraph path: ${{ matrix.service.project }}/*.fsp diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index e86f810..a532da1 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -18,8 +18,6 @@ jobs: strategy: matrix: service: - - name: api - dockerfile: FictionArchive.API/Dockerfile - name: novel-service dockerfile: FictionArchive.Service.NovelService/Dockerfile - name: user-service -- 2.49.1 From 920fd00910db6bc80e0509b543e4b79590307f99 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 13:11:22 -0500 Subject: [PATCH 19/22] [FA-11] Dumb --- .gitea/workflows/build-gateway.yml | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/.gitea/workflows/build-gateway.yml b/.gitea/workflows/build-gateway.yml index 6f3a91c..a335c60 100644 --- a/.gitea/workflows/build-gateway.yml +++ b/.gitea/workflows/build-gateway.yml @@ -95,39 +95,36 @@ jobs: - name: Install Fusion CLI run: dotnet tool install -g HotChocolate.Fusion.CommandLine + - name: Add .NET tools to PATH + run: echo "$HOME/.dotnet/tools" >> $GITHUB_PATH + - name: Create subgraphs directory run: mkdir -p subgraphs - name: Download Novel Service subgraph - uses: actions/download-artifact@v4 + uses: christopherhx/gitea-download-artifact@v4 with: name: novel-service-subgraph path: subgraphs/novel - name: Download Translation Service subgraph - uses: actions/download-artifact@v4 + uses: christopherhx/gitea-download-artifact@v4 with: name: translation-service-subgraph path: subgraphs/translation - name: Download Scheduler Service subgraph - uses: actions/download-artifact@v4 + uses: christopherhx/gitea-download-artifact@v4 with: name: scheduler-service-subgraph path: subgraphs/scheduler - name: Download User Service subgraph - uses: actions/download-artifact@v4 + uses: christopherhx/gitea-download-artifact@v4 with: name: user-service-subgraph path: subgraphs/user - - name: Download File Service subgraph - uses: actions/download-artifact@v4 - with: - name: file-service-subgraph - path: subgraphs/file - - name: Configure subgraph URLs for Docker run: | for fsp in subgraphs/*/*.fsp; do @@ -157,10 +154,6 @@ jobs: - name: Build gateway run: dotnet build FictionArchive.API/FictionArchive.API.csproj -c Release --no-restore -p:SkipFusionBuild=true - - name: Run tests - run: dotnet test FictionArchive.sln -c Release --no-build --verbosity normal - continue-on-error: true - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 -- 2.49.1 From 4635ed1b4e3d92e57db770f6c86013f4b0492e95 Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 13:36:22 -0500 Subject: [PATCH 20/22] [FA-11] Finalized --- .gitea/workflows/build-gateway.yml | 8 -------- .gitea/workflows/release.yml | 3 --- 2 files changed, 11 deletions(-) diff --git a/.gitea/workflows/build-gateway.yml b/.gitea/workflows/build-gateway.yml index a335c60..0964c41 100644 --- a/.gitea/workflows/build-gateway.yml +++ b/.gitea/workflows/build-gateway.yml @@ -12,14 +12,6 @@ on: - 'FictionArchive.Common/**' - 'FictionArchive.Service.Shared/**' - 'FictionArchive.API/**' - pull_request: - branches: - - master - paths: - - 'FictionArchive.Service.*/**' - - 'FictionArchive.Common/**' - - 'FictionArchive.Service.Shared/**' - - 'FictionArchive.API/**' env: REGISTRY: ${{ gitea.server_url }} diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index a532da1..b71bd27 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -4,9 +4,6 @@ on: push: tags: - 'v*.*.*' - pull_request: - branches: - - master env: REGISTRY: ${{ gitea.server_url }} -- 2.49.1 From 09ebdb1b2a60c1dc6399f2febe28fb9a1f25c17a Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 16:08:40 -0500 Subject: [PATCH 21/22] [FA-11] Cleanup --- .gitea/workflows/build-gateway.yml | 7 - Documentation/CICD.md | 201 ++++++++++++++++++++--------- docker-compose.yml | 38 ++---- 3 files changed, 147 insertions(+), 99 deletions(-) diff --git a/.gitea/workflows/build-gateway.yml b/.gitea/workflows/build-gateway.yml index 0964c41..868f992 100644 --- a/.gitea/workflows/build-gateway.yml +++ b/.gitea/workflows/build-gateway.yml @@ -3,15 +3,8 @@ name: Build Gateway on: workflow_dispatch: push: - branches: - - master tags: - 'v*.*.*' - paths: - - 'FictionArchive.Service.*/**' - - 'FictionArchive.Common/**' - - 'FictionArchive.Service.Shared/**' - - 'FictionArchive.API/**' env: REGISTRY: ${{ gitea.server_url }} diff --git a/Documentation/CICD.md b/Documentation/CICD.md index a2f9c6e..d42b958 100644 --- a/Documentation/CICD.md +++ b/Documentation/CICD.md @@ -7,9 +7,9 @@ This document describes the CI/CD pipeline configuration for FictionArchive usin | Workflow | File | Trigger | Purpose | |----------|------|---------|---------| | CI | `build.yml` | Push/PR to master | Build and test all projects | -| Build Subgraphs | `build-subgraphs.yml` | Push to master (service changes) | Build GraphQL subgraph packages | -| Build Gateway | `build-gateway.yml` | Manual or triggered by subgraphs | Compose gateway and build Docker image | +| Build Gateway | `build-gateway.yml` | Tag `v*.*.*` or manual | Build subgraphs, compose gateway, push API image | | Release | `release.yml` | Tag `v*.*.*` | Build and push all Docker images | +| Claude PR Assistant | `claude_assistant.yml` | Issue/PR comments with @claude | AI-assisted code review and issue handling | ## Pipeline Architecture @@ -18,27 +18,32 @@ This document describes the CI/CD pipeline configuration for FictionArchive usin │ Push to master │ └─────────────────────────────┬───────────────────────────────────────┘ │ - ┌───────────────┴───────────────┐ - ▼ ▼ -┌─────────────────────────┐ ┌─────────────────────────┐ -│ build.yml │ │ build-subgraphs.yml │ -│ (CI checks - always) │ │ (if service changes) │ -└─────────────────────────┘ └────────────┬────────────┘ - │ - ▼ - ┌─────────────────────────┐ - │ build-gateway.yml │ - │ (compose & push API) │ - └─────────────────────────┘ + ▼ + ┌─────────────────────────┐ + │ build.yml │ + │ (CI checks) │ + └─────────────────────────┘ ┌─────────────────────────────────────────────────────────────────────┐ │ Push tag v*.*.* │ +└─────────────────────────────┬───────────────────────────────────────┘ + │ + ┌───────────────┴───────────────┐ + ▼ ▼ +┌─────────────────────────┐ ┌─────────────────────────┐ +│ release.yml │ │ build-gateway.yml │ +│ (build & push all │ │ (build subgraphs & │ +│ backend + frontend) │ │ push API gateway) │ +└─────────────────────────┘ └─────────────────────────┘ + +┌─────────────────────────────────────────────────────────────────────┐ +│ Issue/PR comment containing @claude │ └─────────────────────────────┬───────────────────────────────────────┘ │ ▼ ┌─────────────────────────┐ - │ release.yml │ - │ (build & push all) │ + │ claude_assistant.yml │ + │ (AI code assistance) │ └─────────────────────────┘ ``` @@ -51,14 +56,15 @@ Configure these in **Settings → Actions → Secrets**: | Secret | Description | Required By | |--------|-------------|-------------| | `REGISTRY_TOKEN` | Gitea access token with `write:package` scope | `release.yml`, `build-gateway.yml` | -| `GITEA_TOKEN` | Gitea access token for API calls | `build-subgraphs.yml` | +| `CLAUDE_CODE_OAUTH_TOKEN` | Claude Code OAuth token | `claude_assistant.yml` | +| `CLAUDE_GITEA_TOKEN` | Gitea token for Claude assistant | `claude_assistant.yml` | #### Creating Access Tokens 1. Go to **Settings → Applications → Access Tokens** 2. Create a new token with the following scopes: - `write:package` - Push container images - - `write:repository` - Trigger workflows via API + - `write:repository` - For Claude assistant to push commits 3. Copy the token and add it as a repository secret ### Repository Variables @@ -85,42 +91,62 @@ Configure these in **Settings → Actions → Variables**: **Requirements:** - .NET 8.0 SDK -- Python 3.12 - Node.js 20 -- HotChocolate Fusion CLI -### Build Subgraphs (`build-subgraphs.yml`) +**Steps (Backend):** +1. Checkout repository +2. Setup .NET 8.0 +3. Restore dependencies +4. Build solution (Release, with `SkipFusionBuild=true`) +5. Run tests -**Trigger:** Push to `master` with changes in: -- `FictionArchive.Service.*/**` -- `FictionArchive.Common/**` -- `FictionArchive.Service.Shared/**` - -**Jobs:** -1. `build-subgraphs` - Matrix job building each service's `.fsp` package -2. `trigger-gateway` - Triggers gateway rebuild via API - -**Subgraphs Built:** -- Novel Service -- Translation Service -- Scheduler Service -- User Service -- File Service - -**Artifacts:** Each subgraph produces a `.fsp` file retained for 30 days. +**Steps (Frontend):** +1. Checkout repository +2. Setup Node.js 20 +3. Install dependencies (`npm ci`) +4. Run linter (`npm run lint`) +5. Build application (`npm run build`) ### Build Gateway (`build-gateway.yml`) **Trigger:** - Manual dispatch (`workflow_dispatch`) -- Push to `master` with changes in `FictionArchive.API/**` -- Triggered by `build-subgraphs.yml` completion +- Push tag matching `v*.*.*` -**Process:** -1. Downloads all subgraph `.fsp` artifacts -2. Configures Docker-internal URLs for each subgraph -3. Composes gateway schema using Fusion CLI -4. Builds and pushes API Docker image +**Jobs:** + +#### 1. `build-subgraphs` (Matrix Job) +Builds GraphQL subgraph packages for each service: + +| Service | Project | Subgraph Name | +|---------|---------|---------------| +| novel-service | FictionArchive.Service.NovelService | Novel | +| translation-service | FictionArchive.Service.TranslationService | Translation | +| scheduler-service | FictionArchive.Service.SchedulerService | Scheduler | +| user-service | FictionArchive.Service.UserService | User | + +**Note:** File Service and Authentication Service are not subgraphs (no GraphQL schema). + +**Steps:** +1. Checkout repository +2. Setup .NET 8.0 +3. Install HotChocolate Fusion CLI +4. Restore and build service project +5. Export GraphQL schema (`schema export`) +6. Pack subgraph into `.fsp` file +7. Upload artifact (retained 30 days) + +#### 2. `build-gateway` (Depends on `build-subgraphs`) +Composes the API gateway from subgraph packages. + +**Steps:** +1. Checkout repository +2. Setup .NET 8.0 and Fusion CLI +3. Download all subgraph artifacts +4. Configure Docker-internal URLs (`http://{service}-service:8080/graphql`) +5. Compose gateway schema using Fusion CLI +6. Build gateway project +7. Build and push Docker image **Image Tags:** - `//fictionarchive-api:latest` @@ -131,23 +157,54 @@ Configure these in **Settings → Actions → Variables**: **Trigger:** Push tag matching `v*.*.*` (e.g., `v1.0.0`) **Jobs:** -1. `build-and-push` - Matrix job building all backend service images -2. `build-frontend` - Builds and pushes frontend image -**Services Built:** -- `fictionarchive-api` -- `fictionarchive-novel-service` -- `fictionarchive-user-service` -- `fictionarchive-translation-service` -- `fictionarchive-file-service` -- `fictionarchive-scheduler-service` -- `fictionarchive-authentication-service` -- `fictionarchive-frontend` +#### 1. `build-and-push` (Matrix Job) +Builds and pushes all backend service images: + +| Service | Dockerfile | +|---------|------------| +| novel-service | FictionArchive.Service.NovelService/Dockerfile | +| user-service | FictionArchive.Service.UserService/Dockerfile | +| translation-service | FictionArchive.Service.TranslationService/Dockerfile | +| file-service | FictionArchive.Service.FileService/Dockerfile | +| scheduler-service | FictionArchive.Service.SchedulerService/Dockerfile | +| authentication-service | FictionArchive.Service.AuthenticationService/Dockerfile | + +#### 2. `build-frontend` +Builds and pushes the frontend image with environment-specific build arguments. + +**Build Args:** +- `VITE_GRAPHQL_URI` +- `VITE_OIDC_AUTHORITY` +- `VITE_OIDC_CLIENT_ID` +- `VITE_OIDC_REDIRECT_URI` +- `VITE_OIDC_POST_LOGOUT_REDIRECT_URI` **Image Tags:** - `//fictionarchive-:` - `//fictionarchive-:latest` +### Claude PR Assistant (`claude_assistant.yml`) + +**Trigger:** Comments or issues containing `@claude`: +- Issue comments +- Pull request review comments +- Pull request reviews +- New issues (opened or assigned) + +**Permissions Required:** +- `contents: write` +- `pull-requests: write` +- `issues: write` +- `id-token: write` + +**Usage:** +Mention `@claude` in any issue or PR comment to invoke the AI assistant for: +- Code review assistance +- Bug analysis +- Implementation suggestions +- Documentation help + ## Container Registry Images are pushed to the Gitea Container Registry at: @@ -155,6 +212,19 @@ Images are pushed to the Gitea Container Registry at: //fictionarchive-: ``` +### Image Naming Convention + +| Image | Description | +|-------|-------------| +| `fictionarchive-api` | API Gateway (GraphQL Federation) | +| `fictionarchive-novel-service` | Novel Service | +| `fictionarchive-user-service` | User Service | +| `fictionarchive-translation-service` | Translation Service | +| `fictionarchive-file-service` | File Service | +| `fictionarchive-scheduler-service` | Scheduler Service | +| `fictionarchive-authentication-service` | Authentication Service | +| `fictionarchive-frontend` | Web Frontend | + ### Pulling Images ```bash @@ -184,13 +254,13 @@ docker pull //fictionarchive-api:latest - Ensure the `REGISTRY_TOKEN` secret is configured in repository settings - Verify the token has `write:package` scope -**"Failed to trigger gateway workflow"** -- Ensure `GITEA_TOKEN` secret is configured -- Verify the token has `write:repository` scope - **"No subgraph artifacts found"** -- The gateway build requires subgraph artifacts from a previous `build-subgraphs` run -- Trigger `build-subgraphs.yml` manually or push a change to a service +- The gateway build requires subgraph artifacts from the `build-subgraphs` job +- If subgraph builds failed, check the matrix job logs for errors + +**"Schema export failed"** +- Ensure the service project has a valid `subgraph-config.json` +- Check that the service starts correctly for schema export ### Frontend Build Failures @@ -204,6 +274,13 @@ docker pull //fictionarchive-api:latest - Verify `REGISTRY_TOKEN` has correct permissions - Check that the token hasn't expired +### Claude Assistant Failures + +**"Claude assistant not responding"** +- Verify `CLAUDE_CODE_OAUTH_TOKEN` is configured +- Verify `CLAUDE_GITEA_TOKEN` is configured and has write permissions +- Check that the comment contains `@claude` mention + ## Local Testing To test workflows locally before pushing: diff --git a/docker-compose.yml b/docker-compose.yml index 91f0e5c..c3c26d9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,9 +34,7 @@ services: # Backend Services # =========================================== novel-service: - build: - context: . - dockerfile: FictionArchive.Service.NovelService/Dockerfile + image: git.orfl.xyz/conco/fictionarchive-novel-service:latest environment: ConnectionStrings__DefaultConnection: Host=postgres;Database=FictionArchive_NovelService;Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-postgres} ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq @@ -51,9 +49,7 @@ services: restart: unless-stopped translation-service: - build: - context: . - dockerfile: FictionArchive.Service.TranslationService/Dockerfile + image: git.orfl.xyz/conco/fictionarchive-translation-service:latest environment: ConnectionStrings__DefaultConnection: Host=postgres;Database=FictionArchive_TranslationService;Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-postgres} ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq @@ -66,9 +62,7 @@ services: restart: unless-stopped scheduler-service: - build: - context: . - dockerfile: FictionArchive.Service.SchedulerService/Dockerfile + image: git.orfl.xyz/conco/fictionarchive-scheduler-service:latest environment: ConnectionStrings__DefaultConnection: Host=postgres;Database=FictionArchive_SchedulerService;Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-postgres} ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq @@ -80,9 +74,7 @@ services: restart: unless-stopped user-service: - build: - context: . - dockerfile: FictionArchive.Service.UserService/Dockerfile + image: git.orfl.xyz/conco/fictionarchive-user-service:latest environment: ConnectionStrings__DefaultConnection: Host=postgres;Database=FictionArchive_UserService;Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-postgres} ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq @@ -94,9 +86,7 @@ services: restart: unless-stopped authentication-service: - build: - context: . - dockerfile: FictionArchive.Service.AuthenticationService/Dockerfile + image: git.orfl.xyz/conco/fictionarchive-authentication-service:latest environment: ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq depends_on: @@ -105,9 +95,7 @@ services: restart: unless-stopped file-service: - build: - context: . - dockerfile: FictionArchive.Service.FileService/Dockerfile + image: git.orfl.xyz/conco/fictionarchive-file-service:latest environment: ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq S3__Endpoint: ${S3_ENDPOINT:-https://s3.orfl.xyz} @@ -130,9 +118,7 @@ services: # API Gateway # =========================================== api-gateway: - build: - context: . - dockerfile: FictionArchive.API/Dockerfile + image: git.orfl.xyz/conco/fictionarchive-api:latest environment: ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq labels: @@ -154,15 +140,7 @@ services: # Frontend # =========================================== frontend: - build: - context: ./fictionarchive-web - dockerfile: Dockerfile - args: - VITE_GRAPHQL_URI: https://api.fictionarchive.orfl.xyz/graphql/ - VITE_OIDC_AUTHORITY: ${OIDC_AUTHORITY:-https://auth.orfl.xyz/application/o/fiction-archive/} - VITE_OIDC_CLIENT_ID: ${OIDC_CLIENT_ID} - VITE_OIDC_REDIRECT_URI: https://fictionarchive.orfl.xyz/ - VITE_OIDC_POST_LOGOUT_REDIRECT_URI: https://fictionarchive.orfl.xyz/ + image: git.orfl.xyz/conco/fictionarchive-frontend:latest labels: - "traefik.enable=true" - "traefik.http.routers.frontend.rule=Host(`fictionarchive.orfl.xyz`)" -- 2.49.1 From b71d9031e156c2cf3bda33158ec8adceb3d0420c Mon Sep 17 00:00:00 2001 From: gamer147 Date: Wed, 26 Nov 2025 18:26:30 -0500 Subject: [PATCH 22/22] [FA-11] Finished for real --- .gitea/workflows/build.yml | 22 +++++++++++++++++- docker-compose.yml | 40 ++++++++++++++++++++++++++++++++ fictionarchive-web/.dockerignore | 40 ++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 fictionarchive-web/.dockerignore diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 2413c95..ed698a9 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -27,7 +27,27 @@ jobs: run: dotnet build FictionArchive.sln --configuration Release --no-restore /p:SkipFusionBuild=true - name: Run tests - run: dotnet test FictionArchive.sln --configuration Release --no-build --verbosity normal + run: | + dotnet test FictionArchive.sln --configuration Release --no-build --verbosity normal \ + --logger "trx;LogFileName=test-results.trx" \ + --collect:"XPlat Code Coverage" \ + --results-directory ./TestResults + + - name: Upload test results + uses: christopherhx/gitea-upload-artifact@v4 + if: always() + with: + name: test-results + path: ./TestResults/**/*.trx + retention-days: 30 + + - name: Upload coverage results + uses: christopherhx/gitea-upload-artifact@v4 + if: always() + with: + name: coverage-results + path: ./TestResults/**/coverage.cobertura.xml + retention-days: 30 build-frontend: runs-on: ubuntu-latest diff --git a/docker-compose.yml b/docker-compose.yml index c3c26d9..2978d78 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -41,6 +41,11 @@ services: Novelpia__Username: ${NOVELPIA_USERNAME} Novelpia__Password: ${NOVELPIA_PASSWORD} NovelUpdateService__PendingImageUrl: https://files.fictionarchive.orfl.xyz/api/pendingupload.png + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/healthz"] + interval: 30s + timeout: 10s + retries: 3 depends_on: postgres: condition: service_healthy @@ -54,6 +59,11 @@ services: ConnectionStrings__DefaultConnection: Host=postgres;Database=FictionArchive_TranslationService;Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-postgres} ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq DeepL__ApiKey: ${DEEPL_API_KEY} + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/healthz"] + interval: 30s + timeout: 10s + retries: 3 depends_on: postgres: condition: service_healthy @@ -66,6 +76,11 @@ services: environment: ConnectionStrings__DefaultConnection: Host=postgres;Database=FictionArchive_SchedulerService;Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-postgres} ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/healthz"] + interval: 30s + timeout: 10s + retries: 3 depends_on: postgres: condition: service_healthy @@ -78,6 +93,11 @@ services: environment: ConnectionStrings__DefaultConnection: Host=postgres;Database=FictionArchive_UserService;Username=${POSTGRES_USER:-postgres};Password=${POSTGRES_PASSWORD:-postgres} ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/healthz"] + interval: 30s + timeout: 10s + retries: 3 depends_on: postgres: condition: service_healthy @@ -89,6 +109,11 @@ services: image: git.orfl.xyz/conco/fictionarchive-authentication-service:latest environment: ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/healthz"] + interval: 30s + timeout: 10s + retries: 3 depends_on: rabbitmq: condition: service_healthy @@ -103,6 +128,11 @@ services: S3__AccessKey: ${S3_ACCESS_KEY} S3__SecretKey: ${S3_SECRET_KEY} Proxy__BaseUrl: https://files.orfl.xyz/api + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/healthz"] + interval: 30s + timeout: 10s + retries: 3 labels: - "traefik.enable=true" - "traefik.http.routers.file-service.rule=Host(`files.orfl.xyz`)" @@ -121,6 +151,11 @@ services: image: git.orfl.xyz/conco/fictionarchive-api:latest environment: ConnectionStrings__RabbitMQ: amqp://${RABBITMQ_USER:-guest}:${RABBITMQ_PASSWORD:-guest}@rabbitmq + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/healthz"] + interval: 30s + timeout: 10s + retries: 3 labels: - "traefik.enable=true" - "traefik.http.routers.api-gateway.rule=Host(`api.fictionarchive.orfl.xyz`)" @@ -141,6 +176,11 @@ services: # =========================================== frontend: image: git.orfl.xyz/conco/fictionarchive-frontend:latest + healthcheck: + test: ["CMD", "wget", "--no-verbose", "--tries=1", "--spider", "http://localhost/"] + interval: 30s + timeout: 10s + retries: 3 labels: - "traefik.enable=true" - "traefik.http.routers.frontend.rule=Host(`fictionarchive.orfl.xyz`)" diff --git a/fictionarchive-web/.dockerignore b/fictionarchive-web/.dockerignore new file mode 100644 index 0000000..8d3531b --- /dev/null +++ b/fictionarchive-web/.dockerignore @@ -0,0 +1,40 @@ +# Dependencies +node_modules + +# Build output +dist + +# Environment files +.env +.env.local +.env.*.local + +# IDE and editor +.vscode +.idea +*.swp +*.swo + +# Git +.git +.gitignore + +# Logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Test coverage +coverage + +# Docker +Dockerfile +.dockerignore +docker-compose* + +# Documentation +README.md +*.md + +# TypeScript build info +*.tsbuildinfo -- 2.49.1