From 9d841a2757d78acc76c614ebfe91b8ceb4ddbcf6 Mon Sep 17 00:00:00 2001 From: Jeik Date: Thu, 20 Aug 2026 10:22:46 +0800 Subject: [PATCH 1/4] feat(audit): capture complete client HTTP request headers, metadata and fix audit detail inspection --- backend/internal/application/gateway/image.go | 20 +- .../internal/application/gateway/service.go | 10 + .../application/gateway/service_test.go | 8 +- backend/internal/application/gateway/video.go | 1 + backend/internal/application/gateway/voice.go | 25 +- backend/internal/domain/audit/audit.go | 4 + .../relational/audit_repository.go | 13 +- .../relational/audit_repository_test.go | 62 + .../infra/persistence/relational/mapping.go | 6 +- .../infra/persistence/relational/models.go | 4 + .../internal/transport/http/audit/handler.go | 12 +- .../transport/http/inference/handler.go | 29 +- .../http/inference/openai_audio_handler.go | 13 +- .../transport/http/inference/voice_handler.go | 14 +- frontend/package-lock.json | 5131 +++++++++++++++++ .../audits/request-audit-detail-dialog.tsx | 631 +- .../src/features/audits/request-audits-api.ts | 9 +- .../features/audits/request-audits-page.tsx | 12 +- frontend/src/shared/i18n/index.ts | 52 +- frontend/vite.config.ts | 1 + start.bat | 48 + stop.bat | 22 + 22 files changed, 6014 insertions(+), 113 deletions(-) create mode 100644 frontend/package-lock.json create mode 100644 start.bat create mode 100644 stop.bat diff --git a/backend/internal/application/gateway/image.go b/backend/internal/application/gateway/image.go index 4988bec26..45d00e987 100644 --- a/backend/internal/application/gateway/image.go +++ b/backend/internal/application/gateway/image.go @@ -31,6 +31,10 @@ type ImageGenerationInput struct { ResponseFormat string Streaming bool PartialImages int + RequestBody string + Method string + Path string + Headers map[string][]string } // ImageEditInput 表示图片编辑用例已经完成协议校验后的输入。 @@ -48,6 +52,10 @@ type ImageEditInput struct { ResponseFormat string Streaming bool PartialImages int + RequestBody string + Method string + Path string + Headers map[string][]string } type imageProviderSupport func(accountdomain.Provider) bool @@ -69,7 +77,7 @@ func (s *Service) GenerateImage(ctx context.Context, input ImageGenerationInput) Size: input.Size, AspectRatio: input.AspectRatio, Resolution: input.Resolution, Quality: input.Quality, ResponseFormat: input.ResponseFormat, Streaming: input.Streaming, PartialImages: input.PartialImages, }) - }, input.Streaming, input.Resolution, input.Quality, input.Count, 0) + }, input.Streaming, input.Resolution, input.Quality, input.Count, 0, input.RequestBody, input.Method, input.Path, input.Headers) } // EditImage 选择支持图片编辑的路由和账号,并返回可统一审计的上游响应。 @@ -88,7 +96,7 @@ func (s *Service) EditImage(ctx context.Context, input ImageEditInput) (*Result, Resolution: input.Resolution, Quality: input.Quality, ResponseFormat: input.ResponseFormat, Streaming: input.Streaming, PartialImages: input.PartialImages, }) - }, input.Streaming, input.Resolution, input.Quality, input.Count, len(input.ImageURLs)) + }, input.Streaming, input.Resolution, input.Quality, input.Count, len(input.ImageURLs), input.RequestBody, input.Method, input.Path, input.Headers) } func (s *Service) executeImage( @@ -105,6 +113,10 @@ func (s *Service) executeImage( quality string, requestedCount int, inputImageCount int, + requestBody string, + method string, + path string, + headers map[string][]string, ) (*Result, error) { ctx, egressTrace := infraegress.WithTrace(ctx) startedAt := time.Now() @@ -130,6 +142,10 @@ func (s *Service) executeImage( ClientIP: requestmeta.ClientIP(ctx), ModelRouteID: route.ID, ModelPublicID: externalModel, ModelUpstreamModel: modeldomain.DisplayUpstreamModel(route.Provider, route.UpstreamModel), Provider: string(route.Provider), Operation: operation, UsageSource: audit.UsageSourceNone, Streaming: streaming, + RequestMethod: method, + RequestPath: path, + RequestHeaders: headers, + RequestBody: requestBody, } if operation == audit.OperationImageEdit { auditBase.MediaInputImages = int64(max(0, inputImageCount)) diff --git a/backend/internal/application/gateway/service.go b/backend/internal/application/gateway/service.go index b4b0136f3..1652cf74a 100644 --- a/backend/internal/application/gateway/service.go +++ b/backend/internal/application/gateway/service.go @@ -108,6 +108,12 @@ type Input struct { // GrokTurnIndex forwards only the turn supplied by a real Grok Shell client; the server never infers or increments it. GrokTurnIndex string Operation audit.Operation + // Method is the client HTTP request method. + Method string + // Path is the client HTTP request path. + Path string + // Headers is the client HTTP request headers. + Headers map[string][]string // auditOperation may classify a normal protocol request differently for // operator visibility without changing routing or Provider semantics. auditOperation audit.Operation @@ -943,6 +949,10 @@ func (s *Service) createResponseAt(ctx context.Context, input Input, path string ModelRouteID: route.ID, ModelPublicID: publicModel, ModelUpstreamModel: modeldomain.DisplayUpstreamModel(route.Provider, route.UpstreamModel), Provider: string(route.Provider), Operation: auditOperation, UsageSource: audit.UsageSourceNone, Streaming: input.Streaming, MediaInputImages: mediaSummary.InputImages, + RequestMethod: input.Method, + RequestPath: input.Path, + RequestHeaders: input.Headers, + RequestBody: string(input.Body), } if errors.Is(routeErr, clientkeyapp.ErrModelNotAllowed) { record := auditBase diff --git a/backend/internal/application/gateway/service_test.go b/backend/internal/application/gateway/service_test.go index 0f35a8335..b85954de2 100644 --- a/backend/internal/application/gateway/service_test.go +++ b/backend/internal/application/gateway/service_test.go @@ -1225,7 +1225,7 @@ func TestUnpricedVoiceRemainsAvailableToFiniteClientKey(t *testing.T) { accountService := accountapp.NewService(accountRepo, auditRepo, memory.NewDeviceSessionStore(), sticky, registry, testCipher(t), nil) service := NewService(modelRepo, auditRepo, accountService, clientkeyapp.NewService(nil, nil, nil, 60, 4, nil), registry, selector, nil, 1) executed := false - result, err := service.executeVoice(ctx, "req-voice-billing", clientkey.Key{ID: 1, BillingLimitUSDTicks: 1}, voiceModel, audit.OperationTTS, modeldomain.CapabilityTTS, true, audit.PricingResult{}, func(account.Provider) bool { + result, err := service.executeVoice(ctx, "req-voice-billing", clientkey.Key{ID: 1, BillingLimitUSDTicks: 1}, voiceModel, audit.OperationTTS, modeldomain.CapabilityTTS, true, audit.PricingResult{}, "", "", "", nil, func(account.Provider) bool { return true }, func(context.Context, account.Provider, account.Credential, string) (voiceExecutionResult, error) { executed = true @@ -1296,7 +1296,7 @@ func TestVoicePricingSettlesTTSAndRESTSTTUsage(t *testing.T) { if !ok { t.Fatal("TTS pricing unavailable") } - ttsResult, err := service.executeVoice(ctx, "req-priced-tts", limitedKey, ttsModel, audit.OperationTTS, modeldomain.CapabilityTTS, true, ttsPricing, func(account.Provider) bool { + ttsResult, err := service.executeVoice(ctx, "req-priced-tts", limitedKey, ttsModel, audit.OperationTTS, modeldomain.CapabilityTTS, true, ttsPricing, "", "", "", nil, func(account.Provider) bool { return true }, func(context.Context, account.Provider, account.Credential, string) (voiceExecutionResult, error) { return voiceExecutionResult{response: jsonVoiceResponse(http.StatusOK, map[string]any{"ok": true}), pricing: ttsPricing}, nil @@ -1319,7 +1319,7 @@ func TestVoicePricingSettlesTTSAndRESTSTTUsage(t *testing.T) { if !ok { t.Fatal("STT pricing unavailable") } - sttResult, err := service.executeVoice(ctx, "req-priced-stt", limitedKey, sttModel, audit.OperationSTT, modeldomain.CapabilitySTT, true, audit.PricingResult{}, func(account.Provider) bool { + sttResult, err := service.executeVoice(ctx, "req-priced-stt", limitedKey, sttModel, audit.OperationSTT, modeldomain.CapabilitySTT, true, audit.PricingResult{}, "", "", "", nil, func(account.Provider) bool { return true }, func(context.Context, account.Provider, account.Credential, string) (voiceExecutionResult, error) { return voiceExecutionResult{response: jsonVoiceResponse(http.StatusOK, map[string]any{"text": "hello"}), pricing: sttPricing}, nil @@ -1360,7 +1360,7 @@ func TestVoicePricingSettlesTTSAndRESTSTTUsage(t *testing.T) { t.Fatal(err) } executed := false - _, err = service.executeVoice(ctx, "req-capped-tts", cappedKey, ttsModel, audit.OperationTTS, modeldomain.CapabilityTTS, true, ttsPricing, func(account.Provider) bool { + _, err = service.executeVoice(ctx, "req-capped-tts", cappedKey, ttsModel, audit.OperationTTS, modeldomain.CapabilityTTS, true, ttsPricing, "", "", "", nil, func(account.Provider) bool { return true }, func(context.Context, account.Provider, account.Credential, string) (voiceExecutionResult, error) { executed = true diff --git a/backend/internal/application/gateway/video.go b/backend/internal/application/gateway/video.go index 9791c7995..63e1cf90e 100644 --- a/backend/internal/application/gateway/video.go +++ b/backend/internal/application/gateway/video.go @@ -993,6 +993,7 @@ func (s *Service) recordVideoAudit(ctx context.Context, job media.Job, durationM AccountID: accountID, AccountName: job.AccountName, StatusCode: statusCode, ErrorCode: job.ErrorCode, EgressNodeID: job.EgressNodeID, EgressNodeName: job.EgressNodeName, EgressScope: job.EgressScope, EgressMode: audit.EgressMode(job.EgressMode), MediaInputImages: int64(job.InputImageCount), + RequestBody: job.InputJSON, DurationMS: durationMS, AttemptCount: len(attempts), Attempts: append([]audit.Attempt(nil), attempts...), CreatedAt: createdAt, } if job.Status == media.StatusCompleted && job.Seconds > 0 { diff --git a/backend/internal/application/gateway/voice.go b/backend/internal/application/gateway/voice.go index 2cde532da..bc5f29273 100644 --- a/backend/internal/application/gateway/voice.go +++ b/backend/internal/application/gateway/voice.go @@ -35,6 +35,10 @@ type TTSInput struct { OptimizeStreamingLatency int TextNormalization bool WithTimestamps bool + RequestBody string + Method string + Path string + Headers map[string][]string } type STTInput struct { @@ -58,6 +62,10 @@ type STTInput struct { // ResponseFormat is empty for the native Console-compatible response, or an // OpenAI-compatible format normalized by the HTTP transport. ResponseFormat string + RequestBody string + Method string + Path string + Headers map[string][]string } type VoiceListInput struct { @@ -82,7 +90,7 @@ type voiceExecutionResult struct { func (s *Service) SynthesizeSpeech(ctx context.Context, input TTSInput) (*Result, error) { reservation, _ := audit.EstimateOfficialTTSCost(input.Text) - return s.executeVoice(ctx, input.RequestID, input.ClientKey, input.PublicModel, audit.OperationTTS, modeldomain.CapabilityTTS, true, reservation, func(providerValue accountdomain.Provider) bool { + return s.executeVoice(ctx, input.RequestID, input.ClientKey, input.PublicModel, audit.OperationTTS, modeldomain.CapabilityTTS, true, reservation, input.RequestBody, input.Method, input.Path, input.Headers, func(providerValue accountdomain.Provider) bool { _, ok := s.providers.TTS(providerValue) return ok }, func(executionCtx context.Context, providerValue accountdomain.Provider, credential accountdomain.Credential, upstream string) (voiceExecutionResult, error) { @@ -121,13 +129,12 @@ func (s *Service) SynthesizeSpeech(ctx context.Context, input TTSInput) (*Result Status: fmt.Sprintf("%d %s", http.StatusOK, http.StatusText(http.StatusOK)), Header: header, Body: io.NopCloser(bytes.NewReader(result.Audio)), - QuotaUnits: 1, }, pricing: reservation}, nil }) } func (s *Service) ListTTSVoices(ctx context.Context, input VoiceListInput) (*Result, error) { - return s.executeVoice(ctx, input.RequestID, input.ClientKey, input.PublicModel, audit.OperationTTS, modeldomain.CapabilityTTS, false, audit.PricingResult{}, func(providerValue accountdomain.Provider) bool { + return s.executeVoice(ctx, input.RequestID, input.ClientKey, input.PublicModel, audit.OperationTTS, modeldomain.CapabilityTTS, false, audit.PricingResult{}, "", "", "", nil, func(providerValue accountdomain.Provider) bool { _, ok := s.providers.TTS(providerValue) return ok }, func(executionCtx context.Context, providerValue accountdomain.Provider, credential accountdomain.Credential, _ string) (voiceExecutionResult, error) { @@ -156,7 +163,7 @@ func (s *Service) ListTTSVoices(ctx context.Context, input VoiceListInput) (*Res } func (s *Service) GetTTSVoice(ctx context.Context, input VoiceIDInput) (*Result, error) { - return s.executeVoice(ctx, input.RequestID, input.ClientKey, input.PublicModel, audit.OperationTTS, modeldomain.CapabilityTTS, false, audit.PricingResult{}, func(providerValue accountdomain.Provider) bool { + return s.executeVoice(ctx, input.RequestID, input.ClientKey, input.PublicModel, audit.OperationTTS, modeldomain.CapabilityTTS, false, audit.PricingResult{}, "", "", "", nil, func(providerValue accountdomain.Provider) bool { _, ok := s.providers.TTS(providerValue) return ok }, func(executionCtx context.Context, providerValue accountdomain.Provider, credential accountdomain.Credential, _ string) (voiceExecutionResult, error) { @@ -181,7 +188,7 @@ func (s *Service) GetTTSVoice(ctx context.Context, input VoiceIDInput) (*Result, } func (s *Service) TranscribeSpeech(ctx context.Context, input STTInput) (*Result, error) { - return s.executeVoice(ctx, input.RequestID, input.ClientKey, input.PublicModel, audit.OperationSTT, modeldomain.CapabilitySTT, true, audit.PricingResult{}, func(providerValue accountdomain.Provider) bool { + return s.executeVoice(ctx, input.RequestID, input.ClientKey, input.PublicModel, audit.OperationSTT, modeldomain.CapabilitySTT, true, audit.PricingResult{}, input.RequestBody, input.Method, input.Path, input.Headers, func(providerValue accountdomain.Provider) bool { _, ok := s.providers.STT(providerValue) return ok }, func(executionCtx context.Context, providerValue accountdomain.Provider, credential accountdomain.Credential, upstream string) (voiceExecutionResult, error) { @@ -277,6 +284,10 @@ func (s *Service) executeVoice( capability modeldomain.Capability, consumesQuota bool, reservation audit.PricingResult, + requestBody string, + method string, + path string, + headers map[string][]string, supports voiceProviderSupport, execute func(context.Context, accountdomain.Provider, accountdomain.Credential, string) (voiceExecutionResult, error), ) (*Result, error) { @@ -303,6 +314,10 @@ func (s *Service) executeVoice( ClientIP: requestmeta.ClientIP(ctx), ModelRouteID: route.ID, ModelPublicID: externalModel, ModelUpstreamModel: modeldomain.DisplayUpstreamModel(route.Provider, route.UpstreamModel), Provider: string(route.Provider), Operation: operation, UsageSource: audit.UsageSourceNone, + RequestMethod: method, + RequestPath: path, + RequestHeaders: headers, + RequestBody: requestBody, } if err := s.checkLedgerReady(); err != nil { return nil, err diff --git a/backend/internal/domain/audit/audit.go b/backend/internal/domain/audit/audit.go index 6a8ea6cf4..ee850a2a3 100644 --- a/backend/internal/domain/audit/audit.go +++ b/backend/internal/domain/audit/audit.go @@ -111,6 +111,10 @@ type Record struct { FirstTokenMS *int64 DurationMS int64 ErrorCode string + RequestMethod string + RequestPath string + RequestHeaders map[string][]string + RequestBody string AttemptCount int Attempts []Attempt CreatedAt time.Time diff --git a/backend/internal/infra/persistence/relational/audit_repository.go b/backend/internal/infra/persistence/relational/audit_repository.go index 80536ee3b..c73393900 100644 --- a/backend/internal/infra/persistence/relational/audit_repository.go +++ b/backend/internal/infra/persistence/relational/audit_repository.go @@ -351,6 +351,12 @@ func toAuditModels(value audit.Record) (requestAuditModel, []requestAuditAttempt digest := sha256.Sum256([]byte(fmt.Sprintf("%s\x00%d\x00%d\x00%d", value.RequestID, value.ClientKeyID, value.ModelRouteID, value.CreatedAt.UnixNano()))) eventID = fmt.Sprintf("evt_%x", digest[:18]) } + requestHeadersJSON := "{}" + if len(value.RequestHeaders) > 0 { + if raw, err := json.Marshal(value.RequestHeaders); err == nil { + requestHeadersJSON = string(raw) + } + } row := requestAuditModel{ EventID: truncate(eventID, 64), RequestID: truncate(value.RequestID, 64), ClientKeyID: value.ClientKeyID, ClientKeyName: truncate(value.ClientKeyName, 160), ClientIP: strings.TrimSpace(value.ClientIP), ModelRouteID: value.ModelRouteID, ModelPublicID: truncate(value.ModelPublicID, 255), ModelUpstreamModel: truncate(value.ModelUpstreamModel, 255), @@ -365,7 +371,12 @@ func toAuditModels(value audit.Record) (requestAuditModel, []requestAuditAttempt EstimatedCostInUSDTicks: nonNegative(value.EstimatedCostInUSDTicks), PricingModel: truncate(value.PricingModel, 100), PricingVersion: truncate(value.PricingVersion, 20), NumSourcesUsed: nonNegative(value.NumSourcesUsed), NumServerSideToolsUsed: nonNegative(value.NumServerSideToolsUsed), ContextInputTokens: nonNegative(value.ContextInputTokens), ContextOutputTokens: nonNegative(value.ContextOutputTokens), FirstTokenMS: normalizedFirstToken(value), DurationMS: nonNegative(value.DurationMS), - ErrorCode: truncate(value.ErrorCode, 100), AttemptCount: len(value.Attempts), CreatedAt: value.CreatedAt, + ErrorCode: truncate(value.ErrorCode, 100), + RequestMethod: truncate(value.RequestMethod, 16), + RequestPath: truncate(value.RequestPath, 2048), + RequestHeadersJSON: truncate(requestHeadersJSON, 65536), + RequestBody: truncate(value.RequestBody, 16777216), + AttemptCount: len(value.Attempts), CreatedAt: value.CreatedAt, } attempts := make([]requestAuditAttemptModel, 0, len(value.Attempts)) for _, attempt := range value.Attempts { diff --git a/backend/internal/infra/persistence/relational/audit_repository_test.go b/backend/internal/infra/persistence/relational/audit_repository_test.go index 916b06e7c..97cb2c8cf 100644 --- a/backend/internal/infra/persistence/relational/audit_repository_test.go +++ b/backend/internal/infra/persistence/relational/audit_repository_test.go @@ -556,4 +556,66 @@ func TestDegradeTimestampScansPostgresTimeValue(t *testing.T) { } } +func TestAuditRepositoryRequestBody(t *testing.T) { + ctx := context.Background() + database, err := OpenSQLite(ctx, filepath.Join(t.TempDir(), "audit-request-body.db")) + if err != nil { + t.Fatal(err) + } + defer database.Close() + if err := database.InitializeSchema(ctx); err != nil { + t.Fatal(err) + } + repository := NewAuditRepository(database) + now := time.Now().UTC() + reqJSON := `{"model":"grok-4","messages":[{"role":"user","content":"Hello world"}]}` + reqHeaders := map[string][]string{ + "User-Agent": {"curl/8.4.0"}, + "Content-Type": {"application/json"}, + } + record := audit.Record{ + RequestID: "req-with-body", + ClientKeyID: 1, + ModelRouteID: 1, + StatusCode: 200, + RequestMethod: "POST", + RequestPath: "/v1/chat/completions", + RequestHeaders: reqHeaders, + RequestBody: reqJSON, + CreatedAt: now, + } + if err := repository.Create(ctx, record); err != nil { + t.Fatal(err) + } + items, _, err := repository.List(ctx, 0, 10) + if err != nil { + t.Fatal(err) + } + if len(items) != 1 { + t.Fatalf("items len = %d, want 1", len(items)) + } + if items[0].RequestBody != reqJSON { + t.Fatalf("RequestBody = %q, want %q", items[0].RequestBody, reqJSON) + } + if items[0].RequestMethod != "POST" { + t.Fatalf("RequestMethod = %q, want POST", items[0].RequestMethod) + } + if items[0].RequestPath != "/v1/chat/completions" { + t.Fatalf("RequestPath = %q, want /v1/chat/completions", items[0].RequestPath) + } + if items[0].RequestHeaders["User-Agent"][0] != "curl/8.4.0" { + t.Fatalf("RequestHeaders = %#v", items[0].RequestHeaders) + } + detail, err := repository.Get(ctx, items[0].ID) + if err != nil { + t.Fatal(err) + } + if detail.RequestBody != reqJSON { + t.Fatalf("Detail RequestBody = %q, want %q", detail.RequestBody, reqJSON) + } + if detail.RequestMethod != "POST" || detail.RequestPath != "/v1/chat/completions" || detail.RequestHeaders["User-Agent"][0] != "curl/8.4.0" { + t.Fatalf("Detail HTTP fields mismatch: %#v", detail) + } +} + func uint64Pointer(value uint64) *uint64 { return &value } diff --git a/backend/internal/infra/persistence/relational/mapping.go b/backend/internal/infra/persistence/relational/mapping.go index c6ce6e682..e33e8e655 100644 --- a/backend/internal/infra/persistence/relational/mapping.go +++ b/backend/internal/infra/persistence/relational/mapping.go @@ -252,6 +252,10 @@ func toClientKeyDomain(value clientKeyModel, allowedModels []uint64) clientkey.K } func toAuditDomain(value requestAuditModel) audit.Record { + var requestHeaders map[string][]string + if strings.TrimSpace(value.RequestHeadersJSON) != "" && value.RequestHeadersJSON != "{}" { + _ = json.Unmarshal([]byte(value.RequestHeadersJSON), &requestHeaders) + } return audit.Record{ ID: value.ID, EventID: value.EventID, RequestID: value.RequestID, ClientKeyID: value.ClientKeyID, ClientKeyName: value.ClientKeyName, ClientIP: value.ClientIP, ModelRouteID: value.ModelRouteID, ModelPublicID: value.ModelPublicID, ModelUpstreamModel: value.ModelUpstreamModel, @@ -266,7 +270,7 @@ func toAuditDomain(value requestAuditModel) audit.Record { EstimatedCostInUSDTicks: value.EstimatedCostInUSDTicks, PricingModel: value.PricingModel, PricingVersion: value.PricingVersion, NumSourcesUsed: value.NumSourcesUsed, NumServerSideToolsUsed: value.NumServerSideToolsUsed, ContextInputTokens: value.ContextInputTokens, ContextOutputTokens: value.ContextOutputTokens, FirstTokenMS: value.FirstTokenMS, DurationMS: value.DurationMS, - ErrorCode: value.ErrorCode, AttemptCount: value.AttemptCount, CreatedAt: value.CreatedAt, + ErrorCode: value.ErrorCode, RequestMethod: value.RequestMethod, RequestPath: value.RequestPath, RequestHeaders: requestHeaders, RequestBody: value.RequestBody, AttemptCount: value.AttemptCount, CreatedAt: value.CreatedAt, } } diff --git a/backend/internal/infra/persistence/relational/models.go b/backend/internal/infra/persistence/relational/models.go index 4de60c6d1..90f402ba5 100644 --- a/backend/internal/infra/persistence/relational/models.go +++ b/backend/internal/infra/persistence/relational/models.go @@ -329,6 +329,10 @@ type requestAuditModel struct { FirstTokenMS *int64 `gorm:"column:first_token_ms"` DurationMS int64 `gorm:"not null;default:0"` ErrorCode string `gorm:"size:100;check:chk_request_audits_error_code,length(error_code) <= 100"` + RequestMethod string `gorm:"size:16;not null;default:'';check:chk_request_audits_request_method,length(request_method) <= 16"` + RequestPath string `gorm:"type:text;not null;default:'';check:chk_request_audits_request_path,length(request_path) <= 2048"` + RequestHeadersJSON string `gorm:"type:text;not null;default:'{}';check:chk_request_audits_request_headers,length(request_headers_json) <= 65536"` + RequestBody string `gorm:"type:text;not null;default:'';check:chk_request_audits_request_body,length(request_body) <= 16777216"` AttemptCount int `gorm:"not null;default:0;check:chk_request_audits_attempt_count,attempt_count >= 0"` CreatedAt time.Time `gorm:"not null"` } diff --git a/backend/internal/transport/http/audit/handler.go b/backend/internal/transport/http/audit/handler.go index 88ce7a750..18fd2f624 100644 --- a/backend/internal/transport/http/audit/handler.go +++ b/backend/internal/transport/http/audit/handler.go @@ -125,6 +125,10 @@ type auditResponse struct { OutputTokensPerSecond *float64 `json:"outputTokensPerSecond,omitempty"` DurationMS int64 `json:"durationMs"` ErrorCode string `json:"errorCode,omitempty"` + RequestMethod string `json:"requestMethod,omitempty"` + RequestPath string `json:"requestPath,omitempty"` + RequestHeaders map[string][]string `json:"requestHeaders,omitempty"` + RequestBody string `json:"requestBody,omitempty"` AttemptCount int `json:"attemptCount"` CreatedAt time.Time `json:"createdAt"` } @@ -489,7 +493,13 @@ func newAuditResponse(value auditdomain.Record) auditResponse { NumSourcesUsed: value.NumSourcesUsed, NumServerSideToolsUsed: value.NumServerSideToolsUsed, ContextInputTokens: value.ContextInputTokens, ContextOutputTokens: value.ContextOutputTokens, FirstTokenMS: value.FirstTokenMS, OutputTokensPerSecond: auditOutputTokensPerSecond(value), DurationMS: value.DurationMS, - ErrorCode: value.ErrorCode, AttemptCount: value.AttemptCount, CreatedAt: value.CreatedAt, + ErrorCode: value.ErrorCode, + RequestMethod: value.RequestMethod, + RequestPath: value.RequestPath, + RequestHeaders: value.RequestHeaders, + RequestBody: value.RequestBody, + AttemptCount: value.AttemptCount, + CreatedAt: value.CreatedAt, } } diff --git a/backend/internal/transport/http/inference/handler.go b/backend/internal/transport/http/inference/handler.go index af803a0fc..d45081452 100644 --- a/backend/internal/transport/http/inference/handler.go +++ b/backend/internal/transport/http/inference/handler.go @@ -325,6 +325,9 @@ func (h *Handler) createChatCompletion(c *gin.Context) { PromptCacheSeed: extractPromptCacheSeed(c.Request.Header, body), AllowClientToolCacheRoute: allowBuildClientToolCacheRoute(c.Request.Header), GrokTurnIndex: c.GetHeader("x-grok-turn-idx"), + Method: c.Request.Method, + Path: c.Request.URL.RequestURI(), + Headers: c.Request.Header.Clone(), }) if err != nil { writeGatewayError(c, err) @@ -367,6 +370,9 @@ func (h *Handler) createMessage(c *gin.Context) { PromptCacheSeed: extractPromptCacheSeed(c.Request.Header, body), AllowClientToolCacheRoute: allowBuildClientToolCacheRoute(c.Request.Header), GrokTurnIndex: c.GetHeader("x-grok-turn-idx"), + Method: c.Request.Method, + Path: c.Request.URL.RequestURI(), + Headers: c.Request.Header.Clone(), }) if err != nil { writeGatewayAnthropicError(c, err) @@ -381,8 +387,13 @@ func (h *Handler) generateImage(c *gin.Context) { writeOpenAIError(c, http.StatusUnsupportedMediaType, "invalid_request", "图片生成仅支持 application/json") return } + body, err := io.ReadAll(c.Request.Body) + if err != nil { + writeOpenAIError(c, http.StatusRequestEntityTooLarge, "request_too_large", "请求体超过限制") + return + } var request imageGenerationRequest - if decodeSingleJSON(c.Request.Body, &request, false) != nil || strings.TrimSpace(request.Model) == "" || strings.TrimSpace(request.Prompt) == "" { + if decodeSingleJSON(bytes.NewReader(body), &request, false) != nil || strings.TrimSpace(request.Model) == "" || strings.TrimSpace(request.Prompt) == "" { writeOpenAIError(c, http.StatusBadRequest, "invalid_request", "图片请求缺少有效 model 或 prompt") return } @@ -427,7 +438,8 @@ func (h *Handler) generateImage(c *gin.Context) { RequestID: requestID, ClientKey: clientKey, PublicModel: request.Model, Prompt: request.Prompt, Count: count, Size: request.Size, AspectRatio: request.AspectRatio, Resolution: request.Resolution, Quality: quality, ResponseFormat: request.ResponseFormat, - Streaming: request.Stream, PartialImages: partialImages, + Streaming: request.Stream, PartialImages: partialImages, RequestBody: string(body), + Method: c.Request.Method, Path: c.Request.URL.RequestURI(), Headers: c.Request.Header.Clone(), }) if err != nil { writeGatewayError(c, err) @@ -572,8 +584,13 @@ func (h *Handler) editImage(c *gin.Context) { writeOpenAIError(c, http.StatusUnsupportedMediaType, "invalid_request", "图片编辑仅支持 application/json") return } + body, err := io.ReadAll(c.Request.Body) + if err != nil { + writeOpenAIError(c, http.StatusRequestEntityTooLarge, "request_too_large", "请求体超过限制") + return + } var request imageEditJSONRequest - if err := decodeSingleJSON(c.Request.Body, &request, false); err != nil { + if err := decodeSingleJSON(bytes.NewReader(body), &request, false); err != nil { writeOpenAIError(c, http.StatusBadRequest, "invalid_request", "图片编辑 JSON 请求无效") return } @@ -660,7 +677,8 @@ func (h *Handler) editImage(c *gin.Context) { RequestID: requestID, ClientKey: clientKey, PublicModel: model, Prompt: prompt, ImageURLs: imageURLs, Count: count, Size: size, AspectRatio: aspectRatio, Resolution: resolution, Quality: quality, ResponseFormat: request.ResponseFormat, - Streaming: request.Stream, PartialImages: partialImages, + Streaming: request.Stream, PartialImages: partialImages, RequestBody: string(body), + Method: c.Request.Method, Path: c.Request.URL.RequestURI(), Headers: c.Request.Header.Clone(), }) if err != nil { writeGatewayError(c, err) @@ -1140,6 +1158,9 @@ func (h *Handler) handleCreate(c *gin.Context, compact bool) { PromptCacheSeed: extractPromptCacheSeed(c.Request.Header, body), PreviousResponseID: request.PreviousResponseID, AllowClientToolCacheRoute: allowBuildClientToolCacheRoute(c.Request.Header), GrokTurnIndex: c.GetHeader("x-grok-turn-idx"), + Method: c.Request.Method, + Path: c.Request.URL.RequestURI(), + Headers: c.Request.Header.Clone(), } var result *gateway.Result if compact { diff --git a/backend/internal/transport/http/inference/openai_audio_handler.go b/backend/internal/transport/http/inference/openai_audio_handler.go index ba964d316..5d77093f3 100644 --- a/backend/internal/transport/http/inference/openai_audio_handler.go +++ b/backend/internal/transport/http/inference/openai_audio_handler.go @@ -1,7 +1,9 @@ package inference import ( + "bytes" "encoding/json" + "io" "net/http" "strings" @@ -43,8 +45,13 @@ func (h *Handler) handleOpenAISpeech(c *gin.Context) { writeOpenAIError(c, http.StatusUnsupportedMediaType, "invalid_request", "audio speech 仅支持 application/json") return } + body, err := io.ReadAll(c.Request.Body) + if err != nil { + writeOpenAIError(c, http.StatusRequestEntityTooLarge, "request_too_large", "请求体超过限制") + return + } var request openAISpeechRequest - if err := decodeSingleJSON(c.Request.Body, &request, false); err != nil { + if err := decodeSingleJSON(bytes.NewReader(body), &request, false); err != nil { writeOpenAIError(c, http.StatusBadRequest, "invalid_request", "audio speech 请求无效") return } @@ -109,6 +116,10 @@ func (h *Handler) handleOpenAISpeech(c *gin.Context) { OutputFormat: format, Speed: speed, OptimizeStreamingLatency: optimize, + RequestBody: string(body), + Method: c.Request.Method, + Path: c.Request.URL.RequestURI(), + Headers: c.Request.Header.Clone(), } if request.TextNormalization != nil { input.TextNormalization = *request.TextNormalization diff --git a/backend/internal/transport/http/inference/voice_handler.go b/backend/internal/transport/http/inference/voice_handler.go index b727ccd02..1ad4f7b4f 100644 --- a/backend/internal/transport/http/inference/voice_handler.go +++ b/backend/internal/transport/http/inference/voice_handler.go @@ -1,6 +1,7 @@ package inference import ( + "bytes" "encoding/json" "errors" "io" @@ -33,8 +34,13 @@ func (h *Handler) synthesizeSpeech(c *gin.Context) { writeOpenAIError(c, http.StatusUnsupportedMediaType, "invalid_request", "TTS 仅支持 application/json") return } + body, err := io.ReadAll(c.Request.Body) + if err != nil { + writeOpenAIError(c, http.StatusRequestEntityTooLarge, "request_too_large", "请求体超过限制") + return + } var request ttsRequest - if err := decodeSingleJSON(c.Request.Body, &request, false); err != nil { + if err := decodeSingleJSON(bytes.NewReader(body), &request, false); err != nil { writeOpenAIError(c, http.StatusBadRequest, "invalid_request", "TTS 请求无效") return } @@ -69,7 +75,8 @@ func (h *Handler) synthesizeSpeech(c *gin.Context) { } input := gateway.TTSInput{ RequestID: requestID, ClientKey: clientKey, PublicModel: model, Text: text, VoiceID: strings.TrimSpace(request.VoiceID), - Language: language, OutputFormat: format, Speed: speed, OptimizeStreamingLatency: optimize, + Language: language, OutputFormat: format, Speed: speed, OptimizeStreamingLatency: optimize, RequestBody: string(body), + Method: c.Request.Method, Path: c.Request.URL.RequestURI(), Headers: c.Request.Header.Clone(), } if request.TextNormalization != nil { input.TextNormalization = *request.TextNormalization @@ -283,6 +290,9 @@ func (h *Handler) transcribeSpeechRequest(c *gin.Context, openAICompatible bool) } input.ClientKey = clientKey input.RequestID = requestID + input.Method = c.Request.Method + input.Path = c.Request.URL.RequestURI() + input.Headers = c.Request.Header.Clone() result, err := h.gateway.TranscribeSpeech(c.Request.Context(), input) if err != nil { writeGatewayError(c, err) diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 000000000..729512940 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,5131 @@ +{ + "name": "grok2api-frontend", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "grok2api-frontend", + "version": "0.1.0", + "dependencies": { + "@hookform/resolvers": "^5.4.0", + "@radix-ui/react-alert-dialog": "^1.1.19", + "@radix-ui/react-checkbox": "^1.3.7", + "@radix-ui/react-dialog": "^1.1.19", + "@radix-ui/react-dropdown-menu": "^2.1.20", + "@radix-ui/react-label": "^2.1.11", + "@radix-ui/react-popover": "^1.1.19", + "@radix-ui/react-select": "^2.3.3", + "@radix-ui/react-slot": "^1.3.0", + "@radix-ui/react-switch": "^1.3.3", + "@radix-ui/react-tabs": "^1.1.17", + "@radix-ui/react-tooltip": "^1.2.12", + "@shadcn/react": "^0.2.1", + "@tanstack/react-query": "^5.101.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "date-fns": "^4.4.0", + "i18next": "^26.3.6", + "lucide-react": "^1.24.0", + "marked": "^18.0.6", + "next-themes": "^0.4.6", + "react": "^19.2.7", + "react-day-picker": "^10.0.1", + "react-dom": "^19.2.7", + "react-hook-form": "^7.81.0", + "react-i18next": "^17.0.9", + "react-router-dom": "^7.18.1", + "recharts": "2.15.4", + "sonner": "^2.0.7", + "tailwind-merge": "^3.6.0", + "tw-animate-css": "^1.4.0", + "zod": "^4.4.3" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@tailwindcss/vite": "^4.3.2", + "@types/node": "^26.1.1", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.3", + "eslint": "^10.6.0", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.3", + "globals": "^17.7.0", + "tailwindcss": "^4.3.2", + "typescript": "^6.0.2", + "typescript-eslint": "^8.63.0", + "vite": "^8.1.4" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.8", + "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.8", + "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@date-fns/tz": { + "version": "1.5.0", + "resolved": "https://registry.npmmirror.com/@date-fns/tz/-/tz-1.5.0.tgz", + "integrity": "sha512-lwYN/vDPeNRULcepoE/LO2Pgx+7/RV+S9ARfbc9lr2DtGkOD7pAiruHvbR1RX3Qyf6ja47EWJDMsNK5vK08DJg==", + "license": "MIT" + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmmirror.com/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" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmmirror.com/@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" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.7.0", + "resolved": "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", + "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.8.0", + "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.8.0.tgz", + "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.8.0", + "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.8.0.tgz", + "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", + "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.8.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.12", + "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.12.tgz", + "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", + "license": "MIT" + }, + "node_modules/@hookform/resolvers": { + "version": "5.9.1", + "resolved": "https://registry.npmmirror.com/@hookform/resolvers/-/resolvers-5.9.1.tgz", + "integrity": "sha512-7b7vsbraJxKgjVSA1Nur9tLwj539WGJUBLA7QNvXnFoT2pM5Z7G+6rlukk4B2/QrTZy6huRtH6wKeESPKuIr6w==", + "license": "MIT", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, + "peerDependencies": { + "@sinclair/typebox": ">=0.25.24", + "@standard-schema/spec": "^1.0.0", + "@typeschema/main": ">=0.13.7", + "@vinejs/vine": "^2.0.0 || ^3.0.0 || ^4.0.0", + "ajv": "^8.12.0", + "ajv-errors": "^3.0.0", + "ajv-formats": "^2.1.1", + "arktype": "^2.0.0", + "ata-validator": "^1.2.0", + "class-transformer": ">=0.4.0", + "class-validator": ">=0.12.0", + "computed-types": "^1.0.0", + "effect": "^3.10.3", + "fluentvalidation-ts": "^3.0.0", + "fp-ts": "^2.7.0", + "io-ts": "^2.0.0", + "joi": "^17.0.0 || ^18.0.0", + "nope-validator": ">=0.12.0", + "react-hook-form": "^7.55.0", + "superstruct": ">=0.12.0", + "typanion": "^3.3.2", + "valibot": ">=0.31.0 || ^1.0.0-beta.4 || ^1.0.0-rc", + "vest": ">=6.0.0", + "yup": "^1.0.0", + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "@sinclair/typebox": { + "optional": true + }, + "@standard-schema/spec": { + "optional": true + }, + "@typeschema/main": { + "optional": true + }, + "@vinejs/vine": { + "optional": true + }, + "ajv": { + "optional": true + }, + "ajv-errors": { + "optional": true + }, + "ajv-formats": { + "optional": true + }, + "arktype": { + "optional": true + }, + "ata-validator": { + "optional": true + }, + "class-transformer": { + "optional": true + }, + "class-validator": { + "optional": true + }, + "computed-types": { + "optional": true + }, + "effect": { + "optional": true + }, + "fluentvalidation-ts": { + "optional": true + }, + "fp-ts": { + "optional": true + }, + "io-ts": { + "optional": true + }, + "joi": { + "optional": true + }, + "nope-validator": { + "optional": true + }, + "superstruct": { + "optional": true + }, + "typanion": { + "optional": true + }, + "valibot": { + "optional": true + }, + "vest": { + "optional": true + }, + "yup": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmmirror.com/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmmirror.com/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmmirror.com/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmmirror.com/@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" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmmirror.com/@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" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmmirror.com/@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" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.144.0", + "resolved": "https://registry.npmmirror.com/@oxc-project/types/-/types-0.144.0.tgz", + "integrity": "sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@radix-ui/number": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/@radix-ui/number/-/number-1.1.3.tgz", + "integrity": "sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.7", + "resolved": "https://registry.npmmirror.com/@radix-ui/primitive/-/primitive-1.1.7.tgz", + "integrity": "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.23", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.23.tgz", + "integrity": "sha512-VAYOiQRqj3GPpYJE0I9J+X8Ip05cyVlNdKOFeiGS2Ou1HHGfpl0BxOyZm6nmVDyU+W+NF3/XLzmjHmVGydhwgA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dialog": "1.1.23", + "@radix-ui/react-primitive": "2.1.10" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.15", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-arrow/-/react-arrow-1.1.15.tgz", + "integrity": "sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.3.11", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-checkbox/-/react-checkbox-1.3.11.tgz", + "integrity": "sha512-Gnptr9pDDQxD3hgq2dtPbtrp/c2qH1mBwIzw3X/ivrMb2e1t0jMTi606fVEqFPaQR1ggXIVQWKj3P2WW9v7zGQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-size": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.15", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-collection/-/react-collection-1.1.15.tgz", + "integrity": "sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.5", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz", + "integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.2.2", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-context/-/react-context-1.2.2.tgz", + "integrity": "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.23", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-dialog/-/react-dialog-1.1.23.tgz", + "integrity": "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-direction/-/react-direction-1.1.4.tgz", + "integrity": "sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.19", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz", + "integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-effect-event": "0.0.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.24", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.24.tgz", + "integrity": "sha512-geq8l2rJkxvkXsT9RMgtUE3P8pITFpTsvYpbySi1IH4fZEABD/Gp85myayFgxk0ktljGMJnCbeFkyTusvSvv7g==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-menu": "2.1.24", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.6", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.6.tgz", + "integrity": "sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.16", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.16.tgz", + "integrity": "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-id/-/react-id-1.1.4.tgz", + "integrity": "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.15", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-label/-/react-label-2.1.15.tgz", + "integrity": "sha512-o/rdYEwZTTo5tjknnPeyQFU45kUC4i/XyeDPP+HGyi6XqpOP6Zf5Ya5vh/Yfe9Id5JiuWnnAx2XqIeD3UYZt0g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.24", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-menu/-/react-menu-2.1.24.tgz", + "integrity": "sha512-uW7RVuU6Lp/ZtfeY4b3kL32zccgEWvPv1+cf17ubYzHa9cL8AHokmk36cG/XEiH/smbQvumnieXX9j/e9RqJWA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-callback-ref": "1.1.4", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover": { + "version": "1.1.23", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-popover/-/react-popover-1.1.23.tgz", + "integrity": "sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.3.7", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-popper/-/react-popper-1.3.7.tgz", + "integrity": "sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-rect": "1.1.4", + "@radix-ui/react-use-size": "1.1.4", + "@radix-ui/rect": "1.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.17", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-portal/-/react-portal-1.1.17.tgz", + "integrity": "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.10", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-presence/-/react-presence-1.1.10.tgz", + "integrity": "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.10", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-primitive/-/react-primitive-2.1.10.tgz", + "integrity": "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.3.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.19", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.19.tgz", + "integrity": "sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.3.7", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-select/-/react-select-2.3.7.tgz", + "integrity": "sha512-WFGImkmbzcfxeIwq/+4HvRN0pizBwbwQUED4I13ezQsDdfl38ZntN6TmR8XaSzPBqoCToe8rF75j6NPNDSzhbg==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.3", + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-collection": "1.1.15", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-focus-guards": "1.1.6", + "@radix-ui/react-focus-scope": "1.1.16", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-callback-ref": "1.1.4", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-previous": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.11", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.3.3.tgz", + "integrity": "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch": { + "version": "1.3.7", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-switch/-/react-switch-1.3.7.tgz", + "integrity": "sha512-48tB/4dn2UVLBCYhTu9AuR63IHl73l/qLbLgxd86noTUor4/K4LFDAcYjK+isP5313qxaFpjPVogE7+Y0/V3Kw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-size": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.21", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-tabs/-/react-tabs-1.1.21.tgz", + "integrity": "sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-direction": "1.1.4", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-roving-focus": "1.1.19", + "@radix-ui/react-use-controllable-state": "1.2.6" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.2.16", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-tooltip/-/react-tooltip-1.2.16.tgz", + "integrity": "sha512-6EamKFRRnlpdadndbZ6LMwycfwkwPte1B42hs6QA0gYhjaOKqW4PZ4pjaW9UrlDX5eVt/OjncE7BFTPL5nmZhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.2.2", + "@radix-ui/react-dismissable-layer": "1.1.19", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.3.7", + "@radix-ui/react-portal": "1.1.17", + "@radix-ui/react-presence": "1.1.10", + "@radix-ui/react-primitive": "2.1.10", + "@radix-ui/react-slot": "1.3.3", + "@radix-ui/react-use-controllable-state": "1.2.6", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.11" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz", + "integrity": "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.6", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.6.tgz", + "integrity": "sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.7", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.5", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz", + "integrity": "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-is-hydrated": { + "version": "0.1.3", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.3.tgz", + "integrity": "sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz", + "integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-previous/-/react-use-previous-1.1.4.tgz", + "integrity": "sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-rect/-/react-use-rect-1.1.4.tgz", + "integrity": "sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.4", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-size/-/react-use-size-1.1.4.tgz", + "integrity": "sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.11", + "resolved": "https://registry.npmmirror.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.11.tgz", + "integrity": "sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.10" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/@radix-ui/rect/-/rect-1.1.3.tgz", + "integrity": "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==", + "license": "MIT" + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.4.tgz", + "integrity": "sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-Dc5mPD8F5F/FS8i01syd7FTF6yB2fVthH/TRkjwJkzUK6EpoxHtqvZQP5Zwq80/5z19TWYHIg1KOHboCgVx/aQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.4.tgz", + "integrity": "sha512-fpDm4oBo6SqLvWUYCmFhdde3U9KH2fRNNMeAnAPAIwxRL345xutL0EtEUcuoxsoazdJGv/MuDBQHlCDrtbvqOg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.4.tgz", + "integrity": "sha512-rSJoreDE/HoIzoaib6MTp5jQtCTdMHKIvItAKT/ImS6Y6Ww76oUaeMyp4Vc/fAgd/ehji068IxetHXAnqUwN9A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.4.tgz", + "integrity": "sha512-/jm8OGHgn7oGaJu3i/qZI9spUGcJ+y/lk43ttQ/iO1tOd9NissG6o97bighBCiL+BKRngmcDuR6ikfwYdJmVuQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.4.tgz", + "integrity": "sha512-tIP06BeD9EqvECBrPZ+sqdPlYrT+aYaAiu1wYziVx5elRK/ftm33JxVDy2bXGbr6J0CrtirCkR87/X5a2euEng==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.4.tgz", + "integrity": "sha512-Ql1Q0EQqVThvn9VAVlwNzsUvbSFtCMGjLpRRi4pk5i7NZZ4n5ISiLMjHYtus4VQ2PvkSw24zyaCVsiS+sXPj1w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.4.tgz", + "integrity": "sha512-GjbjXD4XXfN19D0LZNbmiCBUoDiRACsYHr0yaIbbn8aFsXjHZifcYqu/W5Er5X2X990WjHXFrxarn5chzItorQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.4.tgz", + "integrity": "sha512-p5WR0NOwaRmJ/B1b6IjEFLLivwEsf3PrdBIhRbhTCQisbo2SvHHpG4ELB/+FgQNnB88LTOF86upmJmbvZdQ2lw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.4.tgz", + "integrity": "sha512-4/GyVjmhR+Tc6HLJvwc1sOhPqAZtySiSMesOZyX6JQ5XBxoTDEMKQzvo07NIK6nTon/SivlZqvhzvuVBNQhObQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.4.tgz", + "integrity": "sha512-l9eeLsCNvPpmSXUej0etw/J1eqV0Jj1D5G/xG6YTijmE6dkv6E2QezgWbTfQk63v952DPqrjOCoiqxq7Bw0YUQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.4.tgz", + "integrity": "sha512-e0F355MSTMm3+UOqtV3L24gFUp2N5m1f8L/7d56deik6va+AXdrt9F8LbzGpeWGWRbZEDq4m8NVnJDeBtf9DZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.4.tgz", + "integrity": "sha512-AWLi0uBRYh6QlE7OKhiz+phZC0qwtij2QZmhmOdsLdFn64m7oMpooE9ICE3lhm9xMb4SpDo2WbHcxX1iFLFtqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.4.tgz", + "integrity": "sha512-UwSDJOg3dqCAejWdxclJjCsh3Qq4vLYMDxmyHqo1btz3stK2VqgwNd3mm5tuIwzSlGIQ/1H9Hr+Zn09mrezNqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@shadcn/react": { + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/@shadcn/react/-/react-0.2.1.tgz", + "integrity": "sha512-5krgi3dRMKb5jH6a+qPzVJUy/54s0kKE4Rw4LjDfLqOdVQTWKUgxWf1kW8r912I0jX/Lzxqc+pgjkjWxUIK5BQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": ">=19", + "react": ">=19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmmirror.com/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.1", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/@tailwindcss/vite/-/vite-4.3.3.tgz", + "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.101.4", + "resolved": "https://registry.npmmirror.com/@tanstack/query-core/-/query-core-5.101.4.tgz", + "integrity": "sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.101.4", + "resolved": "https://registry.npmmirror.com/@tanstack/react-query/-/react-query-5.101.4.tgz", + "integrity": "sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.101.4" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmmirror.com/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.8", + "resolved": "https://registry.npmmirror.com/@types/d3-shape/-/d3-shape-3.1.8.tgz", + "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmmirror.com/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmmirror.com/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "26.2.0", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-26.2.0.tgz", + "integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~8.3.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.18", + "resolved": "https://registry.npmmirror.com/@types/react/-/react-19.2.18.tgz", + "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmmirror.com/@types/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.67.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz", + "integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/type-utils": "8.67.0", + "@typescript-eslint/utils": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.67.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.67.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.67.0.tgz", + "integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.67.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.67.0.tgz", + "integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.67.0", + "@typescript-eslint/types": "^8.67.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.67.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz", + "integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.67.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz", + "integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.67.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz", + "integrity": "sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.67.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.67.0.tgz", + "integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.67.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz", + "integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.67.0", + "@typescript-eslint/tsconfig-utils": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/visitor-keys": "8.67.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.67.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.67.0.tgz", + "integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.67.0", + "@typescript-eslint/types": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.67.0", + "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz", + "integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.67.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.5", + "resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-6.0.5.tgz", + "integrity": "sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/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" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmmirror.com/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.15", + "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.15.tgz", + "integrity": "sha512-FwMjJJ7HnyZpWe+oWxegG0fezZyBZUagI5LZEoO3GCbtbKNwRfMH9Ue5d5v01PNePBy1QSfPSDTTeVL0Hb9EzA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.28.8", + "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.8.tgz", + "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.12", + "caniuse-lite": "^1.0.30001809", + "electron-to-chromium": "^1.5.402", + "node-releases": "^2.0.53", + "update-browserslist-db": "^1.3.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001809", + "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz", + "integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmmirror.com/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmmirror.com/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", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmmirror.com/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.2", + "resolved": "https://registry.npmmirror.com/d3-format/-/d3-format-3.1.2.tgz", + "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/date-fns": { + "version": "4.4.0", + "resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-4.4.0.tgz", + "integrity": "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmmirror.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmmirror.com/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.411", + "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.411.tgz", + "integrity": "sha512-gglkxzokjHfawpGxq75XdBV2/l3BAPzrsMs70qgaZdTW5rpV1tC4MdgJVP9fN126bODA4ZJQkn1wryEzJyQXIg==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.24.5", + "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", + "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.8.1", + "resolved": "https://registry.npmmirror.com/eslint/-/eslint-10.8.1.tgz", + "integrity": "sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ==", + "dev": true, + "license": "MIT", + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.7.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmmirror.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.4", + "resolved": "https://registry.npmmirror.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.4.tgz", + "integrity": "sha512-7bqTKz7T0r+HKWFarNXByDE9/5+73wI2ru+M3zuqGbR7s/b/5/pQJXZoufWlrngqGqoZto73ZkGumCdLxk+4rw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmmirror.com/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/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" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/fast-equals": { + "version": "5.4.1", + "resolved": "https://registry.npmmirror.com/fast-equals/-/fast-equals-5.4.1.tgz", + "integrity": "sha512-DjlFSM5Pk9cGcL0q5QXl66eGzx0N6szNgaswwc5ZphlBohjTVJSnGgI+rJVOgOi65qUoQnDZN4nDqi33udtydQ==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmmirror.com/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" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/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" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.11.0", + "resolved": "https://registry.npmmirror.com/globals/-/globals-17.11.0.tgz", + "integrity": "sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmmirror.com/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmmirror.com/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/html-parse-stringify": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/html-parse-stringify/-/html-parse-stringify-4.0.1.tgz", + "integrity": "sha512-0zHsZJrK7S3K2aucXWL6ycoYJ/iNtIcFHC/nYQgFklPtrv5LpJctIiSCroWZWeuoXvuyFdzp6KzjJQ+OT5MfFw==", + "license": "MIT", + "funding": { + "url": "https://locize.com" + } + }, + "node_modules/i18next": { + "version": "26.3.6", + "resolved": "https://registry.npmmirror.com/i18next/-/i18next-26.3.6.tgz", + "integrity": "sha512-Bu5Z2nAXgfVyM8xvW3jk9EKRIuX37PudsrBViThNFx7CR7aaYTpP01cxNB/E4c4UUzTDiAZRstEhsRfPOL/8xA==", + "funding": [ + { + "type": "individual", + "url": "https://www.locize.com/i18next" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + }, + { + "type": "individual", + "url": "https://www.locize.com" + } + ], + "license": "MIT", + "peerDependencies": { + "typescript": "^5 || ^6 || ^7" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmmirror.com/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" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "1.32.0", + "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-1.32.0.tgz", + "integrity": "sha512-txX56hMFnRxPi1f9/nH69YN8uvAO6a7Y1KSWKjCDAtdD9+soEgmWuCt6iRm1pkxUZo2+YntSdsE1L6bIuKoY8Q==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/marked": { + "version": "18.0.10", + "resolved": "https://registry.npmmirror.com/marked/-/marked-18.0.10.tgz", + "integrity": "sha512-FJeH4bRpYoXiggcgriCGItKCSv3xkngJc4QCZ/rkQCogU3VYaLxYJoZl8Nw/b4+x7iij/pd+09mZ6A1dXzpL0A==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next-themes": { + "version": "0.4.6", + "resolved": "https://registry.npmmirror.com/next-themes/-/next-themes-0.4.6.tgz", + "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/node-releases": { + "version": "2.0.53", + "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.53.tgz", + "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmmirror.com/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", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmmirror.com/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" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.8", + "resolved": "https://registry.npmmirror.com/react/-/react-19.2.8.tgz", + "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-day-picker": { + "version": "10.0.1", + "resolved": "https://registry.npmmirror.com/react-day-picker/-/react-day-picker-10.0.1.tgz", + "integrity": "sha512-eNh6BlwcYInWaJtRv18mXQ06Ys/H6rdTZAnTaSdOYJuTpwP1JMCHNd1FDRadA+gbeinq+psdULN5Xnowy9mV8w==", + "license": "MIT", + "dependencies": { + "@date-fns/tz": "^1.4.1", + "date-fns": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/gpbl" + }, + "peerDependencies": { + "@types/react": ">=16.8.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-dom": { + "version": "19.2.8", + "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-19.2.8.tgz", + "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.8" + } + }, + "node_modules/react-hook-form": { + "version": "7.85.0", + "resolved": "https://registry.npmmirror.com/react-hook-form/-/react-hook-form-7.85.0.tgz", + "integrity": "sha512-U2MTriFXnclmV4rOE20p2DcRFv5WEg3FIcBFOKcOLFHDVvGIMPvLTkTWefUsonmlaVy23khVDxDWym6uJVGOzw==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-i18next": { + "version": "17.0.11", + "resolved": "https://registry.npmmirror.com/react-i18next/-/react-i18next-17.0.11.tgz", + "integrity": "sha512-cDtkXgxjuFTWUH6V+aQn1Ve5vDiUztCNPWW5GtSHDccsgRXO1nE6QFWCEmc1KAutrb3OUv87wFShJL5RhUwPXg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "html-parse-stringify": "^4.0.1", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "i18next": ">= 26.2.0", + "react": ">= 16.8.0", + "typescript": "^5 || ^6 || ^7" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmmirror.com/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmmirror.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-router": { + "version": "7.18.2", + "resolved": "https://registry.npmmirror.com/react-router/-/react-router-7.18.2.tgz", + "integrity": "sha512-aUVMjFm3GAPTTZL7oYr5E7ETiqfQCHRLH+B+5afnICvf0r7kkK4eR6SMuwbSTJw/7t+12khT/Kahij49fqOCIg==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.18.2", + "resolved": "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-7.18.2.tgz", + "integrity": "sha512-AIKJ/jgGlFb3EbfCXk5Gzshiwt+l3mqbCrNjmEWMMjqQxNJ3svBa6bgzFyCC2Sw3RA0VWF1kg3uQf2OFhxb8hw==", + "license": "MIT", + "dependencies": { + "react-router": "7.18.2" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-smooth": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/react-smooth/-/react-smooth-4.0.4.tgz", + "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", + "license": "MIT", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmmirror.com/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmmirror.com/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/recharts": { + "version": "2.15.4", + "resolved": "https://registry.npmmirror.com/recharts/-/recharts-2.15.4.tgz", + "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.4", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmmirror.com/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "license": "MIT", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/rolldown": { + "version": "1.2.4", + "resolved": "https://registry.npmmirror.com/rolldown/-/rolldown-1.2.4.tgz", + "integrity": "sha512-rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.144.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.2.4", + "@rolldown/binding-darwin-arm64": "1.2.4", + "@rolldown/binding-darwin-x64": "1.2.4", + "@rolldown/binding-freebsd-x64": "1.2.4", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.4", + "@rolldown/binding-linux-arm64-gnu": "1.2.4", + "@rolldown/binding-linux-arm64-musl": "1.2.4", + "@rolldown/binding-linux-ppc64-gnu": "1.2.4", + "@rolldown/binding-linux-s390x-gnu": "1.2.4", + "@rolldown/binding-linux-x64-gnu": "1.2.4", + "@rolldown/binding-linux-x64-musl": "1.2.4", + "@rolldown/binding-openharmony-arm64": "1.2.4", + "@rolldown/binding-win32-arm64-msvc": "1.2.4", + "@rolldown/binding-win32-x64-msvc": "1.2.4" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmmirror.com/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/sonner": { + "version": "2.0.8", + "resolved": "https://registry.npmmirror.com/sonner/-/sonner-2.0.8.tgz", + "integrity": "sha512-UM/ByIoFra8yzV75n1o0Puu0bw5U/9UNnDacrJNspekBewIfsQ3D6ez1nvlWpt7aTsO6rujQtifBpycwIivqlg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/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" + } + }, + "node_modules/tailwind-merge": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/tailwind-merge/-/tailwind-merge-3.6.0.tgz", + "integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.3", + "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tw-animate-css": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/tw-animate-css/-/tw-animate-css-1.4.0.tgz", + "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Wombosvideo" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "devOptional": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.67.0", + "resolved": "https://registry.npmmirror.com/typescript-eslint/-/typescript-eslint-8.67.0.tgz", + "integrity": "sha512-S2udFs8tCKEKffuJ4TB1idGUZiXdCPGi3IPBGWXarbLQ5UPXORV8QEVzJ4gCRduURMb5EkpNCdjbk0eDIuI8Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.67.0", + "@typescript-eslint/parser": "8.67.0", + "@typescript-eslint/typescript-estree": "8.67.0", + "@typescript-eslint/utils": "8.67.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "8.3.0", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.3.1", + "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz", + "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "devOptional": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmmirror.com/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmmirror.com/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmmirror.com/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/vite": { + "version": "8.2.1", + "resolved": "https://registry.npmmirror.com/vite/-/vite-8.2.1.tgz", + "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.25", + "rolldown": "~1.2.1", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.4.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/vite/node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/vite/node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmmirror.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmmirror.com/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" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/frontend/src/features/audits/request-audit-detail-dialog.tsx b/frontend/src/features/audits/request-audit-detail-dialog.tsx index ef946bf76..0688f9488 100644 --- a/frontend/src/features/audits/request-audit-detail-dialog.tsx +++ b/frontend/src/features/audits/request-audit-detail-dialog.tsx @@ -1,12 +1,27 @@ import { useQuery } from "@tanstack/react-query"; -import { Braces, FileText, Globe2, KeyRound, Network, Server, TriangleAlert } from "lucide-react"; +import { + Check, + CheckCircle2, + Code2, + Copy, + FileCode, + FileText, + Globe2, + KeyRound, + Network, + Server, + TriangleAlert, +} from "lucide-react"; import { useMemo, useState, type ReactNode } from "react"; import { useTranslation } from "react-i18next"; +import { toast } from "sonner"; import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { getRequestAudit, type AuditAttemptDTO, type AuditDTO } from "@/features/audits/request-audits-api"; +import { copyToClipboard } from "@/shared/clipboard"; import { CopyButton } from "@/shared/components/copy-button"; import { ErrorState, LoadingState } from "@/shared/components/data-state"; import { cn } from "@/shared/lib/cn"; @@ -23,9 +38,16 @@ const PRE_UPSTREAM_ERROR_CODES = new Set([ "upstream_unavailable", ]); -export function RequestAuditDetailDialog({ audit, open, onOpenChange }: { audit: AuditDTO | null; open: boolean; onOpenChange: (open: boolean) => void }) { +export function RequestAuditDetailDialog({ + audit, + open, + onOpenChange, +}: { + audit: AuditDTO | null; + open: boolean; + onOpenChange: (open: boolean) => void; +}) { const { t, i18n } = useTranslation(); - const [selectedNumber, setSelectedNumber] = useState(null); const detailQuery = useQuery({ queryKey: ["request-audits", "detail", audit?.id], queryFn: ({ signal }) => getRequestAudit(audit?.id ?? "", signal), @@ -33,52 +55,421 @@ export function RequestAuditDetailDialog({ audit, open, onOpenChange }: { audit: gcTime: AUDIT_DETAIL_CACHE_TIME_MS, }); + const activeAudit = detailQuery.data?.audit ?? audit; const attempts = detailQuery.data?.attempts ?? []; - const selectedAttempt = attempts.find((attempt) => attempt.number === selectedNumber) ?? attempts[0]; return ( - - - {t("audits.detailTitle")} - - {audit?.requestId} - {audit?.clientIp ? {t("audits.clientIp")}: {audit.clientIp} : null} - {audit?.reasoningEffort ? {t("audits.reasoningEffort")}: {audit.reasoningEffort} : null} - {audit ? {formatDateTime(audit.createdAt, i18n.language)} : null} + + +
+ {t("audits.detailTitle")} + {activeAudit ? ( + = 400} + /> + ) : null} +
+ + + {activeAudit?.requestId} + + {activeAudit?.clientIp ? ( + + + {activeAudit.clientIp} + + ) : null} + {activeAudit?.operation ? ( + + {activeAudit.operation} + + ) : null} + {activeAudit ? ( + + {formatDateTime(activeAudit.createdAt, i18n.language)} + + ) : null}
- {detailQuery.isPending ? : null} - {detailQuery.isError ? void detailQuery.refetch()} /> : null} - {detailQuery.data ? ( - attempts.length > 0 && selectedAttempt ? ( -
- - + {detailQuery.isPending && !activeAudit ? : null} + {detailQuery.isError ? ( + void detailQuery.refetch()} /> + ) : null} + + {activeAudit ? ( + +
+ + + + {t("audits.requestOverview")} + + + + {t("audits.clientRequestBody")} + + + + {t("audits.upstreamDiagnostics")} + {attempts.length > 0 ? ( + + {attempts.length} + + ) : null} + +
+ + + + + + + + + + + + +
+ ) : null} + +
+ ); +} + +function RequestOverviewPanel({ audit }: { audit: AuditDTO }) { + const { t, i18n } = useTranslation(); + + const tokenSummary = useMemo(() => { + if (!audit.totalTokens && !audit.inputTokens && !audit.outputTokens) return null; + const parts = [ + `${t("audits.input")} ${formatNumber(audit.inputTokens, i18n.language)}`, + ]; + if (audit.cachedInputTokens > 0) { + parts.push(`(${t("audits.cached")} ${formatNumber(audit.cachedInputTokens, i18n.language)})`); + } + parts.push(`· ${t("audits.output")} ${formatNumber(audit.outputTokens, i18n.language)}`); + if (audit.reasoningTokens > 0) { + parts.push(`(${t("audits.reasoning")} ${formatNumber(audit.reasoningTokens, i18n.language)})`); + } + parts.push(`· ${t("audits.total")} ${formatNumber(audit.totalTokens, i18n.language)}`); + return parts.join(" "); + }, [audit, t, i18n.language]); + + const costDisplay = useMemo(() => { + const costTicks = audit.costInUsdTicks > 0 ? audit.costInUsdTicks : audit.estimatedCostInUsdTicks; + if (!costTicks) return "$0"; + const usd = (costTicks / 100_000_000).toFixed(6); + return `$${usd}${audit.costInUsdTicks <= 0 && audit.estimatedCostInUsdTicks > 0 ? " (估算)" : ""}`; + }, [audit]); + + const durationDisplay = useMemo(() => { + let text = `${formatNumber(audit.durationMs, i18n.language)} ms`; + if (audit.firstTokenMs) { + text += ` (${t("audits.firstTokenMs")}: ${formatNumber(audit.firstTokenMs, i18n.language)} ms)`; + } + return text; + }, [audit, t, i18n.language]); + + return ( +
+ + + + + + + + + {audit.errorCode ? ( + + ) : null} + {tokenSummary ? ( + + ) : null} + {audit.mediaInputImages > 0 || audit.mediaOutputImages > 0 || audit.mediaOutputSeconds > 0 ? ( + 0 ? `${t("audits.mediaInput")}: ${audit.mediaInputImages} 张` : "", + audit.mediaOutputImages > 0 ? `${t("audits.mediaOutput")}: ${audit.mediaOutputImages} 张` : "", + audit.mediaOutputSeconds > 0 ? `${audit.mediaOutputSeconds} 秒` : "", + ].filter(Boolean).join(" · ")} + /> + ) : null} +
+ ); +} + +function ClientRequestBodyPanel({ audit }: { audit: AuditDTO }) { + const { t } = useTranslation(); + const [activeView, setActiveView] = useState<"raw_http" | "body" | "headers">("raw_http"); + const [copiedType, setCopiedType] = useState(null); + + const rawBody = audit.requestBody ?? ""; + const method = audit.requestMethod || "POST"; + const path = audit.requestPath || ""; + const headers = audit.requestHeaders ?? {}; + + const { formattedJson, headerText, rawHttpText, markdownHttpBlock, markdownJsonBlock, byteSize } = useMemo(() => { + let formatted = rawBody; + if (rawBody) { + try { + const parsed = JSON.parse(rawBody); + formatted = JSON.stringify(parsed, null, 2); + } catch { + formatted = rawBody; + } + } + + const headerLines = Object.entries(headers) + .map(([key, values]) => `${key}: ${values.join(", ")}`) + .join("\n"); + + const requestLine = path ? `${method} ${path} HTTP/1.1` : ""; + const rawHttp = [requestLine, headerLines, "", formatted] + .filter((segment, idx) => idx === 2 ? true : Boolean(segment)) + .join("\n"); + + const mdHttp = "```http\n" + rawHttp + "\n```"; + const mdJson = formatted ? "```json\n" + formatted + "\n```" : ""; + const size = new Blob([rawBody]).size; + + return { + formattedJson: formatted, + headerText: headerLines, + rawHttpText: rawHttp, + markdownHttpBlock: mdHttp, + markdownJsonBlock: mdJson, + byteSize: size, + }; + }, [rawBody, method, path, headers]); + + async function handleCopy(type: "raw" | "json" | "markdown_http" | "markdown_json" | "headers") { + let textToCopy = rawHttpText; + if (type === "json") textToCopy = formattedJson; + else if (type === "markdown_http") textToCopy = markdownHttpBlock; + else if (type === "markdown_json") textToCopy = markdownJsonBlock; + else if (type === "headers") textToCopy = headerText; + + const ok = await copyToClipboard(textToCopy); + if (ok) { + setCopiedType(type); + setTimeout(() => setCopiedType(null), 1800); + toast.success(t("common.copied")); + } else { + toast.error(t("common.copyFailed")); + } + } + + if (!rawBody && Object.keys(headers).length === 0 && !path) { + return } message={t("audits.noRequestBody")} />; + } + + return ( +
+
+
+ {method && path ? ( + + {method} {path} + + ) : null} + {byteSize > 0 ? ( + + {formatByteSize(byteSize)} + + ) : null} + {Object.keys(headers).length > 0 ? ( + + {Object.keys(headers).length} headers + + ) : null} +
+ +
+
+ + + +
+ + + +
+
+ +
+ {activeView === "raw_http" ? ( +
+            {rawHttpText}
+          
+ ) : activeView === "body" ? ( + formattedJson ? ( +
+              {formattedJson}
+            
) : ( -
- -

{t(detailQuery.data.audit.errorCode && PRE_UPSTREAM_ERROR_CODES.has(detailQuery.data.audit.errorCode) ? "audits.noUpstreamAttempt" : "audits.noFailureAttempts")}

- {detailQuery.data.audit.errorCode ? {detailQuery.data.audit.errorCode} : null} +

{t("audits.noRequestBody")}

+ ) + ) : ( + Object.keys(headers).length > 0 ? ( +
+ {Object.entries(headers).map(([key, values]) => ( +
+ {key}: + {values.join(", ")} +
+ ))}
+ ) : ( +

{t("audits.noRequestHeaders")}

) + )} +
+
+ ); +} + +function UpstreamAttemptsPanel({ + audit, + attempts, +}: { + audit: AuditDTO; + attempts: AuditAttemptDTO[]; +}) { + const { t } = useTranslation(); + const [selectedNumber, setSelectedNumber] = useState(null); + + const selectedAttempt = attempts.find((attempt) => attempt.number === selectedNumber) ?? attempts[0]; + + if (attempts.length === 0) { + const isSuccess = audit.statusCode >= 200 && audit.statusCode < 300 && !audit.errorCode; + if (isSuccess) { + return ( +
+ +

{t("audits.successNoAttempts")}

+
+ ); + } + return ( +
+ +

+ {t( + audit.errorCode && PRE_UPSTREAM_ERROR_CODES.has(audit.errorCode) + ? "audits.noUpstreamAttempt" + : "audits.noFailureAttempts" + )} +

+ {audit.errorCode ? ( + + {audit.errorCode} + ) : null} - - +
+ ); + } + + return ( +
+ + +
); } @@ -88,13 +479,21 @@ function AttemptButton({ attempt, selected, onClick }: { attempt: AuditAttemptDT return ( ); @@ -104,28 +503,28 @@ function AttemptDetail({ attempt }: { attempt: AuditAttemptDTO }) { const { t } = useTranslation(); return (
- +
- - {t("audits.overview")} - {t("audits.responseBody")} - {t("audits.responseHeaders")} - {t("audits.errorChain")} + + {t("audits.overview")} + {t("audits.responseBody")} + {t("audits.responseHeaders")} + {t("audits.errorChain")}
- + - + - +
@@ -136,7 +535,15 @@ function AttemptDetail({ attempt }: { attempt: AuditAttemptDTO }) { function AttemptResponseBody({ attempt }: { attempt: AuditAttemptDTO }) { const { t } = useTranslation(); const displayValue = useMemo(() => formattedResponseBody(attempt), [attempt]); - return ; + return ( + + ); } function AttemptSummary({ attempt }: { attempt: AuditAttemptDTO }) { @@ -148,11 +555,13 @@ function AttemptSummary({ attempt }: { attempt: AuditAttemptDTO }) { ? t("audits.upstreamStreamFailure", { status: attempt.upstreamStatusCode ?? "-" }) : isHTTP ? t("audits.upstreamHttpFailure", { status: attempt.upstreamStatusCode ?? "-" }) - : attempt.source === "gateway_transport" ? t("audits.gatewayTransportFailure") : t("audits.credentialFailure"); + : attempt.source === "gateway_transport" + ? t("audits.gatewayTransportFailure") + : t("audits.credentialFailure"); return (
-

{title}

+

{title}

); } @@ -160,28 +569,37 @@ function AttemptSummary({ attempt }: { attempt: AuditAttemptDTO }) { function AttemptOverview({ attempt }: { attempt: AuditAttemptDTO }) { const { t, i18n } = useTranslation(); return ( -
+
- + - {attempt.transportError ? : null} + {attempt.transportError ? ( + + ) : null}
); } function OverviewField({ className, label, value, copy }: { className?: string; label: string; value: string; copy?: boolean }) { return ( -
+
-

{label}

-

{value}

+

{label}

+

+ {value} +

{copy ? ( -
+
) : null} @@ -189,19 +607,35 @@ function OverviewField({ className, label, value, copy }: { className?: string; ); } -function CodePanel({ value, displayValue, emptyMessage, encoding, truncated }: { value: string; displayValue: string; emptyMessage: string; encoding: string; truncated: boolean }) { +function CodePanel({ + value, + displayValue, + emptyMessage, + encoding, + truncated, +}: { + value: string; + displayValue: string; + emptyMessage: string; + encoding: string; + truncated: boolean; +}) { const { t } = useTranslation(); if (!value) return } message={emptyMessage} />; return ( -
-
- +
+
+ {t("audits.bodyEncoding", { encoding })} - {truncated ? {t("audits.bodyTruncated")} : null} + {truncated ? {t("audits.bodyTruncated")} : null}
-
{displayValue}
+
+
+          {displayValue}
+        
+
); } @@ -210,18 +644,22 @@ function HeadersPanel({ headers }: { headers: Record }) { const { t } = useTranslation(); const entries = useMemo(() => Object.entries(headers), [headers]); const copyValue = useMemo(() => JSON.stringify(headers, null, 2), [headers]); - if (entries.length === 0) return } message={t("audits.emptyResponseHeaders")} />; + if (entries.length === 0) return } message={t("audits.emptyResponseHeaders")} />; return ( -
-
- {t("audits.headerCount", { count: entries.length })} +
+
+ {t("audits.headerCount", { count: entries.length })}
-
+
{entries.map(([name, values]) => ( -
- {name} -
{values.map((value, index) => {value})}
+
+ {name} +
+ {values.map((value, index) => ( + {value} + ))} +
))}
@@ -234,16 +672,19 @@ function ErrorChainPanel({ attempt }: { attempt: AuditAttemptDTO }) { const copyValue = useMemo(() => JSON.stringify(attempt.errorChain, null, 2), [attempt.errorChain]); if (attempt.errorChain.length === 0) return } message={t("audits.emptyErrorChain")} />; return ( -
-
- {t("audits.errorFrameCount", { count: attempt.errorChain.length })} +
+
+ {t("audits.errorFrameCount", { count: attempt.errorChain.length })}
-
    +
      {attempt.errorChain.map((frame, index) => ( -
    1. -
      #{index + 1}{frame.type}
      -

      {frame.message}

      +
    2. +
      + #{index + 1} + {frame.type} +
      +

      {frame.message}

    3. ))}
    @@ -252,7 +693,12 @@ function ErrorChainPanel({ attempt }: { attempt: AuditAttemptDTO }) { } function EmptyPanel({ icon, message }: { icon: ReactNode; message: string }) { - return
    {icon}

    {message}

    ; + return ( +
    + {icon} +

    {message}

    +
    + ); } function formattedResponseBody(attempt: AuditAttemptDTO): string { @@ -266,12 +712,25 @@ function formattedResponseBody(attempt: AuditAttemptDTO): string { } } +function formatByteSize(bytes: number): string { + if (bytes < 1024) return `${bytes} B`; + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; + return `${(bytes / (1024 * 1024)).toFixed(2)} MB`; +} + function StatusBadge({ statusCode, failed = false }: { statusCode: number; failed?: boolean }) { const className = failed - ? "bg-amber-500/10 text-amber-700 dark:text-amber-300" + ? "bg-amber-500/10 text-amber-700 dark:text-amber-300 border-amber-500/30" : statusCode >= 500 - ? "bg-red-500/10 text-red-700 dark:text-red-300" - : statusCode >= 400 ? "bg-amber-500/10 text-amber-700 dark:text-amber-300" - : statusCode >= 200 && statusCode < 300 ? "bg-emerald-500/10 text-emerald-700 dark:text-emerald-300" : "bg-muted text-muted-foreground"; - return {statusCode}; + ? "bg-red-500/10 text-red-700 dark:text-red-300 border-red-500/30" + : statusCode >= 400 + ? "bg-amber-500/10 text-amber-700 dark:text-amber-300 border-amber-500/30" + : statusCode >= 200 && statusCode < 300 + ? "bg-emerald-500/10 text-emerald-700 dark:text-emerald-300 border-emerald-500/30" + : "bg-muted text-muted-foreground"; + return ( + + {statusCode} + + ); } diff --git a/frontend/src/features/audits/request-audits-api.ts b/frontend/src/features/audits/request-audits-api.ts index 4d510bf05..91f2fdee6 100644 --- a/frontend/src/features/audits/request-audits-api.ts +++ b/frontend/src/features/audits/request-audits-api.ts @@ -66,6 +66,10 @@ export type AuditDTO = { outputTokensPerSecond?: number; durationMs: number; errorCode?: string; + requestMethod?: string; + requestPath?: string; + requestHeaders?: Record; + requestBody?: string; attemptCount: number; createdAt: string; }; @@ -154,7 +158,10 @@ const auditValidator = hasShape({ costInUsdTicks: isNumber, estimatedCostInUsdTicks: isNumber, pricingModel: isOptional(isString), pricingVersion: isOptional(isString), billing: isOptional(auditBillingValidator), numSourcesUsed: isNumber, numServerSideToolsUsed: isNumber, contextInputTokens: isNumber, contextOutputTokens: isNumber, firstTokenMs: isOptional(isNumber), outputTokensPerSecond: isOptional(isNumber), - durationMs: isNumber, errorCode: isOptional(isString), attemptCount: isNumber, createdAt: isString, + durationMs: isNumber, errorCode: isOptional(isString), + requestMethod: isOptional(isString), requestPath: isOptional(isString), + requestHeaders: isOptional(isRecordOf(isArrayOf(isString))), + requestBody: isOptional(isString), attemptCount: isNumber, createdAt: isString, }); const auditAttemptValidator = hasShape({ id: isString, number: isNumber, source: isOneOf("upstream_http", "gateway_transport", "credential"), stage: isString, diff --git a/frontend/src/features/audits/request-audits-page.tsx b/frontend/src/features/audits/request-audits-page.tsx index 7988ebf3c..b7ae6e840 100644 --- a/frontend/src/features/audits/request-audits-page.tsx +++ b/frontend/src/features/audits/request-audits-page.tsx @@ -705,14 +705,20 @@ function AuditStatus({ audit, onOpen }: { audit: AuditDTO; onOpen: () => void }) {mode} ); - if (!audit.errorCode && audit.attemptCount === 0) return
    {content}
    ; return ( - + - {audit.errorCode || t("audits.openDiagnostics")} + {audit.errorCode || t("audits.viewDetails")} ); diff --git a/frontend/src/shared/i18n/index.ts b/frontend/src/shared/i18n/index.ts index 8dea5ba8f..96ab13122 100644 --- a/frontend/src/shared/i18n/index.ts +++ b/frontend/src/shared/i18n/index.ts @@ -942,7 +942,31 @@ const resources = { sources: "来源 {{count}}", sourcesLabel: "检索来源", serverTools: "工具调用 {{count}} 次", - detailTitle: "请求诊断详情", + detailTitle: "请求诊断与报文详情", + requestOverview: "请求概览", + clientRequestBody: "完整报文", + clientRequest: "完整报文", + requestHeaders: "HTTP 请求头", + requestBody: "请求体 (Body)", + requestLine: "请求行", + upstreamDiagnostics: "上游诊断", + copyJson: "复制 JSON", + copyBodyJson: "复制 Body JSON", + copyHeaders: "复制请求头", + copyRawHttp: "复制完整 HTTP 报文", + copyMarkdown: "复制 Markdown", + copyMarkdownCode: "复制 Markdown 代码块", + noRequestBody: "未捕获到客户端请求报文。", + noRequestHeaders: "未记录到 HTTP 请求头。", + requestBodySize: "报文大小:{{size}}", + successNoAttempts: "该请求已成功完成,无上游失败诊断记录。", + requestModel: "请求模型", + upstreamModel: "上游模型", + targetAccount: "目标账号", + clientApiKey: "客户端 Key", + egressNode: "出口节点", + tokenUsage: "Token 消耗", + firstTokenMs: "首字耗时", failedAttemptCount: "{{count}} 次失败", attemptTimeline: "失败尝试", noFailureAttempts: "该请求没有可用的失败尝试诊断。", @@ -1695,7 +1719,31 @@ const resources = { sources: "Sources {{count}}", sourcesLabel: "Search sources", serverTools: "Tool calls {{count}} times", - detailTitle: "Request diagnostics", + detailTitle: "Request Diagnostics & Payload", + requestOverview: "Overview", + clientRequestBody: "Full Request", + clientRequest: "Full Request", + requestHeaders: "HTTP Request Headers", + requestBody: "Request Body", + requestLine: "Request Line", + upstreamDiagnostics: "Upstream Diagnostics", + copyJson: "Copy JSON", + copyBodyJson: "Copy Body JSON", + copyHeaders: "Copy Headers", + copyRawHttp: "Copy Raw HTTP", + copyMarkdown: "Copy Markdown", + copyMarkdownCode: "Copy Markdown Block", + noRequestBody: "No client request body captured.", + noRequestHeaders: "No HTTP request headers captured.", + requestBodySize: "Payload size: {{size}}", + successNoAttempts: "This request completed successfully with no failed upstream attempts.", + requestModel: "Model", + upstreamModel: "Upstream Model", + targetAccount: "Account", + clientApiKey: "Client Key", + egressNode: "Egress Node", + tokenUsage: "Token Usage", + firstTokenMs: "First Token", failedAttemptCount: "{{count}} failed", failedAttemptCount_one: "{{count}} failed attempt", failedAttemptCount_other: "{{count}} failed attempts", diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 893515825..1a6e681d1 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -15,6 +15,7 @@ export default defineConfig({ }, }, server: { + host: "::", port: 5173, proxy: { "/api": process.env.VITE_DEV_API_TARGET ?? "http://127.0.0.1:8000", diff --git a/start.bat b/start.bat new file mode 100644 index 000000000..6ebaf0af8 --- /dev/null +++ b/start.bat @@ -0,0 +1,48 @@ +@echo off +chcp 65001 >nul +title Grok2API 一键启动器 + +echo ======================================================= +echo Grok2API 一键启动脚本 (前后端分离) +echo ======================================================= +echo. + +:: 环境变量保障 +set "PATH=C:\Program Files\Go\bin;D:\EngSofeware\nodejs;%PATH%" +set "GOTMPDIR=%USERPROFILE%\AppData\Local\Temp" + +:: 检查 Go 环境 +where go >nul 2>nul +if %errorlevel% neq 0 ( + echo [错误] 未检测到 Go 环境,请确认是否已安装 Go。 + pause + exit /b 1 +) + +:: 检查 Node.js 环境 +where npm >nul 2>nul +if %errorlevel% neq 0 ( + echo [错误] 未检测到 Node.js / npm 环境,请确认是否已安装 Node.js。 + pause + exit /b 1 +) + +echo [1/2] 正在启动后端服务 (端口: 8000)... +start "Grok2API - 后端服务 (Port 8000)" cmd /k "cd /d "%~dp0backend" && go run ./cmd/grok2api --config ../config.yaml" + +echo [2/2] 正在启动前端服务 (端口: 5173)... +start "Grok2API - 前端服务 (Port 5173)" cmd /k "cd /d "%~dp0frontend" && npm run dev" + +echo. +echo ======================================================= +echo 服务已在后台启动! +echo ======================================================= +echo - 前端管理页面: http://localhost:5173 (或 http://[::1]:5173) +echo - 后端 API 地址: http://localhost:8000 (或 http://[::1]:8000) +echo - 管理员账号: admin +echo - 初始密码: anhuang520 +echo. +echo 提示: 请勿关闭弹出的后端与前端命令行窗口。 +echo ======================================================= +echo. +pause diff --git a/stop.bat b/stop.bat new file mode 100644 index 000000000..3f3da35f9 --- /dev/null +++ b/stop.bat @@ -0,0 +1,22 @@ +@echo off +chcp 65001 >nul +title Grok2API 一键停止 + +echo ======================================================= +echo Grok2API 一键停止脚本 +echo ======================================================= +echo. + +echo 正在停止占用 8000 端口 (后端) 的进程... +for /f "tokens=5" %%a in ('netstat -aon ^| findstr ":8000" ^| findstr "LISTENING"') do ( + taskkill /F /PID %%a >nul 2>nul +) + +echo 正在停止占用 5173 端口 (前端) 的进程... +for /f "tokens=5" %%a in ('netstat -aon ^| findstr ":5173" ^| findstr "LISTENING"') do ( + taskkill /F /PID %%a >nul 2>nul +) + +echo 停止完成! +echo ======================================================= +timeout /t 3 >nul From 86010fc55c7d5b6913844fb493c384ff0f7bbf2e Mon Sep 17 00:00:00 2001 From: Jeik Date: Thu, 20 Aug 2026 10:36:07 +0800 Subject: [PATCH 2/4] feat(audit): add response body recording, stream reassembly, and audit log retention settings --- backend/internal/app/application.go | 11 + backend/internal/application/audit/service.go | 10 + backend/internal/application/gateway/image.go | 12 +- .../internal/application/gateway/service.go | 33 ++- backend/internal/application/gateway/voice.go | 12 +- .../internal/application/settings/service.go | 34 ++- backend/internal/domain/audit/audit.go | 1 + backend/internal/domain/settings/settings.go | 11 +- backend/internal/infra/config/config.go | 4 + .../relational/audit_repository.go | 16 ++ .../infra/persistence/relational/mapping.go | 2 +- .../infra/persistence/relational/models.go | 1 + backend/internal/repository/audit.go | 1 + .../internal/transport/http/audit/handler.go | 2 + .../transport/http/inference/handler.go | 236 +++++++++++++++++- .../transport/http/settings/handler.go | 30 ++- .../audits/request-audit-detail-dialog.tsx | 115 +++++++++ .../src/features/audits/request-audits-api.ts | 3 +- .../src/features/settings/settings-api.ts | 10 +- .../src/features/settings/settings-model.ts | 30 ++- .../src/features/settings/settings-page.tsx | 133 +++++++++- frontend/src/shared/i18n/index.ts | 37 ++- 22 files changed, 694 insertions(+), 50 deletions(-) diff --git a/backend/internal/app/application.go b/backend/internal/app/application.go index 7f648c53a..c37c03137 100644 --- a/backend/internal/app/application.go +++ b/backend/internal/app/application.go @@ -591,6 +591,17 @@ func (a *Application) Run(ctx context.Context) error { }) return nil }) + startBackground("audit_retention_cleanup", func(taskCtx context.Context) error { + a.runPeriodicTask(taskCtx, time.Hour, "audit_retention_cleanup", func(runCtx context.Context) error { + retentionDays := a.settings.Get().Config.Audit.RetentionDays + if retentionDays <= 0 { + retentionDays = 7 + } + _, err := a.audits.PurgeOutdated(runCtx, retentionDays) + return err + }) + return nil + }) startBackground("quota_recovery", func(taskCtx context.Context) error { a.quotaRecovery.Run(taskCtx) return nil diff --git a/backend/internal/application/audit/service.go b/backend/internal/application/audit/service.go index 4051fc13e..81152301d 100644 --- a/backend/internal/application/audit/service.go +++ b/backend/internal/application/audit/service.go @@ -405,6 +405,16 @@ func (s *Service) Get(ctx context.Context, id uint64) (auditdomain.Record, error return s.audits.Get(ctx, id) } +// PurgeOutdated 清理超过指定保留天数的历史审计记录。 +func (s *Service) PurgeOutdated(ctx context.Context, retentionDays int) (int64, error) { + if retentionDays <= 0 { + return 0, nil + } + cutoff := time.Now().UTC().AddDate(0, 0, -retentionDays) + return s.audits.PurgeOlderThan(ctx, cutoff) +} + + // CursorResult 表示按递减 ID 游标读取的一页审计记录。 type CursorResult struct { Items []auditdomain.Record diff --git a/backend/internal/application/gateway/image.go b/backend/internal/application/gateway/image.go index 45d00e987..005da5450 100644 --- a/backend/internal/application/gateway/image.go +++ b/backend/internal/application/gateway/image.go @@ -303,7 +303,7 @@ func (s *Service) executeImage( effectiveQuotaMode := lease.QuotaMode accountID := credential.ID var once sync.Once - finalize := func(_ Usage, _ string, errorCode string) { + finalizeWithBody := func(_ Usage, _ string, errorCode string, responseBody string) { once.Do(func() { successful := auditRequestSucceeded(response.StatusCode, errorCode) lease.completeSelectorObservation(successful) @@ -312,6 +312,7 @@ func (s *Service) executeImage( record := auditBase record.AccountID, record.AccountName, record.StatusCode = &accountID, credential.Name, response.StatusCode record.ErrorCode = errorCode + record.ResponseBody = responseBody record.DurationMS, record.CreatedAt = time.Since(startedAt).Milliseconds(), time.Now().UTC() applyAuditEgress(&record, egressTrace, route.Provider) if successful { @@ -356,8 +357,15 @@ func (s *Service) executeImage( } }) } + finalize := func(usage Usage, responseID string, errorCode string) { + finalizeWithBody(usage, responseID, errorCode, "") + } finalizationOwnsReservation = true - return &Result{StatusCode: response.StatusCode, Status: response.Status, Header: response.Header, Body: &finalizingBody{ReadCloser: response.Body, finalize: func() { finalize(Usage{}, "", "stream_closed") }}, Finalize: finalize}, nil + return &Result{ + StatusCode: response.StatusCode, Status: response.Status, Header: response.Header, + Body: &finalizingBody{ReadCloser: response.Body, finalize: func() { finalize(Usage{}, "", "stream_closed") }}, + Finalize: finalize, FinalizeWithBody: finalizeWithBody, + }, nil } // quotaFinalizationModes separates the immediate local consumption fence from diff --git a/backend/internal/application/gateway/service.go b/backend/internal/application/gateway/service.go index 1652cf74a..fd04898c1 100644 --- a/backend/internal/application/gateway/service.go +++ b/backend/internal/application/gateway/service.go @@ -154,6 +154,7 @@ type Result struct { MarkFirstToken func() RecordStreamFailure func(StreamFailureDiagnostic) Finalize func(usage Usage, responseID, errorCode string) + FinalizeWithBody func(usage Usage, responseID, errorCode, responseBody string) } // StreamFailureDiagnostic safely projects a failure termination event returned in-stream after downstream 2xx headers. @@ -1061,7 +1062,7 @@ func (s *Service) createResponseAt(ctx context.Context, input Input, path string handoffResponse := func(response *provider.Response, lease *accountLease, credential accountdomain.Credential, upstreamStartedAt time.Time) *Result { accountID := credential.ID var once sync.Once - finalize := func(usage Usage, responseID, errorCode string) { + finalizeWithBody := func(usage Usage, responseID, errorCode, responseBody string) { once.Do(func() { // HTTP 状态码保留线上真实值;流在 2xx 响应头之后失败时由 errorCode // 决定最终结果,避免把协议状态与业务结果混为一谈。 @@ -1114,7 +1115,27 @@ func (s *Service) createResponseAt(ctx context.Context, input Input, path string } record.DurationMS = time.Since(startedAt).Milliseconds() record.ErrorCode = errorCode + record.ResponseBody = responseBody attempts := failureAttempts.snapshot() + if !successful && len(attempts) == 0 { + statusCode := response.StatusCode + failureAttempts.append(audit.Attempt{ + Source: audit.AttemptSourceUpstreamHTTP, + Stage: "response_stream", + AccountID: auditAccountID(credential.ID), + AccountName: credential.Name, + Method: http.MethodPost, + RequestPath: sanitizeRequestPath(path), + UpstreamURL: sanitizeUpstreamURL(response.UpstreamURL), + StartedAt: upstreamStartedAt.UTC(), + DurationMS: time.Since(upstreamStartedAt).Milliseconds(), + UpstreamStatusCode: &statusCode, + UpstreamStatus: response.Status, + ResponseHeaders: sanitizeDiagnosticHeaders(response.Header), + TransportError: errorCode, + }) + attempts = failureAttempts.snapshot() + } if !successful || len(attempts) > 0 { record.Attempts = attempts } @@ -1166,6 +1187,9 @@ func (s *Service) createResponseAt(ctx context.Context, input Input, path string timing.finish(s.logger, outcome) }) } + finalize := func(usage Usage, responseID, errorCode string) { + finalizeWithBody(usage, responseID, errorCode, "") + } response.Body = &firstByteReadCloser{ReadCloser: response.Body, mark: timing.markFirstBody} recordStreamFailure := func(diagnostic StreamFailureDiagnostic) { failureAttempts.captureStreamFailure(credential, upstreamStartedAt, response, diagnostic) @@ -1175,7 +1199,12 @@ func (s *Service) createResponseAt(ctx context.Context, input Input, path string markFirstToken = firstToken.mark } timingHandedOff = true - return &Result{StatusCode: response.StatusCode, Status: response.Status, Header: response.Header, Body: &finalizingBody{ReadCloser: response.Body, finalize: func() { finalize(Usage{}, "", "stream_closed") }}, MarkFirstToken: markFirstToken, RecordStreamFailure: recordStreamFailure, Finalize: finalize} + return &Result{ + StatusCode: response.StatusCode, Status: response.Status, Header: response.Header, + Body: &finalizingBody{ReadCloser: response.Body, finalize: func() { finalize(Usage{}, "", "stream_closed") }}, + MarkFirstToken: markFirstToken, RecordStreamFailure: recordStreamFailure, + Finalize: finalize, FinalizeWithBody: finalizeWithBody, + } } // fail_open retains at most one successful no-thinking stream. The account // lease is released immediately; the read pump applies upstream backpressure diff --git a/backend/internal/application/gateway/voice.go b/backend/internal/application/gateway/voice.go index bc5f29273..75aed96e0 100644 --- a/backend/internal/application/gateway/voice.go +++ b/backend/internal/application/gateway/voice.go @@ -475,7 +475,7 @@ func (s *Service) executeVoice( } accountID := credential.ID var once sync.Once - finalize := func(_ Usage, _ string, errorCode string) { + finalizeWithBody := func(_ Usage, _ string, errorCode string, responseBody string) { once.Do(func() { successful := auditRequestSucceeded(response.StatusCode, errorCode) lease.completeSelectorObservation(successful) @@ -484,6 +484,7 @@ func (s *Service) executeVoice( record := auditBase record.AccountID, record.AccountName, record.StatusCode = &accountID, credential.Name, response.StatusCode record.ErrorCode = errorCode + record.ResponseBody = responseBody record.DurationMS, record.CreatedAt = time.Since(startedAt).Milliseconds(), time.Now().UTC() applyAuditEgress(&record, egressTrace, route.Provider) if successful && completedPricing.CostInUSDTicks > 0 { @@ -517,7 +518,14 @@ func (s *Service) executeVoice( } }) } - return &Result{StatusCode: response.StatusCode, Status: response.Status, Header: response.Header, Body: &finalizingBody{ReadCloser: response.Body, finalize: func() { finalize(Usage{}, "", "stream_closed") }}, Finalize: finalize}, nil + finalize := func(usage Usage, responseID string, errorCode string) { + finalizeWithBody(usage, responseID, errorCode, "") + } + return &Result{ + StatusCode: response.StatusCode, Status: response.Status, Header: response.Header, + Body: &finalizingBody{ReadCloser: response.Body, finalize: func() { finalize(Usage{}, "", "stream_closed") }}, + Finalize: finalize, FinalizeWithBody: finalizeWithBody, + }, nil } func jsonVoiceResponse(status int, value any) *provider.Response { diff --git a/backend/internal/application/settings/service.go b/backend/internal/application/settings/service.go index 79249c6d8..8ac8fa9b9 100644 --- a/backend/internal/application/settings/service.go +++ b/backend/internal/application/settings/service.go @@ -119,10 +119,16 @@ type SegmentedSelectorConfig struct { // AuditConfig 是管理接口使用的审计可编辑输入。 type AuditConfig struct { - BufferSize int - BatchSize int - FlushInterval string - CommitDelayMS int + BufferSize int + BatchSize int + FlushInterval string + CommitDelayMS int + RetentionDays int + RetentionDaysProvided bool + RecordRequestBody bool + RecordRequestBodyProvided bool + RecordResponseBody bool + RecordResponseBodyProvided bool } // ClientKeyDefaultsConfig 是管理接口使用的密钥默认限制输入。 @@ -403,9 +409,14 @@ func applyDomainConfig(base config.Config, value settingsdomain.Config) config.C if value.Audit.CommitDelay > 0 { commitDelay = value.Audit.CommitDelay } + retentionDays := base.Audit.RetentionDays + if value.Audit.RetentionDays > 0 { + retentionDays = value.Audit.RetentionDays + } base.Audit = config.AuditConfig{ BufferSize: value.Audit.BufferSize, BatchSize: value.Audit.BatchSize, FlushInterval: config.Duration(value.Audit.FlushInterval), - CommitDelay: config.Duration(commitDelay), + CommitDelay: config.Duration(commitDelay), RetentionDays: retentionDays, + RecordRequestBody: value.Audit.RecordRequestBody, RecordResponseBody: value.Audit.RecordResponseBody, LedgerMode: base.Audit.LedgerMode, LedgerFailureThreshold: base.Audit.LedgerFailureThreshold, LedgerUnhealthyGrace: base.Audit.LedgerUnhealthyGrace, LedgerQueueHighWatermarkPct: base.Audit.LedgerQueueHighWatermarkPct, } @@ -482,6 +493,7 @@ func toDomainConfig(value config.Config) settingsdomain.Config { }, Audit: settingsdomain.AuditConfig{ BufferSize: value.Audit.BufferSize, BatchSize: value.Audit.BatchSize, FlushInterval: value.Audit.FlushInterval.Value(), CommitDelay: value.Audit.CommitDelay.Value(), + RetentionDays: value.Audit.RetentionDays, RecordRequestBody: value.Audit.RecordRequestBody, RecordResponseBody: value.Audit.RecordResponseBody, }, ClientKeyDefaults: settingsdomain.ClientKeyDefaultsConfig{ RPMLimit: value.ClientKeyDefaults.RPMLimit, MaxConcurrent: value.ClientKeyDefaults.MaxConcurrent, @@ -574,6 +586,15 @@ func mergeEditable(current config.Config, input EditableConfig) (config.Config, if input.Audit.CommitDelayMS > 0 { next.Audit.CommitDelay = config.Duration(time.Duration(input.Audit.CommitDelayMS) * time.Millisecond) } + if input.Audit.RetentionDaysProvided { + next.Audit.RetentionDays = input.Audit.RetentionDays + } + if input.Audit.RecordRequestBodyProvided { + next.Audit.RecordRequestBody = input.Audit.RecordRequestBody + } + if input.Audit.RecordResponseBodyProvided { + next.Audit.RecordResponseBody = input.Audit.RecordResponseBody + } next.ClientKeyDefaults.RPMLimit = input.ClientKeyDefaults.RPMLimit next.ClientKeyDefaults.MaxConcurrent = input.ClientKeyDefaults.MaxConcurrent if input.AccountsProvided { @@ -711,6 +732,9 @@ func toEditable(cfg config.Config) EditableConfig { }, Audit: AuditConfig{ BufferSize: cfg.Audit.BufferSize, BatchSize: cfg.Audit.BatchSize, FlushInterval: cfg.Audit.FlushInterval.String(), CommitDelayMS: int(cfg.Audit.CommitDelay.Value() / time.Millisecond), + RetentionDays: cfg.Audit.RetentionDays, RetentionDaysProvided: true, + RecordRequestBody: cfg.Audit.RecordRequestBody, RecordRequestBodyProvided: true, + RecordResponseBody: cfg.Audit.RecordResponseBody, RecordResponseBodyProvided: true, }, ClientKeyDefaults: ClientKeyDefaultsConfig{RPMLimit: cfg.ClientKeyDefaults.RPMLimit, MaxConcurrent: cfg.ClientKeyDefaults.MaxConcurrent}, Accounts: AccountsConfig{ diff --git a/backend/internal/domain/audit/audit.go b/backend/internal/domain/audit/audit.go index ee850a2a3..3c17f39ba 100644 --- a/backend/internal/domain/audit/audit.go +++ b/backend/internal/domain/audit/audit.go @@ -115,6 +115,7 @@ type Record struct { RequestPath string RequestHeaders map[string][]string RequestBody string + ResponseBody string AttemptCount int Attempts []Attempt CreatedAt time.Time diff --git a/backend/internal/domain/settings/settings.go b/backend/internal/domain/settings/settings.go index 2a00befc0..b699ea07c 100644 --- a/backend/internal/domain/settings/settings.go +++ b/backend/internal/domain/settings/settings.go @@ -120,10 +120,13 @@ type SegmentedSelectorConfig struct { } type AuditConfig struct { - BufferSize int - BatchSize int - FlushInterval time.Duration - CommitDelay time.Duration + BufferSize int + BatchSize int + FlushInterval time.Duration + CommitDelay time.Duration + RetentionDays int + RecordRequestBody bool + RecordResponseBody bool } // ClientKeyDefaultsConfig 定义新建客户端密钥的默认限制。 diff --git a/backend/internal/infra/config/config.go b/backend/internal/infra/config/config.go index da19074f3..e5f2265e1 100644 --- a/backend/internal/infra/config/config.go +++ b/backend/internal/infra/config/config.go @@ -248,6 +248,9 @@ type AuditConfig struct { BatchSize int `yaml:"batchSize"` FlushInterval Duration `yaml:"flushInterval"` CommitDelay Duration `yaml:"commitDelay"` + RetentionDays int `yaml:"retentionDays"` + RecordRequestBody bool `yaml:"recordRequestBody"` + RecordResponseBody bool `yaml:"recordResponseBody"` LedgerMode string `yaml:"ledgerMode"` LedgerFailureThreshold int `yaml:"ledgerFailureThreshold"` LedgerUnhealthyGrace Duration `yaml:"ledgerUnhealthyGrace"` @@ -920,6 +923,7 @@ func defaultConfig() Config { }, Audit: AuditConfig{ BufferSize: 16384, BatchSize: 256, FlushInterval: Duration(250 * time.Millisecond), CommitDelay: Duration(5 * time.Millisecond), + RetentionDays: 7, RecordRequestBody: true, RecordResponseBody: true, LedgerMode: "enforce", LedgerFailureThreshold: 1, LedgerUnhealthyGrace: Duration(10 * time.Second), LedgerQueueHighWatermarkPct: 90, }, diff --git a/backend/internal/infra/persistence/relational/audit_repository.go b/backend/internal/infra/persistence/relational/audit_repository.go index c73393900..3025d282e 100644 --- a/backend/internal/infra/persistence/relational/audit_repository.go +++ b/backend/internal/infra/persistence/relational/audit_repository.go @@ -376,6 +376,7 @@ func toAuditModels(value audit.Record) (requestAuditModel, []requestAuditAttempt RequestPath: truncate(value.RequestPath, 2048), RequestHeadersJSON: truncate(requestHeadersJSON, 65536), RequestBody: truncate(value.RequestBody, 16777216), + ResponseBody: truncate(value.ResponseBody, 16777216), AttemptCount: len(value.Attempts), CreatedAt: value.CreatedAt, } attempts := make([]requestAuditAttemptModel, 0, len(value.Attempts)) @@ -1058,3 +1059,18 @@ func applyAuditQuery(query *gorm.DB, search string, start, end time.Time, filter } return query } + +func (r *AuditRepository) PurgeOlderThan(ctx context.Context, cutoff time.Time) (int64, error) { + var totalDeleted int64 + err := r.db.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + _ = tx.Exec("DELETE FROM request_audit_attempts WHERE audit_id IN (SELECT id FROM request_audits WHERE created_at < ?)", cutoff).Error + res := tx.Where("created_at < ?", cutoff).Delete(&requestAuditModel{}) + if res.Error != nil { + return res.Error + } + totalDeleted = res.RowsAffected + return nil + }) + return totalDeleted, err +} + diff --git a/backend/internal/infra/persistence/relational/mapping.go b/backend/internal/infra/persistence/relational/mapping.go index e33e8e655..0f0fc3761 100644 --- a/backend/internal/infra/persistence/relational/mapping.go +++ b/backend/internal/infra/persistence/relational/mapping.go @@ -270,7 +270,7 @@ func toAuditDomain(value requestAuditModel) audit.Record { EstimatedCostInUSDTicks: value.EstimatedCostInUSDTicks, PricingModel: value.PricingModel, PricingVersion: value.PricingVersion, NumSourcesUsed: value.NumSourcesUsed, NumServerSideToolsUsed: value.NumServerSideToolsUsed, ContextInputTokens: value.ContextInputTokens, ContextOutputTokens: value.ContextOutputTokens, FirstTokenMS: value.FirstTokenMS, DurationMS: value.DurationMS, - ErrorCode: value.ErrorCode, RequestMethod: value.RequestMethod, RequestPath: value.RequestPath, RequestHeaders: requestHeaders, RequestBody: value.RequestBody, AttemptCount: value.AttemptCount, CreatedAt: value.CreatedAt, + ErrorCode: value.ErrorCode, RequestMethod: value.RequestMethod, RequestPath: value.RequestPath, RequestHeaders: requestHeaders, RequestBody: value.RequestBody, ResponseBody: value.ResponseBody, AttemptCount: value.AttemptCount, CreatedAt: value.CreatedAt, } } diff --git a/backend/internal/infra/persistence/relational/models.go b/backend/internal/infra/persistence/relational/models.go index 90f402ba5..4c2756282 100644 --- a/backend/internal/infra/persistence/relational/models.go +++ b/backend/internal/infra/persistence/relational/models.go @@ -333,6 +333,7 @@ type requestAuditModel struct { RequestPath string `gorm:"type:text;not null;default:'';check:chk_request_audits_request_path,length(request_path) <= 2048"` RequestHeadersJSON string `gorm:"type:text;not null;default:'{}';check:chk_request_audits_request_headers,length(request_headers_json) <= 65536"` RequestBody string `gorm:"type:text;not null;default:'';check:chk_request_audits_request_body,length(request_body) <= 16777216"` + ResponseBody string `gorm:"type:text;not null;default:'';check:chk_request_audits_response_body,length(response_body) <= 16777216"` AttemptCount int `gorm:"not null;default:0;check:chk_request_audits_attempt_count,attempt_count >= 0"` CreatedAt time.Time `gorm:"not null"` } diff --git a/backend/internal/repository/audit.go b/backend/internal/repository/audit.go index d8e7bceda..65e406af4 100644 --- a/backend/internal/repository/audit.go +++ b/backend/internal/repository/audit.go @@ -17,6 +17,7 @@ type AuditRepository interface { Summarize(ctx context.Context, query AuditSummaryQuery) (audit.Summary, error) SumTokensByAccountsSince(ctx context.Context, accountIDs []uint64, since time.Time) (map[uint64]int64, error) SummarizeDegrade(ctx context.Context, query DegradeSummaryQuery) (DegradeSummaryResult, error) + PurgeOlderThan(ctx context.Context, cutoff time.Time) (int64, error) } type DegradeSummaryQuery struct { diff --git a/backend/internal/transport/http/audit/handler.go b/backend/internal/transport/http/audit/handler.go index 18fd2f624..7f6ff739e 100644 --- a/backend/internal/transport/http/audit/handler.go +++ b/backend/internal/transport/http/audit/handler.go @@ -129,6 +129,7 @@ type auditResponse struct { RequestPath string `json:"requestPath,omitempty"` RequestHeaders map[string][]string `json:"requestHeaders,omitempty"` RequestBody string `json:"requestBody,omitempty"` + ResponseBody string `json:"responseBody,omitempty"` AttemptCount int `json:"attemptCount"` CreatedAt time.Time `json:"createdAt"` } @@ -498,6 +499,7 @@ func newAuditResponse(value auditdomain.Record) auditResponse { RequestPath: value.RequestPath, RequestHeaders: value.RequestHeaders, RequestBody: value.RequestBody, + ResponseBody: value.ResponseBody, AttemptCount: value.AttemptCount, CreatedAt: value.CreatedAt, } diff --git a/backend/internal/transport/http/inference/handler.go b/backend/internal/transport/http/inference/handler.go index d45081452..765c62a33 100644 --- a/backend/internal/transport/http/inference/handler.go +++ b/backend/internal/transport/http/inference/handler.go @@ -1243,8 +1243,15 @@ func (h *Handler) writeProtocolResult(c *gin.Context, result *gateway.Result, st usage := gateway.Usage{} responseID := "" errorCode := "" + responseBody := "" defer result.Body.Close() - defer func() { result.Finalize(usage, responseID, errorCode) }() + defer func() { + if result.FinalizeWithBody != nil { + result.FinalizeWithBody(usage, responseID, errorCode, responseBody) + } else { + result.Finalize(usage, responseID, errorCode) + } + }() if isUpstreamCredentialStatus(result.StatusCode) { errorCode = "upstream_unavailable" clientCode := readCredentialErrorCode(result.StatusCode, result.Body) @@ -1272,13 +1279,13 @@ func (h *Handler) writeProtocolResult(c *gin.Context, result *gateway.Result, st var err error if stream { metadata, copyErr := copyStream(c.Writer, result.Body, protocol, result.MarkFirstToken) - usage, responseID, err = metadata.Usage, metadata.ResponseID, copyErr + usage, responseID, responseBody, err = metadata.Usage, metadata.ResponseID, metadata.ResponseBody, copyErr if metadata.StreamFailure != nil && result.RecordStreamFailure != nil { result.RecordStreamFailure(*metadata.StreamFailure) } } else { metadata, copyErr := copyJSON(c.Writer, result.Body, protocol) - usage, responseID, err = metadata.Usage, metadata.ResponseID, copyErr + usage, responseID, responseBody, err = metadata.Usage, metadata.ResponseID, metadata.ResponseBody, copyErr } if err != nil { switch { @@ -1304,6 +1311,7 @@ type responseMetadata struct { ResponseID string Model string SequenceNumber int64 + ResponseBody string StreamFailure *gateway.StreamFailureDiagnostic } @@ -1546,19 +1554,38 @@ func copyJSON(writer gin.ResponseWriter, source io.Reader, protocol streamProtoc if readErr != nil { if errors.Is(readErr, io.EOF) { if metadataComplete { - return normalizeMetadataUsage(extractMetadata(metadataBody), protocol), nil + meta := normalizeMetadataUsage(extractMetadata(metadataBody), protocol) + meta.ResponseBody = string(metadataBody) + return meta, nil } - return responseMetadata{}, nil + return responseMetadata{ResponseBody: string(metadataBody)}, nil } - return responseMetadata{}, readErr + return responseMetadata{ResponseBody: string(metadataBody)}, readErr } } } +type streamAccumulator struct { + chatContent strings.Builder + chatReasoning strings.Builder + chatRefusal strings.Builder + chatFinishReason string + chatSystemFingerprint string + anthropicThinking strings.Builder + anthropicText strings.Builder + anthropicStopReason string + responsesText strings.Builder + responsesReasoning strings.Builder + rawSSE strings.Builder +} + +const maxStreamAccumulatorBytes = 524288 // 512KB + type responseInspector struct { protocol streamProtocol pending []byte metadata responseMetadata + acc streamAccumulator onFirstToken func() firstTokenSeen bool firstTokenReady bool @@ -1592,6 +1619,10 @@ func (i *responseInspector) Inspect(chunk []byte) { continue } if bytes.HasPrefix(line, []byte("data:")) { + if i.acc.rawSSE.Len() < maxStreamAccumulatorBytes { + i.acc.rawSSE.Write(line) + i.acc.rawSSE.WriteByte('\n') + } value := bytes.TrimSpace(bytes.TrimPrefix(line, []byte("data:"))) if containsGeneratedDelta(value, i.protocol) { i.metadata.Usage.OutputObserved = true @@ -1599,6 +1630,7 @@ func (i *responseInspector) Inspect(chunk []byte) { i.observeFirstToken(value) i.observeTerminal(value) if !bytes.Equal(value, []byte("[DONE]")) { + i.accumulateStreamChunk(value) metadata := extractMetadata(value) if hasUsageMetadata(metadata.Usage) { if metadata.Usage.ResponseModel == "" { @@ -1624,6 +1656,194 @@ func (i *responseInspector) Inspect(chunk []byte) { } } +func (i *responseInspector) accumulateStreamChunk(data []byte) { + switch i.protocol { + case streamProtocolChat: + var chunk struct { + ID string `json:"id"` + Model string `json:"model"` + SystemFingerprint string `json:"system_fingerprint"` + Choices []struct { + Delta struct { + Content string `json:"content"` + Reasoning string `json:"reasoning"` + ReasoningContent string `json:"reasoning_content"` + ThinkingContent string `json:"thinking_content"` + Refusal string `json:"refusal"` + } `json:"delta"` + FinishReason string `json:"finish_reason"` + } `json:"choices"` + } + if json.Unmarshal(data, &chunk) == nil { + if chunk.SystemFingerprint != "" { + i.acc.chatSystemFingerprint = chunk.SystemFingerprint + } + for _, choice := range chunk.Choices { + if choice.Delta.Content != "" && i.acc.chatContent.Len() < maxStreamAccumulatorBytes { + i.acc.chatContent.WriteString(choice.Delta.Content) + } + reasoning := choice.Delta.ReasoningContent + if reasoning == "" { + reasoning = choice.Delta.Reasoning + } + if reasoning == "" { + reasoning = choice.Delta.ThinkingContent + } + if reasoning != "" && i.acc.chatReasoning.Len() < maxStreamAccumulatorBytes { + i.acc.chatReasoning.WriteString(reasoning) + } + if choice.Delta.Refusal != "" && i.acc.chatRefusal.Len() < maxStreamAccumulatorBytes { + i.acc.chatRefusal.WriteString(choice.Delta.Refusal) + } + if choice.FinishReason != "" { + i.acc.chatFinishReason = choice.FinishReason + } + } + } + case streamProtocolAnthropic: + var evt struct { + Type string `json:"type"` + Delta struct { + Type string `json:"type"` + Text string `json:"text"` + Thinking string `json:"thinking"` + StopReason string `json:"stop_reason"` + } `json:"delta"` + } + if json.Unmarshal(data, &evt) == nil { + if evt.Delta.Type == "text_delta" && evt.Delta.Text != "" && i.acc.anthropicText.Len() < maxStreamAccumulatorBytes { + i.acc.anthropicText.WriteString(evt.Delta.Text) + } + if evt.Delta.Type == "thinking_delta" && evt.Delta.Thinking != "" && i.acc.anthropicThinking.Len() < maxStreamAccumulatorBytes { + i.acc.anthropicThinking.WriteString(evt.Delta.Thinking) + } + if evt.Delta.StopReason != "" { + i.acc.anthropicStopReason = evt.Delta.StopReason + } + } + case streamProtocolResponses: + var evt struct { + Type string `json:"type"` + Delta string `json:"delta"` + } + if json.Unmarshal(data, &evt) == nil { + if (evt.Type == "response.output_text.delta" || evt.Type == "response.text.delta") && evt.Delta != "" && i.acc.responsesText.Len() < maxStreamAccumulatorBytes { + i.acc.responsesText.WriteString(evt.Delta) + } + if (evt.Type == "response.reasoning_text.delta" || evt.Type == "response.reasoning_summary_text.delta") && evt.Delta != "" && i.acc.responsesReasoning.Len() < maxStreamAccumulatorBytes { + i.acc.responsesReasoning.WriteString(evt.Delta) + } + } + } +} + +func (i *responseInspector) buildResponseBody() string { + switch i.protocol { + case streamProtocolChat: + if i.acc.chatContent.Len() > 0 || i.acc.chatReasoning.Len() > 0 || i.acc.chatFinishReason != "" { + msg := map[string]any{ + "role": "assistant", + "content": i.acc.chatContent.String(), + } + if i.acc.chatReasoning.Len() > 0 { + msg["reasoning_content"] = i.acc.chatReasoning.String() + } + if i.acc.chatRefusal.Len() > 0 { + msg["refusal"] = i.acc.chatRefusal.String() + } + finishReason := i.acc.chatFinishReason + if finishReason == "" { + finishReason = "stop" + } + resp := map[string]any{ + "id": i.metadata.ResponseID, + "object": "chat.completion", + "created": time.Now().Unix(), + "model": i.metadata.Model, + "choices": []map[string]any{ + { + "index": 0, + "message": msg, + "finish_reason": finishReason, + }, + }, + } + if i.acc.chatSystemFingerprint != "" { + resp["system_fingerprint"] = i.acc.chatSystemFingerprint + } + if i.metadata.Usage.TotalTokens > 0 || i.metadata.Usage.InputTokens > 0 || i.metadata.Usage.OutputTokens > 0 { + resp["usage"] = map[string]any{ + "prompt_tokens": i.metadata.Usage.InputTokens, + "completion_tokens": i.metadata.Usage.OutputTokens, + "total_tokens": i.metadata.Usage.TotalTokens, + } + } + if raw, err := json.MarshalIndent(resp, "", " "); err == nil { + return string(raw) + } + } + case streamProtocolAnthropic: + if i.acc.anthropicText.Len() > 0 || i.acc.anthropicThinking.Len() > 0 { + blocks := make([]map[string]any, 0, 2) + if i.acc.anthropicThinking.Len() > 0 { + blocks = append(blocks, map[string]any{ + "type": "thinking", + "thinking": i.acc.anthropicThinking.String(), + }) + } + if i.acc.anthropicText.Len() > 0 { + blocks = append(blocks, map[string]any{ + "type": "text", + "text": i.acc.anthropicText.String(), + }) + } + stopReason := i.acc.anthropicStopReason + if stopReason == "" { + stopReason = "end_turn" + } + resp := map[string]any{ + "id": i.metadata.ResponseID, + "type": "message", + "role": "assistant", + "content": blocks, + "model": i.metadata.Model, + "stop_reason": stopReason, + } + if i.metadata.Usage.TotalTokens > 0 || i.metadata.Usage.InputTokens > 0 || i.metadata.Usage.OutputTokens > 0 { + resp["usage"] = map[string]any{ + "input_tokens": i.metadata.Usage.InputTokens, + "output_tokens": i.metadata.Usage.OutputTokens, + } + } + if raw, err := json.MarshalIndent(resp, "", " "); err == nil { + return string(raw) + } + } + case streamProtocolResponses: + if i.acc.responsesText.Len() > 0 || i.acc.responsesReasoning.Len() > 0 { + resp := map[string]any{ + "id": i.metadata.ResponseID, + "object": "response", + "model": i.metadata.Model, + "output_text": i.acc.responsesText.String(), + } + if i.acc.responsesReasoning.Len() > 0 { + resp["reasoning_text"] = i.acc.responsesReasoning.String() + } + if raw, err := json.MarshalIndent(resp, "", " "); err == nil { + return string(raw) + } + } + } + return i.acc.rawSSE.String() +} + +func (i *responseInspector) Metadata() responseMetadata { + meta := normalizeMetadataUsage(i.metadata, i.protocol) + meta.ResponseBody = i.buildResponseBody() + return meta +} + func (i *responseInspector) observeReasoningStart() { if i.firstTokenSeen || i.firstTokenReady || i.onFirstToken == nil { return @@ -1740,10 +1960,6 @@ func containsGeneratedDelta(data []byte, protocol streamProtocol) bool { return false } -func (i *responseInspector) Metadata() responseMetadata { - return normalizeMetadataUsage(i.metadata, i.protocol) -} - func normalizeMetadataUsage(metadata responseMetadata, protocol streamProtocol) responseMetadata { if protocol != streamProtocolAnthropic { return metadata diff --git a/backend/internal/transport/http/settings/handler.go b/backend/internal/transport/http/settings/handler.go index 06849104f..0f15adb7f 100644 --- a/backend/internal/transport/http/settings/handler.go +++ b/backend/internal/transport/http/settings/handler.go @@ -116,10 +116,13 @@ type segmentedSelectorConfigDTO struct { } type auditConfigDTO struct { - BufferSize int `json:"bufferSize"` - BatchSize int `json:"batchSize"` - FlushInterval string `json:"flushInterval"` - CommitDelayMS int `json:"commitDelayMS"` + BufferSize int `json:"bufferSize"` + BatchSize int `json:"batchSize"` + FlushInterval string `json:"flushInterval"` + CommitDelayMS int `json:"commitDelayMS"` + RetentionDays *int `json:"retentionDays,omitempty"` + RecordRequestBody *bool `json:"recordRequestBody,omitempty"` + RecordResponseBody *bool `json:"recordResponseBody,omitempty"` } type clientKeyDefaultsConfigDTO struct { @@ -233,6 +236,12 @@ func (value settingsConfigDTO) toApplication() settingsapp.EditableConfig { }, Audit: settingsapp.AuditConfig{ BufferSize: value.Audit.BufferSize, BatchSize: value.Audit.BatchSize, FlushInterval: value.Audit.FlushInterval, CommitDelayMS: value.Audit.CommitDelayMS, + RetentionDays: intValue(value.Audit.RetentionDays), + RetentionDaysProvided: value.Audit.RetentionDays != nil, + RecordRequestBody: boolValue(value.Audit.RecordRequestBody), + RecordRequestBodyProvided: value.Audit.RecordRequestBody != nil, + RecordResponseBody: boolValue(value.Audit.RecordResponseBody), + RecordResponseBodyProvided: value.Audit.RecordResponseBody != nil, }, ClientKeyDefaults: settingsapp.ClientKeyDefaultsConfig{ RPMLimit: value.ClientKeyDefaults.RPMLimit, MaxConcurrent: value.ClientKeyDefaults.MaxConcurrent, @@ -317,6 +326,9 @@ func newSettingsResponse(value settingsapp.Snapshot) settingsResponse { }, Audit: auditConfigDTO{ BufferSize: config.Audit.BufferSize, BatchSize: config.Audit.BatchSize, FlushInterval: config.Audit.FlushInterval, CommitDelayMS: config.Audit.CommitDelayMS, + RetentionDays: intPointer(config.Audit.RetentionDays), + RecordRequestBody: boolPointer(config.Audit.RecordRequestBody), + RecordResponseBody: boolPointer(config.Audit.RecordResponseBody), }, ClientKeyDefaults: clientKeyDefaultsConfigDTO{ RPMLimit: config.ClientKeyDefaults.RPMLimit, MaxConcurrent: config.ClientKeyDefaults.MaxConcurrent, @@ -357,6 +369,15 @@ func boolValue(value *bool) bool { return *value } +func intPointer(value int) *int { return &value } + +func intValue(value *int) int { + if value == nil { + return 0 + } + return *value +} + func stringSliceValue(value *[]string) []string { if value == nil { return nil @@ -368,3 +389,4 @@ func stringSlicePointer(value []string) *[]string { cloned := append([]string(nil), value...) return &cloned } + diff --git a/frontend/src/features/audits/request-audit-detail-dialog.tsx b/frontend/src/features/audits/request-audit-detail-dialog.tsx index 0688f9488..fcc5c4482 100644 --- a/frontend/src/features/audits/request-audit-detail-dialog.tsx +++ b/frontend/src/features/audits/request-audit-detail-dialog.tsx @@ -1,5 +1,6 @@ import { useQuery } from "@tanstack/react-query"; import { + Bot, Check, CheckCircle2, Code2, @@ -111,6 +112,10 @@ export function RequestAuditDetailDialog({ {t("audits.clientRequestBody")} + + + {t("audits.responseBodyTitle")} + {t("audits.upstreamDiagnostics")} @@ -131,6 +136,10 @@ export function RequestAuditDetailDialog({ + + + + @@ -412,6 +421,112 @@ function ClientRequestBodyPanel({ audit }: { audit: AuditDTO }) { ); } +function ResponseBodyPanel({ audit }: { audit: AuditDTO }) { + const { t } = useTranslation(); + const [copiedType, setCopiedType] = useState(null); + + const rawBody = audit.responseBody ?? ""; + + const { formattedJson, isJson, markdownBlock, byteSize } = useMemo(() => { + let formatted = rawBody; + let jsonValid = false; + if (rawBody) { + try { + const parsed = JSON.parse(rawBody); + formatted = JSON.stringify(parsed, null, 2); + jsonValid = true; + } catch { + formatted = rawBody; + jsonValid = false; + } + } + + const md = jsonValid ? "```json\n" + formatted + "\n```" : "```\n" + formatted + "\n```"; + const size = new Blob([rawBody]).size; + + return { + formattedJson: formatted, + isJson: jsonValid, + markdownBlock: md, + byteSize: size, + }; + }, [rawBody]); + + async function handleCopy(type: "raw" | "markdown") { + const textToCopy = type === "raw" ? formattedJson : markdownBlock; + const ok = await copyToClipboard(textToCopy); + if (ok) { + setCopiedType(type); + setTimeout(() => setCopiedType(null), 1800); + toast.success(t("common.copied")); + } else { + toast.error(t("common.copyFailed")); + } + } + + if (!rawBody) { + return } message={t("audits.noResponseBody")} />; + } + + return ( +
    +
    +
    + {audit.statusCode ? ( + = 400} /> + ) : null} + {byteSize > 0 ? ( + + {formatByteSize(byteSize)} + + ) : null} + {audit.streaming ? ( + + {t("audits.modeStreaming")} + + ) : null} + {audit.outputTokens > 0 ? ( + + {audit.outputTokens} tokens + + ) : null} +
    + +
    + + +
    +
    + +
    +
    +          {formattedJson}
    +        
    +
    +
    + ); +} + function UpstreamAttemptsPanel({ audit, attempts, diff --git a/frontend/src/features/audits/request-audits-api.ts b/frontend/src/features/audits/request-audits-api.ts index 91f2fdee6..a569b5217 100644 --- a/frontend/src/features/audits/request-audits-api.ts +++ b/frontend/src/features/audits/request-audits-api.ts @@ -70,6 +70,7 @@ export type AuditDTO = { requestPath?: string; requestHeaders?: Record; requestBody?: string; + responseBody?: string; attemptCount: number; createdAt: string; }; @@ -161,7 +162,7 @@ const auditValidator = hasShape({ durationMs: isNumber, errorCode: isOptional(isString), requestMethod: isOptional(isString), requestPath: isOptional(isString), requestHeaders: isOptional(isRecordOf(isArrayOf(isString))), - requestBody: isOptional(isString), attemptCount: isNumber, createdAt: isString, + requestBody: isOptional(isString), responseBody: isOptional(isString), attemptCount: isNumber, createdAt: isString, }); const auditAttemptValidator = hasShape({ id: isString, number: isNumber, source: isOneOf("upstream_http", "gateway_transport", "credential"), stage: isString, diff --git a/frontend/src/features/settings/settings-api.ts b/frontend/src/features/settings/settings-api.ts index a36847fc0..c31c20bbc 100644 --- a/frontend/src/features/settings/settings-api.ts +++ b/frontend/src/features/settings/settings-api.ts @@ -24,7 +24,7 @@ export type SettingsConfigDTO = { accountIsolatedConnections: boolean; segmentedSelector: { enabled: boolean; minCandidates: number; windowSize: number }; }; - audit: { bufferSize: number; batchSize: number; flushInterval: string; commitDelayMS: number }; + audit: { bufferSize: number; batchSize: number; flushInterval: string; commitDelayMS: number; retentionDays?: number; recordRequestBody?: boolean; recordResponseBody?: boolean }; clientKeyDefaults: { rpmLimit: number; maxConcurrent: number }; accounts: { markBuildForbiddenReauth: boolean; @@ -132,7 +132,10 @@ const settingsConfigValidator = hasShape({ accountIsolatedConnections: isOptional(isBoolean), segmentedSelector: isOptional(hasShape({ enabled: isBoolean, minCandidates: isNumber, windowSize: isNumber })), }), - audit: hasShape({ bufferSize: isNumber, batchSize: isNumber, flushInterval: isString, commitDelayMS: isOptional(isNumber) }), + audit: hasShape({ + bufferSize: isNumber, batchSize: isNumber, flushInterval: isString, commitDelayMS: isOptional(isNumber), + retentionDays: isOptional(isNumber), recordRequestBody: isOptional(isBoolean), recordResponseBody: isOptional(isBoolean), + }), clientKeyDefaults: hasShape({ rpmLimit: isNumber, maxConcurrent: isNumber }), // Older backends may omit accounts; withSettingsDefaults supplies a safe local default. accounts: isOptional(hasShape({ @@ -172,6 +175,9 @@ function withSettingsDefaults(snapshot: SettingsSnapshotDTO): SettingsSnapshotDT audit: { ...snapshot.config.audit, commitDelayMS: snapshot.config.audit.commitDelayMS ?? 5, + retentionDays: snapshot.config.audit.retentionDays ?? 7, + recordRequestBody: snapshot.config.audit.recordRequestBody ?? true, + recordResponseBody: snapshot.config.audit.recordResponseBody ?? true, }, routing: { ...snapshot.config.routing, diff --git a/frontend/src/features/settings/settings-model.ts b/frontend/src/features/settings/settings-model.ts index 96c03fdfb..01b2db98a 100644 --- a/frontend/src/features/settings/settings-model.ts +++ b/frontend/src/features/settings/settings-model.ts @@ -152,7 +152,15 @@ export const settingsSchema = z.object({ }), }).refine((value) => durationSeconds(value.cooldownMax) >= durationSeconds(value.cooldownBase), { path: ["cooldownMax"] }) .refine((value) => value.segmentedSelector.windowSize <= value.segmentedSelector.minCandidates, { path: ["segmentedSelector", "windowSize"] }), - audit: z.object({ bufferSize: positiveInteger.max(262_144), batchSize: positiveInteger.max(4_096), flushInterval: auditFlushDuration, commitDelayMS: positiveInteger.max(50) }) + audit: z.object({ + bufferSize: positiveInteger.max(262_144), + batchSize: positiveInteger.max(4_096), + flushInterval: auditFlushDuration, + commitDelayMS: positiveInteger.max(50), + retentionDays: z.number().int().min(0).max(365), + recordRequestBody: z.boolean(), + recordResponseBody: z.boolean(), + }) .refine((value) => value.batchSize <= value.bufferSize, { path: ["batchSize"] }), clientKeyDefaults: z.object({ rpmLimit: positiveInteger.max(100_000), maxConcurrent: positiveInteger.max(1_024) }), accounts: z.object({ @@ -209,7 +217,15 @@ export function toSettingsForm(config: SettingsConfigDTO): SettingsForm { accountIsolatedConnections: config.routing.accountIsolatedConnections, segmentedSelector: config.routing.segmentedSelector, }, - audit: { bufferSize: config.audit.bufferSize, batchSize: config.audit.batchSize, flushInterval: parseDuration(config.audit.flushInterval), commitDelayMS: config.audit.commitDelayMS }, + audit: { + bufferSize: config.audit.bufferSize, + batchSize: config.audit.batchSize, + flushInterval: parseDuration(config.audit.flushInterval), + commitDelayMS: config.audit.commitDelayMS, + retentionDays: config.audit.retentionDays ?? 7, + recordRequestBody: config.audit.recordRequestBody ?? true, + recordResponseBody: config.audit.recordResponseBody ?? true, + }, clientKeyDefaults: config.clientKeyDefaults, accounts: { markBuildForbiddenReauth: config.accounts.markBuildForbiddenReauth, @@ -252,7 +268,15 @@ export function toSettingsDTO(config: SettingsForm): SettingsConfigDTO { accountIsolatedConnections: config.routing.accountIsolatedConnections, segmentedSelector: config.routing.segmentedSelector, }, - audit: { bufferSize: config.audit.bufferSize, batchSize: config.audit.batchSize, flushInterval: formatDuration(config.audit.flushInterval), commitDelayMS: config.audit.commitDelayMS }, + audit: { + bufferSize: config.audit.bufferSize, + batchSize: config.audit.batchSize, + flushInterval: formatDuration(config.audit.flushInterval), + commitDelayMS: config.audit.commitDelayMS, + retentionDays: config.audit.retentionDays, + recordRequestBody: config.audit.recordRequestBody, + recordResponseBody: config.audit.recordResponseBody, + }, clientKeyDefaults: config.clientKeyDefaults, accounts: { markBuildForbiddenReauth: config.accounts.markBuildForbiddenReauth, diff --git a/frontend/src/features/settings/settings-page.tsx b/frontend/src/features/settings/settings-page.tsx index c88919d90..b9ac679fc 100644 --- a/frontend/src/features/settings/settings-page.tsx +++ b/frontend/src/features/settings/settings-page.tsx @@ -85,6 +85,7 @@ export function SettingsPage() { {t("console.name")} {t("settings.groups.delivery")} {t("settings.groups.policies")} + {t("settings.audit.tabTitle")} {t("settings.accounts.title")} {t("updates.title")} @@ -341,15 +342,6 @@ export function SettingsPage() { - -
    - - - } /> - -
    -
    -
    @@ -358,6 +350,129 @@ export function SettingsPage() { + + +
    + + + + + + ( +
    + +
    + )} + /> +
    + + + ( +
    + +
    + )} + /> +
    +
    +
    + + +
    + + + + + + + + ( + + )} + /> + + + + +
    +
    +
    +
    diff --git a/frontend/src/shared/i18n/index.ts b/frontend/src/shared/i18n/index.ts index 96ab13122..ae7bad1d1 100644 --- a/frontend/src/shared/i18n/index.ts +++ b/frontend/src/shared/i18n/index.ts @@ -944,8 +944,12 @@ const resources = { serverTools: "工具调用 {{count}} 次", detailTitle: "请求诊断与报文详情", requestOverview: "请求概览", - clientRequestBody: "完整报文", + clientRequestBody: "请求报文", clientRequest: "完整报文", + responseBodyTitle: "响应报文", + noResponseBody: "未捕获到服务端响应报文或未启用响应报文记录。", + copyResponseRaw: "复制响应报文", + modeStreaming: "SSE 流式", requestHeaders: "HTTP 请求头", requestBody: "请求体 (Body)", requestLine: "请求行", @@ -1007,7 +1011,7 @@ const resources = { restartRequired: "重启生效", invalidValue: "请输入有效值", durationUnit: "时长单位", - groups: { providers: "Provider 配置", delivery: "媒体与网络", policies: "运行策略", about: "关于" }, + groups: { providers: "Provider 配置", delivery: "媒体与网络", policies: "运行策略", audit: "日志设置", accounts: "账号维护", about: "关于" }, server: { title: "服务容量", maxConcurrentRequests: "API 请求并发上限", maxConcurrentRequestsHelp: "单实例同时处理的 /v1 请求数;调低不会中断进行中的请求。" }, provider: { title: "Grok Build", baseURL: "上游地址", baseURLHelp: "Grok Build 请求使用的主要上游端点。", fallbackBaseURL: "XAI 备用地址", fallbackBaseURLHelp: "自动路由至 XAI 或 Build 请求回退时使用的端点。", clientVersion: "客户端版本", clientVersionHelp: "随 Build 请求发送的版本;建议使用已完成兼容验证的版本。", clientIdentifier: "客户端标识", clientIdentifierHelp: "随请求发送的客户端身份标识,需与上游协议保持一致。", tokenAuth: "Token Auth", tokenAuthHelp: "随 Grok Build 请求发送的客户端协议标识,通常保持为 xai-grok-cli。", userAgent: "User-Agent", userAgentHelp: "用于 Grok Build 推理请求及其出口节点;直连 XAI 媒体请求按客户端版本使用官方 xai-grok-build User-Agent。", recommendedVersion: "推荐 {{version}}", syncRecommendedVersion: "同步最新版本", recommendedVersionApplied: "已是推荐版本", syncRecommendedVersionDescription: "使用已完成兼容验证的版本和 User-Agent 填充当前表单,保存后生效。" }, web: { title: "Grok Web", baseURL: "上游地址", baseURLHelp: "Grok Web API 与账号操作使用的基础端点。", statsigMode: "x-statsig 获取方式", statsigModeHelp: "选择固定配置或通过签名服务动态获取请求标识。", statsigManual: "手动填写", statsigURL: "URL 获取", statsigValue: "x-statsig-id", statsigValueHelp: "直接配置 Grok Web 请求使用的 x-statsig-id。", statsigConfigured: "已配置", statsigKeepConfigured: "留空保持当前配置", statsigValuePlaceholder: "粘贴 x-statsig-id", statsigSignerURL: "签名服务 URL", statsigSignerURLHelp: "按请求获取 x-statsig-id 的内部签名服务地址。", quotaTimeout: "额度请求超时", quotaTimeoutHelp: "查询账号额度与状态时允许等待上游响应的最长时间。", chatTimeout: "聊天超时", chatTimeoutHelp: "Grok Web 聊天请求完成前允许等待的最长时间。", streamIdleTimeout: "流式空闲超时", streamIdleTimeoutHelp: "上游文本聊天流连续无数据时允许等待的最长时间;不影响图片、视频和其他媒体请求。", imageTimeout: "图片超时", imageTimeoutHelp: "图片生成与编辑任务完成前允许等待的最长时间。", videoTimeout: "视频超时", videoTimeoutHelp: "视频生成任务完成前允许等待的最长时间。", mediaConcurrency: "媒体任务并发", mediaConcurrencyHelp: "单实例可同时执行的图片与视频任务数;修改后需重启。", recoveryBackoffBase: "额度恢复退避", recoveryBackoffBaseHelp: "额度恢复检查连续失败后的初始重试间隔。", recoveryBackoffMax: "额度恢复最大退避", recoveryBackoffMaxHelp: "额度恢复检查采用指数退避时允许达到的最大间隔。", allowNSFW: "允许 NSFW 图片", allowNSFWHelp: "允许 Grok Web 图片请求启用成人内容偏好。", clearanceMode: "Clearance 管理", clearanceModeHelp: "选择手动维护 Cloudflare 凭据,或由 FlareSolverr 按实际出口自动获取并定期刷新。", clearanceManual: "手动维护", clearanceFlareSolverr: "FlareSolverr", flareSolverrURL: "FlareSolverr 地址", flareSolverrURLHelp: "用于求解 Cloudflare Clearance 的内部 FlareSolverr 服务地址。", clearanceTimeout: "求解超时", clearanceTimeoutHelp: "等待单次 FlareSolverr 求解完成的最长时间。", clearanceRefresh: "刷新周期", clearanceRefreshHelp: "固定出口 Clearance 的定期刷新间隔;账号粘性代理会在实际请求中按账号独立维护。" }, @@ -1016,7 +1020,26 @@ const resources = { media: { title: "媒体存储", maxImageSize: "单张图片上限", maxImageSizeHelp: "单个图片文件允许写入本地媒体存储的最大大小。", maxTotalSize: "媒体存储上限", maxTotalSizeHelp: "本地图片与视频文件允许占用的总存储空间。", cleanupThresholdPercent: "自动清理阈值", cleanupThresholdPercentHelp: "存储使用率达到该比例后,系统按时间清理较早的媒体文件。", cleanupInterval: "容量检查间隔", cleanupIntervalHelp: "后台检查媒体存储使用量并触发清理的时间间隔。", sizeUnit: "存储单位", publicApiBaseURL: "公共 API 地址", publicApiBaseURLHelp: "用于生成图片公开 URL 与文档示例地址。留空时回退到服务启动值,默认为 http://127.0.0.1:8000。" }, egress: { title: "出口代理", description: "节点按 Grok Build、Grok Web、Grok Console 或 Web 资源独立管理代理和健康状态。Build 沿用 Provider 的 User-Agent;Web 与 Console 节点单独管理浏览器 User-Agent 和 Cookie。代理地址和 Cloudflare Cookie 仅写入。", add: "添加节点", saved: "代理节点已保存", deleted: "代理节点已删除", name: "名称", scope: "作用域", proxy: "代理", clearance: "Clearance", health: "健康度", directFallback: "未配置节点时使用直连", scopeBuild: "Grok Build", scopeWeb: "Grok Web", scopeWebAsset: "Grok Web(仅资源)", configured: "已配置", direct: "直连", none: "无", editTitle: "编辑代理节点", addTitle: "添加代理节点", dialogDescription: "Build 节点只配置代理并沿用 Provider 的 User-Agent;Web 与 Console 节点使用各自的浏览器 User-Agent 和 Cloudflare Cookie。", enabled: "启用", proxyURL: "代理地址", proxyProtocols: "支持 HTTP、HTTPS、SOCKS4/4A、SOCKS5/5H、Trojan、VLESS、Shadowsocks 和 VMess。\n隧道协议支持 TCP、WebSocket 与 TLS;Resin 可在用户名中使用 {account}。", proxyPool: "代理池模式", proxyPoolHelp: "共享代理池的单次连接失败不会使整个出口节点进入冷却;包含 {account} 的代理会自动启用此策略。", userAgent: "User-Agent", cloudflareCookie: "Cloudflare Cookie", keepConfigured: "已配置,留空保持不变", operationFailed: "操作失败", refreshClearance: "刷新 Clearance", clearanceRefreshed: "Clearance 已刷新", clearanceManaged: "Cloudflare Cookie 与 User-Agent 由 FlareSolverr 自动维护;Resin 等账号粘性代理会按账号隔离。", accounts: "已绑定", probe: "探测", healthy: "可用", unhealthy: "不可用", notTested: "未测试", test: "测试代理", testedOne: "代理测试完成", operations: "代理运营", automation: "自动任务", subscriptions: "代理订阅", testAll: "测试全部", rebalance: "立即调配", importText: "导入文本", addSource: "添加订阅", source: "订阅源", sync: "同步", capacity: "账号容量", noSources: "暂无订阅源", never: "从未", unlimited: "无限制", sourceSaved: "订阅源已保存", sourceDeleted: "订阅源已删除", sourceSynced: "订阅同步完成:导入 {{imported}},跳过 {{skipped}}", imported: "代理导入完成:导入 {{imported}},跳过 {{skipped}}", tested: "代理测试完成:可用 {{healthy}},不可用 {{unhealthy}}", rebalanced: "账号调配完成:新增 {{assigned}},均衡 {{rebalanced}},未分配 {{unplaced}}", automationSaved: "自动任务已保存", editSource: "编辑订阅", subscriptionURL: "订阅地址", sourceDialogDescription: "配置订阅来源、作用域、刷新周期和单节点账号容量。", importDialogDescription: "支持明文或 Base64 代理列表,每行一个 HTTP、SOCKS、Trojan、VLESS、SS 或 VMess 地址。", probeInterval: "探测间隔(秒)", probeIntervalHelp: "后台重新测试已启用代理节点的间隔。", assignmentInterval: "调配间隔(秒)", assignmentIntervalHelp: "后台检查自动分配与节点容量的间隔。", autoAssign: "自动分配未绑定账号", autoAssignHelp: "将可调度且未绑定的账号分配到近期探测可用的节点。", autoBalance: "自动均衡自动绑定账号", autoBalanceHelp: "在健康节点之间调整自动绑定;手工绑定始终保持不变。", refreshInterval: "订阅刷新间隔(秒)", proxyList: "代理列表" }, routing: { title: "路由策略", stickyTTL: "会话粘性时长", stickyTTLHelp: "同一会话优先复用已选账号的有效时长;到期后重新参与调度。", cooldownBase: "基础冷却时间", cooldownBaseHelp: "账号发生可恢复故障后首次进入冷却的时长。", cooldownMax: "最大冷却时间", cooldownMaxHelp: "连续故障触发指数退避时允许达到的最长冷却时间。", capacityWait: "账号满载等待", capacityWaitHelp: "全部候选账号并发已满时,切换或失败前等待容量释放的时间。", maxAttempts: "最大尝试次数", maxAttemptsHelp: "单次请求在可用账号之间切换并重试的最大次数,范围为 1–200。", videoMaxAttempts: "视频最大尝试次数", videoMaxAttemptsHelp: "仅作用于视频任务创建阶段的切号重试,可单独设置数值,或开启无限制。创建成功后的轮询不会换号。", preferFreeBuild: "Grok Build Free 账号优先", preferFreeBuildHelp: "启用后优先选择已确认可用的 Free 账号,其他路由条件保持不变。", accountIsolatedConnections: "按账号隔离上游连接", accountIsolatedConnectionsHelp: "开启后,不同账号使用各自的上游 TCP/HTTP 连接池,便于外部 L4 或按连接哈希的负载均衡器分散流量;同一账号仍可复用连接。适用于 Build / Web / Console 出站及 Web 转 Build。会增加连接数、TLS 握手、内存和文件描述符占用,建议仅在确有外部连接级负载均衡需求时开启。" }, - audit: { title: "请求审计", bufferSize: "队列容量", bufferSizeHelp: "审计记录进入持久化前可在内存队列中等待的最大数量。", batchSize: "批量写入数", batchSizeHelp: "每次数据库事务最多写入的审计记录数量。", flushInterval: "刷新间隔", flushIntervalHelp: "队列未达到批量写入数时,强制提交待处理审计记录的间隔。", commitDelay: "提交聚合等待(毫秒)", commitDelayHelp: "需要确认提交的请求进入原子批次前允许等待的最长时间。" }, + audit: { + title: "请求审计与日志设置", + tabTitle: "日志设置", + retentionTitle: "日志保留与报文策略", + retentionDays: "日志保留天数", + retentionDaysHelp: "自动清理超过指定天数的请求审计与上游失败诊断记录。设置为 0 表示永久保留,默认为 7 天。", + recordRequestBody: "记录客户端请求报文", + recordRequestBodyHelp: "记录客户端发送的完整 HTTP 请求头与 Body(包含文本与多模态数据,便于报文复现与排查)。", + recordResponseBody: "记录服务端响应报文", + recordResponseBodyHelp: "流式输出结束后自动聚合完整的响应报文(SSE / JSON),便于在审计中查看与复制。", + performanceTitle: "写入缓冲与性能", + bufferSize: "队列容量", + bufferSizeHelp: "审计记录进入持久化前可在内存队列中等待的最大数量。", + batchSize: "批量写入数", + batchSizeHelp: "每次数据库事务最多写入的审计记录数量。", + flushInterval: "刷新间隔", + flushIntervalHelp: "队列未达到批量写入数时,强制提交待处理审计记录的间隔。", + commitDelay: "提交聚合等待(毫秒)", + commitDelayHelp: "需要确认提交的请求进入原子批次前允许等待的最长时间。", + }, clientKeys: { title: "密钥默认限制", rpmLimit: "默认 RPM", rpmLimitHelp: "新建客户端密钥默认允许的每分钟请求数。", maxConcurrent: "默认并发数", maxConcurrentHelp: "新建客户端密钥默认允许同时执行的请求数。" }, accounts: { title: "账号维护", @@ -1721,8 +1744,12 @@ const resources = { serverTools: "Tool calls {{count}} times", detailTitle: "Request Diagnostics & Payload", requestOverview: "Overview", - clientRequestBody: "Full Request", + clientRequestBody: "Request Body", clientRequest: "Full Request", + responseBodyTitle: "Response Body", + noResponseBody: "No response body captured or response payload recording is disabled.", + copyResponseRaw: "Copy Response Body", + modeStreaming: "SSE Stream", requestHeaders: "HTTP Request Headers", requestBody: "Request Body", requestLine: "Request Line", @@ -1783,7 +1810,7 @@ const resources = { errorFrameCount_one: "{{count}} error frame", errorFrameCount_other: "{{count}} error frames", }, - settings: { title: "Runtime settings", description: "Manage hot-reloadable gateway runtime parameters.", saved: "Settings saved and applied", restartRequired: "Restart required", invalidValue: "Enter a valid value", durationUnit: "Duration unit", groups: { providers: "Providers", delivery: "Media & network", policies: "Runtime policies", about: "About" }, server: { title: "Service capacity", maxConcurrentRequests: "API request concurrency limit", maxConcurrentRequestsHelp: "Maximum simultaneous /v1 requests per instance. Lowering the limit does not interrupt active requests." }, provider: { title: "Grok Build provider", baseURL: "Upstream URL", baseURLHelp: "Primary upstream endpoint used for Grok Build requests.", fallbackBaseURL: "XAI fallback URL", fallbackBaseURLHelp: "Endpoint used when routing to XAI or falling back from Grok Build.", clientVersion: "Client version", clientVersionHelp: "Version sent with Build requests. Use a version that has passed compatibility validation.", clientIdentifier: "Client identifier", clientIdentifierHelp: "Client identity sent with requests; keep it aligned with the upstream protocol.", tokenAuth: "Token auth", tokenAuthHelp: "Client protocol identifier sent with Grok Build requests; normally keep xai-grok-cli.", userAgent: "User-Agent", userAgentHelp: "Used by Grok Build inference requests and their egress nodes. Direct XAI media requests derive the official xai-grok-build User-Agent from the client version.", recommendedVersion: "Recommended {{version}}", syncRecommendedVersion: "Sync latest version", recommendedVersionApplied: "Recommended version active", syncRecommendedVersionDescription: "Fill the form with the validated version and User-Agent; save to apply." }, web: { title: "Grok Web", baseURL: "Upstream URL", baseURLHelp: "Base endpoint used for Grok Web API calls and account operations.", statsigMode: "x-statsig source", statsigModeHelp: "Use a fixed value or retrieve request identity dynamically from a signer service.", statsigManual: "Manual", statsigURL: "Fetch from URL", statsigValue: "x-statsig-id", statsigValueHelp: "Fixed x-statsig-id attached to Grok Web requests.", statsigConfigured: "Configured", statsigKeepConfigured: "Leave blank to keep the current value", statsigValuePlaceholder: "Paste x-statsig-id", statsigSignerURL: "Signer URL", statsigSignerURLHelp: "Internal signer endpoint used to retrieve x-statsig-id per request.", quotaTimeout: "Quota timeout", quotaTimeoutHelp: "Maximum time to wait for upstream quota and account-status queries.", chatTimeout: "Chat timeout", chatTimeoutHelp: "Maximum time to wait for a Grok Web chat request to complete.", streamIdleTimeout: "Stream idle timeout", streamIdleTimeoutHelp: "Maximum period without upstream data for raw text-chat streams; image, video, and other media requests are unaffected.", imageTimeout: "Image timeout", imageTimeoutHelp: "Maximum time to wait for image generation or editing to complete.", videoTimeout: "Video timeout", videoTimeoutHelp: "Maximum time to wait for a Grok Web video generation request to complete.", mediaConcurrency: "Media job concurrency", mediaConcurrencyHelp: "Image and video jobs one instance may run concurrently. Changes require a restart.", recoveryBackoffBase: "Quota recovery backoff", recoveryBackoffBaseHelp: "Initial retry interval after consecutive quota-recovery check failures.", recoveryBackoffMax: "Maximum recovery backoff", recoveryBackoffMaxHelp: "Maximum interval reached by exponential backoff for quota-recovery checks.", allowNSFW: "Allow NSFW images", allowNSFWHelp: "Allow Grok Web image requests to enable the adult-content preference.", clearanceMode: "Clearance management", clearanceModeHelp: "Maintain Cloudflare credentials manually or let FlareSolverr acquire and refresh them on the actual egress.", clearanceManual: "Manual", clearanceFlareSolverr: "FlareSolverr", flareSolverrURL: "FlareSolverr URL", flareSolverrURLHelp: "Internal FlareSolverr endpoint used to solve Cloudflare Clearance.", clearanceTimeout: "Solve timeout", clearanceTimeoutHelp: "Maximum time allowed for one FlareSolverr solve.", clearanceRefresh: "Refresh interval", clearanceRefreshHelp: "Periodic refresh interval for fixed egress; account-bound proxies are maintained independently during requests." }, console: { baseURLHelp: "Base endpoint used for Grok Console requests and account operations.", chatTimeoutHelp: "Maximum time to wait for a Grok Console chat request to complete.", streamIdleTimeout: "Stream idle timeout", streamIdleTimeoutHelp: "Maximum period without upstream data for text SSE only; media and non-streaming requests are unaffected." }, batch: { title: "Batch tasks", importConcurrency: "Import sync concurrency", importConcurrencyHelp: "Accounts initialized and synchronized concurrently after a batch import.", conversionConcurrency: "Account conversion concurrency", conversionConcurrencyHelp: "Cross-provider account conversions that may run concurrently.", syncConcurrency: "Data sync concurrency", syncConcurrencyHelp: "Quota, status, and capability synchronizations that may run concurrently.", refreshConcurrency: "Credential refresh concurrency", refreshConcurrencyHelp: "Account credential refreshes that may run concurrently.", randomDelay: "Maximum random delay (ms)", randomDelayHelp: "Maximum randomized delay before batch work starts to smooth upstream request bursts." }, media: { title: "Media storage", maxImageSize: "Maximum image size", maxImageSizeHelp: "Largest individual image that may be written to local media storage.", maxTotalSize: "Media storage limit", maxTotalSizeHelp: "Total local storage available to image and video files.", cleanupThresholdPercent: "Automatic cleanup threshold", cleanupThresholdPercentHelp: "Storage utilization that triggers removal of older media files.", cleanupInterval: "Capacity check interval", cleanupIntervalHelp: "Interval between background storage checks and cleanup evaluation.", sizeUnit: "Storage unit", publicApiBaseURL: "Public API base URL", publicApiBaseURLHelp: "Used for public image URLs and docs examples. Leave empty to use the service startup value, which defaults to http://127.0.0.1:8000." }, egress: { title: "Egress proxies", description: "Nodes manage proxy and health independently for Grok Build, Grok Web, Grok Console, or Web assets. Build inherits the Provider User-Agent; Web and Console nodes manage their own browser User-Agent and cookies. Proxy URLs and Cloudflare cookies are write-only.", add: "Add node", saved: "Proxy node saved", deleted: "Proxy node deleted", name: "Name", scope: "Scope", proxy: "Proxy", clearance: "Clearance", health: "Health", directFallback: "Direct connection is used when no node is configured", scopeBuild: "Grok Build", scopeWeb: "Grok Web", scopeWebAsset: "Grok Web (assets only)", configured: "Configured", direct: "Direct", none: "None", editTitle: "Edit proxy node", addTitle: "Add proxy node", dialogDescription: "Build nodes only configure the proxy and inherit the Provider User-Agent; Web and Console nodes use their own browser User-Agent and Cloudflare cookies.", enabled: "Enabled", proxyURL: "Proxy URL", proxyProtocols: "Supports HTTP, HTTPS, SOCKS4, SOCKS4A, SOCKS5, and SOCKS5H.\nResin users can put {account} in the username to bind one lease per account.", proxyPool: "Proxy pool mode", proxyPoolHelp: "A request-level connection failure does not cool the entire shared node. Proxies containing {account} use this policy automatically.", userAgent: "User-Agent", cloudflareCookie: "Cloudflare Cookie", keepConfigured: "Configured; leave blank to keep unchanged", operationFailed: "Operation failed", refreshClearance: "Refresh Clearance", clearanceRefreshed: "Clearance refreshed", clearanceManaged: "Cloudflare cookies and User-Agent are managed by FlareSolverr; account-bound proxies are isolated per account." }, routing: { title: "Routing policy", stickyTTL: "Sticky session TTL", stickyTTLHelp: "How long a session prefers its selected account before returning to normal scheduling.", cooldownBase: "Base cooldown", cooldownBaseHelp: "Initial cooldown applied after a recoverable account failure.", cooldownMax: "Maximum cooldown", cooldownMaxHelp: "Longest cooldown allowed when repeated failures trigger exponential backoff.", capacityWait: "Saturated account wait", capacityWaitHelp: "How long to wait for capacity before switching accounts or failing when all candidates are saturated.", maxAttempts: "Maximum attempts", maxAttemptsHelp: "Maximum account switches and retries allowed for one request, from 1 to 200.", videoMaxAttempts: "Video maximum attempts", videoMaxAttemptsHelp: "Create-phase account failover for video jobs only. Set a number or enable unlimited. Polling after create stays on the same account.", preferFreeBuild: "Prefer Grok Build Free accounts", preferFreeBuildHelp: "Prefer confirmed usable Free accounts without changing other routing eligibility rules.", accountIsolatedConnections: "Isolate upstream connections by account", accountIsolatedConnectionsHelp: "When enabled, each account uses its own upstream TCP/HTTP connection pool so external L4 or connection-hash load balancers can spread traffic; the same account still reuses connections. Applies to Build, Web, Console, and Web-to-Build traffic. This increases connections, TLS handshakes, memory, and file-descriptor usage, so enable it only when connection-level balancing is required." }, audit: { title: "Request audit", bufferSize: "Queue capacity", bufferSizeHelp: "Maximum audit records held in memory before persistence.", batchSize: "Batch size", batchSizeHelp: "Maximum audit records written in one database transaction.", flushInterval: "Flush interval", flushIntervalHelp: "Maximum time pending audit records wait before they are committed.", commitDelay: "Commit aggregation delay (ms)", commitDelayHelp: "Maximum time acknowledged writes wait to join an atomic batch before commit." }, clientKeys: { title: "Client key defaults", rpmLimit: "Default RPM", rpmLimitHelp: "Requests per minute assigned to newly created client keys.", maxConcurrent: "Default concurrency", maxConcurrentHelp: "Concurrent requests assigned to newly created client keys." }, accounts: { title: "Account maintenance", invalidationTitle: "Automatic interception", botRiskSchedulingTitle: "Bot-risk scheduling", excludeBuildBotFlaggedFromScheduling: "Exclude bot-risk Build accounts from scheduling", excludeBuildBotFlaggedFromSchedulingHelp: "When enabled, Grok Build accounts whose JWT bot_flag_source/bfs is 1 or 2 are removed from Build scheduling only. Linked Web/Console accounts keep their normal scheduling. Accounts remain in the pool for viewing and manual actions.", cleanupTitle: "Account cleanup", autoCleanReauthEnabled: "Automatic invalid-account cleanup", autoCleanReauthEnabledHelp: "Permanently deletes invalid accounts after the configured retention period. Accounts with active requests or video jobs are skipped.", autoCleanReauthInterval: "Cleanup interval", autoCleanReauthIntervalHelp: "How often to check for eligible accounts, from 1 minute to 1 hour.", autoCleanReauthMinAge: "Minimum retention", autoCleanReauthMinAgeHelp: "How long to retain an account after it is marked for reauthorization.", autoCleanIncludeDisabled: "Include disabled accounts", autoCleanIncludeDisabledHelp: "Includes disabled accounts that require reauthorization in automatic cleanup.", autoCleanEnableTitle: "Enable automatic cleanup?", autoCleanEnableDescription: "Eligible accounts will be permanently deleted and cannot be recovered. The first run starts after one full cleanup interval.", autoCleanIncludeDisabledTitle: "Include disabled accounts?", autoCleanIncludeDisabledDescription: "Disabled accounts that require reauthorization will also be permanently deleted. This action cannot be undone.", autoCleanConfirm: "Enable" }, units: { seconds: "Seconds", minutes: "Minutes", hours: "Hours", days: "Days" } }, + settings: { title: "Runtime settings", description: "Manage hot-reloadable gateway runtime parameters.", saved: "Settings saved and applied", restartRequired: "Restart required", invalidValue: "Enter a valid value", durationUnit: "Duration unit", groups: { providers: "Providers", delivery: "Media & network", policies: "Runtime policies", audit: "Audit & Logs", accounts: "Account maintenance", about: "About" }, server: { title: "Service capacity", maxConcurrentRequests: "API request concurrency limit", maxConcurrentRequestsHelp: "Maximum simultaneous /v1 requests per instance. Lowering the limit does not interrupt active requests." }, provider: { title: "Grok Build provider", baseURL: "Upstream URL", baseURLHelp: "Primary upstream endpoint used for Grok Build requests.", fallbackBaseURL: "XAI fallback URL", fallbackBaseURLHelp: "Endpoint used when routing to XAI or falling back from Grok Build.", clientVersion: "Client version", clientVersionHelp: "Version sent with Build requests. Use a version that has passed compatibility validation.", clientIdentifier: "Client identifier", clientIdentifierHelp: "Client identity sent with requests; keep it aligned with the upstream protocol.", tokenAuth: "Token auth", tokenAuthHelp: "Client protocol identifier sent with Grok Build requests; normally keep xai-grok-cli.", userAgent: "User-Agent", userAgentHelp: "Used by Grok Build inference requests and their egress nodes. Direct XAI media requests derive the official xai-grok-build User-Agent from the client version.", recommendedVersion: "Recommended {{version}}", syncRecommendedVersion: "Sync latest version", recommendedVersionApplied: "Recommended version active", syncRecommendedVersionDescription: "Fill the form with the validated version and User-Agent; save to apply." }, web: { title: "Grok Web", baseURL: "Upstream URL", baseURLHelp: "Base endpoint used for Grok Web API calls and account operations.", statsigMode: "x-statsig source", statsigModeHelp: "Use a fixed value or retrieve request identity dynamically from a signer service.", statsigManual: "Manual", statsigURL: "Fetch from URL", statsigValue: "x-statsig-id", statsigValueHelp: "Fixed x-statsig-id attached to Grok Web requests.", statsigConfigured: "Configured", statsigKeepConfigured: "Leave blank to keep the current value", statsigValuePlaceholder: "Paste x-statsig-id", statsigSignerURL: "Signer URL", statsigSignerURLHelp: "Internal signer endpoint used to retrieve x-statsig-id per request.", quotaTimeout: "Quota timeout", quotaTimeoutHelp: "Maximum time to wait for upstream quota and account-status queries.", chatTimeout: "Chat timeout", chatTimeoutHelp: "Maximum time to wait for a Grok Web chat request to complete.", streamIdleTimeout: "Stream idle timeout", streamIdleTimeoutHelp: "Maximum period without upstream data for raw text-chat streams; image, video, and other media requests are unaffected.", imageTimeout: "Image timeout", imageTimeoutHelp: "Maximum time to wait for image generation or editing to complete.", videoTimeout: "Video timeout", videoTimeoutHelp: "Maximum time to wait for a Grok Web video generation request to complete.", mediaConcurrency: "Media job concurrency", mediaConcurrencyHelp: "Image and video jobs one instance may run concurrently. Changes require a restart.", recoveryBackoffBase: "Quota recovery backoff", recoveryBackoffBaseHelp: "Initial retry interval after consecutive quota-recovery check failures.", recoveryBackoffMax: "Maximum recovery backoff", recoveryBackoffMaxHelp: "Maximum interval reached by exponential backoff for quota-recovery checks.", allowNSFW: "Allow NSFW images", allowNSFWHelp: "Allow Grok Web image requests to enable the adult-content preference.", clearanceMode: "Clearance management", clearanceModeHelp: "Maintain Cloudflare credentials manually or let FlareSolverr acquire and refresh them on the actual egress.", clearanceManual: "Manual", clearanceFlareSolverr: "FlareSolverr", flareSolverrURL: "FlareSolverr URL", flareSolverrURLHelp: "Internal FlareSolverr endpoint used to solve Cloudflare Clearance.", clearanceTimeout: "Solve timeout", clearanceTimeoutHelp: "Maximum time allowed for one FlareSolverr solve.", clearanceRefresh: "Refresh interval", clearanceRefreshHelp: "Periodic refresh interval for fixed egress; account-bound proxies are maintained independently during requests." }, console: { baseURLHelp: "Base endpoint used for Grok Console requests and account operations.", chatTimeoutHelp: "Maximum time to wait for a Grok Console chat request to complete.", streamIdleTimeout: "Stream idle timeout", streamIdleTimeoutHelp: "Maximum period without upstream data for text SSE only; media and non-streaming requests are unaffected." }, batch: { title: "Batch tasks", importConcurrency: "Import sync concurrency", importConcurrencyHelp: "Accounts initialized and synchronized concurrently after a batch import.", conversionConcurrency: "Account conversion concurrency", conversionConcurrencyHelp: "Cross-provider account conversions that may run concurrently.", syncConcurrency: "Data sync concurrency", syncConcurrencyHelp: "Quota, status, and capability synchronizations that may run concurrently.", refreshConcurrency: "Credential refresh concurrency", refreshConcurrencyHelp: "Account credential refreshes that may run concurrently.", randomDelay: "Maximum random delay (ms)", randomDelayHelp: "Maximum randomized delay before batch work starts to smooth upstream request bursts." }, media: { title: "Media storage", maxImageSize: "Maximum image size", maxImageSizeHelp: "Largest individual image that may be written to local media storage.", maxTotalSize: "Media storage limit", maxTotalSizeHelp: "Total local storage available to image and video files.", cleanupThresholdPercent: "Automatic cleanup threshold", cleanupThresholdPercentHelp: "Storage utilization that triggers removal of older media files.", cleanupInterval: "Capacity check interval", cleanupIntervalHelp: "Interval between background storage checks and cleanup evaluation.", sizeUnit: "Storage unit", publicApiBaseURL: "Public API base URL", publicApiBaseURLHelp: "Used for public image URLs and docs examples. Leave empty to use the service startup value, which defaults to http://127.0.0.1:8000." }, egress: { title: "Egress proxies", description: "Nodes manage proxy and health independently for Grok Build, Grok Web, Grok Console, or Web assets. Build inherits the Provider User-Agent; Web and Console nodes manage their own browser User-Agent and cookies. Proxy URLs and Cloudflare cookies are write-only.", add: "Add node", saved: "Proxy node saved", deleted: "Proxy node deleted", name: "Name", scope: "Scope", proxy: "Proxy", clearance: "Clearance", health: "Health", directFallback: "Direct connection is used when no node is configured", scopeBuild: "Grok Build", scopeWeb: "Grok Web", scopeWebAsset: "Grok Web (assets only)", configured: "Configured", direct: "Direct", none: "None", editTitle: "Edit proxy node", addTitle: "Add proxy node", dialogDescription: "Build nodes only configure the proxy and inherit the Provider User-Agent; Web and Console nodes use their own browser User-Agent and Cloudflare cookies.", enabled: "Enabled", proxyURL: "Proxy URL", proxyProtocols: "Supports HTTP, HTTPS, SOCKS4, SOCKS4A, SOCKS5, and SOCKS5H.\nResin users can put {account} in the username to bind one lease per account.", proxyPool: "Proxy pool mode", proxyPoolHelp: "A request-level connection failure does not cool the entire shared node. Proxies containing {account} use this policy automatically.", userAgent: "User-Agent", cloudflareCookie: "Cloudflare Cookie", keepConfigured: "Configured; leave blank to keep unchanged", operationFailed: "Operation failed", refreshClearance: "Refresh Clearance", clearanceRefreshed: "Clearance refreshed", clearanceManaged: "Cloudflare cookies and User-Agent are managed by FlareSolverr; account-bound proxies are isolated per account." }, routing: { title: "Routing policy", stickyTTL: "Sticky session TTL", stickyTTLHelp: "How long a session prefers its selected account before returning to normal scheduling.", cooldownBase: "Base cooldown", cooldownBaseHelp: "Initial cooldown applied after a recoverable account failure.", cooldownMax: "Maximum cooldown", cooldownMaxHelp: "Longest cooldown allowed when repeated failures trigger exponential backoff.", capacityWait: "Saturated account wait", capacityWaitHelp: "How long to wait for capacity before switching accounts or failing when all candidates are saturated.", maxAttempts: "Maximum attempts", maxAttemptsHelp: "Maximum account switches and retries allowed for one request, from 1 to 200.", videoMaxAttempts: "Video maximum attempts", videoMaxAttemptsHelp: "Create-phase account failover for video jobs only. Set a number or enable unlimited. Polling after create stays on the same account.", preferFreeBuild: "Prefer Grok Build Free accounts", preferFreeBuildHelp: "Prefer confirmed usable Free accounts without changing other routing eligibility rules.", accountIsolatedConnections: "Isolate upstream connections by account", accountIsolatedConnectionsHelp: "When enabled, each account uses its own upstream TCP/HTTP connection pool so external L4 or connection-hash load balancers can spread traffic; the same account still reuses connections. Applies to Build, Web, Console, and Web-to-Build traffic. This increases connections, TLS handshakes, memory, and file-descriptor usage, so enable it only when connection-level balancing is required." }, audit: { title: "Request Audit & Logs", tabTitle: "Audit & Logs", retentionTitle: "Log Retention & Payload Policy", retentionDays: "Log retention days", retentionDaysHelp: "Automatically purges request audits and failure attempts older than the specified days. Set to 0 for indefinite retention, default is 7 days.", recordRequestBody: "Record client request payloads", recordRequestBodyHelp: "Captures complete HTTP headers and request bodies from client calls for diagnostic and replication purposes.", recordResponseBody: "Record server response payloads", recordResponseBodyHelp: "Reconstructs complete SSE stream responses and JSON bodies to view in request audit details.", performanceTitle: "Buffer & Performance", bufferSize: "Queue capacity", bufferSizeHelp: "Maximum audit records held in memory before persistence.", batchSize: "Batch size", batchSizeHelp: "Maximum audit records written in one database transaction.", flushInterval: "Flush interval", flushIntervalHelp: "Maximum time pending audit records wait before they are committed.", commitDelay: "Commit aggregation delay (ms)", commitDelayHelp: "Maximum time acknowledged writes wait to join an atomic batch before commit." }, clientKeys: { title: "Client key defaults", rpmLimit: "Default RPM", rpmLimitHelp: "Requests per minute assigned to newly created client keys.", maxConcurrent: "Default concurrency", maxConcurrentHelp: "Concurrent requests assigned to newly created client keys." }, accounts: { title: "Account maintenance", invalidationTitle: "Automatic interception", botRiskSchedulingTitle: "Bot-risk scheduling", excludeBuildBotFlaggedFromScheduling: "Exclude bot-risk Build accounts from scheduling", excludeBuildBotFlaggedFromSchedulingHelp: "When enabled, Grok Build accounts whose JWT bot_flag_source/bfs is 1 or 2 are removed from Build scheduling only. Linked Web/Console accounts keep their normal scheduling. Accounts remain in the pool for viewing and manual actions.", cleanupTitle: "Account cleanup", autoCleanReauthEnabled: "Automatic invalid-account cleanup", autoCleanReauthEnabledHelp: "Permanently deletes invalid accounts after the configured retention period. Accounts with active requests or video jobs are skipped.", autoCleanReauthInterval: "Cleanup interval", autoCleanReauthIntervalHelp: "How often to check for eligible accounts, from 1 minute to 1 hour.", autoCleanReauthMinAge: "Minimum retention", autoCleanReauthMinAgeHelp: "How long to retain an account after it is marked for reauthorization.", autoCleanIncludeDisabled: "Include disabled accounts", autoCleanIncludeDisabledHelp: "Includes disabled accounts that require reauthorization in automatic cleanup.", autoCleanEnableTitle: "Enable automatic cleanup?", autoCleanEnableDescription: "Eligible accounts will be permanently deleted and cannot be recovered. The first run starts after one full cleanup interval.", autoCleanIncludeDisabledTitle: "Include disabled accounts?", autoCleanIncludeDisabledDescription: "Disabled accounts that require reauthorization will also be permanently deleted. This action cannot be undone.", autoCleanConfirm: "Enable" }, units: { seconds: "Seconds", minutes: "Minutes", hours: "Hours", days: "Days" } }, egressProxyProfiles: { title: "Proxy address library", libraryTitle: "Proxy address library", libraryDescription: "Manage reusable proxy addresses in one place. Changes sync to every node using the address.", description: "Save proxy addresses that need to be reused across nodes.", add: "Add proxy address", addTitle: "Add proxy address", editTitle: "Edit proxy address", dialogDescription: "Changing the proxy address updates every bound node. Capacity and operational state remain isolated per node.", name: "Address name", endpoint: "Proxy address", nodes: "Used by", search: "Search address names", empty: "No proxy addresses", emptyLibrary: "The library is empty. Add an address to reuse it across nodes.", noMatches: "No matching proxy addresses", loadingSelection: "Loading selected address…", selectionUnavailable: "Proxy address #{{id}} is unavailable", saved: "Proxy address saved", deleted: "Proxy address deleted", deleteTitle: "Delete proxy address?", deleteDescription: "This deletes {{name}}. Addresses currently used by nodes cannot be deleted.", deleteBlocked: "Used by {{count}} nodes", assignment: "Proxy address source", assignmentHelp: "Select an address from the library to reuse it across Provider nodes. Health, capacity, User-Agent, and Clearance remain isolated per node.", independent: "Configure this node separately", managedByProfile: "Managed by the proxy address library", nodeCount: "{{count}} nodes", backToLibrary: "Back to address library", createFromPicker: "Add and use a proxy address", refreshNodes: "Refresh proxy nodes", reveal: "Reveal full proxy URL", hide: "Hide full proxy URL", revealUnavailable: "Only a saved proxy address can reveal its URL" }, settingsBuildTransport: { responseHeaderTimeout: "Response header timeout", responseHeaderTimeoutHelp: "Maximum time to wait for the first Grok Build response headers after the request body is sent.", streamIdleTimeout: "Stream idle timeout", streamIdleTimeoutHelp: "Maximum wait for useful generated content or tool progress. Keepalives and private control events do not extend the deadline." }, From d76070aa4e15c82229b61d9b82481cd607ce0a85 Mon Sep 17 00:00:00 2001 From: Jeik Date: Thu, 20 Aug 2026 10:41:28 +0800 Subject: [PATCH 3/4] fix(frontend): use DialogDescription asChild to prevent DOM nesting warning --- .../audits/request-audit-detail-dialog.tsx | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/frontend/src/features/audits/request-audit-detail-dialog.tsx b/frontend/src/features/audits/request-audit-detail-dialog.tsx index fcc5c4482..a9431a9c3 100644 --- a/frontend/src/features/audits/request-audit-detail-dialog.tsx +++ b/frontend/src/features/audits/request-audit-detail-dialog.tsx @@ -72,26 +72,28 @@ export function RequestAuditDetailDialog({ /> ) : null}
    - - - {activeAudit?.requestId} - - {activeAudit?.clientIp ? ( - - - {activeAudit.clientIp} - - ) : null} - {activeAudit?.operation ? ( - - {activeAudit.operation} - - ) : null} - {activeAudit ? ( - - {formatDateTime(activeAudit.createdAt, i18n.language)} + +
    + + {activeAudit?.requestId} - ) : null} + {activeAudit?.clientIp ? ( + + + {activeAudit.clientIp} + + ) : null} + {activeAudit?.operation ? ( + + {activeAudit.operation} + + ) : null} + {activeAudit ? ( + + {formatDateTime(activeAudit.createdAt, i18n.language)} + + ) : null} +
    From 1316ed730be89d19b2099c8742f6f172373a6c58 Mon Sep 17 00:00:00 2001 From: Chenyme <118253778+chenyme@users.noreply.github.com> Date: Thu, 20 Aug 2026 19:45:35 +0800 Subject: [PATCH 4/4] feat: add privacy-safe request diagnostics and bounded retention --- backend/internal/app/application.go | 4 +- backend/internal/application/audit/service.go | 90 +- .../application/audit/service_test.go | 35 + backend/internal/application/gateway/image.go | 24 +- .../internal/application/gateway/service.go | 28 +- .../application/gateway/service_test.go | 8 +- backend/internal/application/gateway/video.go | 2 +- backend/internal/application/gateway/voice.go | 29 +- .../internal/application/settings/service.go | 35 +- .../application/settings/service_test.go | 44 + backend/internal/domain/audit/audit.go | 2 - backend/internal/domain/settings/settings.go | 20 +- backend/internal/infra/config/config.go | 9 +- backend/internal/infra/config/config_test.go | 21 + .../relational/audit_repository.go | 74 +- .../relational/audit_repository_test.go | 70 +- .../infra/persistence/relational/mapping.go | 2 +- .../infra/persistence/relational/models.go | 2 - .../internal/transport/http/audit/handler.go | 14 +- .../transport/http/inference/handler.go | 244 +- .../http/inference/openai_audio_handler.go | 3 +- .../transport/http/inference/voice_handler.go | 10 +- .../transport/http/settings/handler.go | 24 +- config.example.yaml | 2 + frontend/package-lock.json | 5131 ----------------- .../audits/request-audit-detail-dialog.tsx | 490 +- .../src/features/audits/request-audits-api.ts | 8 +- .../src/features/settings/settings-api.ts | 6 +- .../src/features/settings/settings-model.ts | 6 - .../src/features/settings/settings-page.tsx | 39 - frontend/src/shared/i18n/index.ts | 62 +- frontend/vite.config.ts | 1 - start.bat | 48 - stop.bat | 22 - 34 files changed, 520 insertions(+), 6089 deletions(-) delete mode 100644 frontend/package-lock.json delete mode 100644 start.bat delete mode 100644 stop.bat diff --git a/backend/internal/app/application.go b/backend/internal/app/application.go index 17f6b12e0..a447b0efd 100644 --- a/backend/internal/app/application.go +++ b/backend/internal/app/application.go @@ -595,8 +595,8 @@ func (a *Application) Run(ctx context.Context) error { startBackground("audit_retention_cleanup", func(taskCtx context.Context) error { a.runPeriodicTask(taskCtx, time.Hour, "audit_retention_cleanup", func(runCtx context.Context) error { retentionDays := a.settings.Get().Config.Audit.RetentionDays - if retentionDays <= 0 { - retentionDays = 7 + if retentionDays == 0 { + return nil } _, err := a.audits.PurgeOutdated(runCtx, retentionDays) return err diff --git a/backend/internal/application/audit/service.go b/backend/internal/application/audit/service.go index 81152301d..242fbc7ad 100644 --- a/backend/internal/application/audit/service.go +++ b/backend/internal/application/audit/service.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "log/slog" + "sort" "strconv" "strings" "sync" @@ -67,13 +68,20 @@ const ( ) const ( - auditEnqueueWait = 25 * time.Millisecond - auditWriteTimeout = 2 * time.Second - auditWriteAttempts = 3 - auditWriteRetryBase = 250 * time.Millisecond - auditWriteRetryMax = 5 * time.Second - auditDefaultCommitDelay = 5 * time.Millisecond - auditSummaryTTL = 10 * time.Second + auditEnqueueWait = 25 * time.Millisecond + auditWriteTimeout = 2 * time.Second + auditWriteAttempts = 3 + auditWriteRetryBase = 250 * time.Millisecond + auditWriteRetryMax = 5 * time.Second + auditDefaultCommitDelay = 5 * time.Millisecond + auditSummaryTTL = 10 * time.Second + requestMethodLimit = 16 + requestPathLimit = 2048 + requestHeaderNameLimit = 256 + requestHeaderValueLimit = 2048 + requestHeaderValuesLimit = 32 + requestHeaderCountLimit = 128 + requestHeadersLimit = 32 << 10 ) type auditWriteRequest struct { @@ -244,7 +252,7 @@ func (s *Service) Start() { // Record 将审计写入有界队列;突发满载时短暂等待,持续拥塞才降级丢弃审计。 func (s *Service) Record(value auditdomain.Record) bool { - return s.enqueueBestEffort(context.Background(), auditWriteRequest{record: value}) == nil + return s.enqueueBestEffort(context.Background(), auditWriteRequest{record: sanitizeRequestMetadata(value)}) == nil } // Create returns success only after the audit and billing transaction commits. @@ -265,6 +273,7 @@ func (s *Service) createAcknowledged(ctx context.Context, value auditdomain.Reco if value.ClientIP == "" { value.ClientIP = requestmeta.ClientIP(ctx) } + value = sanitizeRequestMetadata(value) if !s.started.Load() || s.stopped.Load() { return ErrWriterUnavailable } @@ -290,6 +299,70 @@ func (s *Service) createAcknowledged(ctx context.Context, value auditdomain.Reco } } +func sanitizeRequestMetadata(value auditdomain.Record) auditdomain.Record { + // Query strings can contain credentials or signed resource URLs. Endpoint + // identity only needs the path. + value.RequestMethod = truncateRequestMetadata(strings.ToUpper(strings.TrimSpace(value.RequestMethod)), requestMethodLimit) + value.RequestPath = truncateRequestMetadata(strings.SplitN(value.RequestPath, "?", 2)[0], requestPathLimit) + if len(value.RequestHeaders) == 0 { + value.RequestHeaders = nil + return value + } + names := make([]string, 0, len(value.RequestHeaders)) + for name := range value.RequestHeaders { + names = append(names, name) + } + sort.Strings(names) + result := make(map[string][]string, len(value.RequestHeaders)) + for _, originalName := range names { + if len(result) >= requestHeaderCountLimit { + break + } + name := truncateRequestMetadata(strings.TrimSpace(originalName), requestHeaderNameLimit) + if name == "" { + continue + } + values := value.RequestHeaders[originalName] + cloned := make([]string, 0, len(values)) + if isSensitiveRequestHeader(name) { + cloned = []string{"[REDACTED]"} + } else { + for _, item := range values[:min(len(values), requestHeaderValuesLimit)] { + cloned = append(cloned, truncateRequestMetadata(item, requestHeaderValueLimit)) + } + } + result[name] = cloned + encoded, err := json.Marshal(result) + if err != nil || len(encoded) > requestHeadersLimit { + delete(result, name) + break + } + } + value.RequestHeaders = result + return value +} + +func truncateRequestMetadata(value string, limit int) string { + value = strings.ToValidUTF8(value, "�") + runes := []rune(value) + if len(runes) <= limit { + return value + } + return string(runes[:limit]) +} + +func isSensitiveRequestHeader(name string) bool { + name = strings.ToLower(strings.TrimSpace(name)) + for _, marker := range []string{ + "auth", "cookie", "credential", "dpop", "jwt", "key", "password", "secret", "session", "signature", "token", + } { + if strings.Contains(name, marker) { + return true + } + } + return false +} + func (s *Service) tryEnqueue(request auditWriteRequest) error { s.lifecycleMu.RLock() defer s.lifecycleMu.RUnlock() @@ -414,7 +487,6 @@ func (s *Service) PurgeOutdated(ctx context.Context, retentionDays int) (int64, return s.audits.PurgeOlderThan(ctx, cutoff) } - // CursorResult 表示按递减 ID 游标读取的一页审计记录。 type CursorResult struct { Items []auditdomain.Record diff --git a/backend/internal/application/audit/service_test.go b/backend/internal/application/audit/service_test.go index 6cce705ed..646c4b17f 100644 --- a/backend/internal/application/audit/service_test.go +++ b/backend/internal/application/audit/service_test.go @@ -6,6 +6,7 @@ import ( "fmt" "log/slog" "path/filepath" + "strings" "sync" "sync/atomic" "testing" @@ -70,6 +71,40 @@ func TestCreateCapturesClientIPFromRequestContext(t *testing.T) { } } +func TestCreateKeepsOnlySanitizedRequestMetadata(t *testing.T) { + repo := newGatedAuditRepository() + close(repo.release) + service := NewService(repo, slog.Default(), 8, 1, time.Hour) + service.Start() + t.Cleanup(func() { closeAuditService(t, service) }) + + record := auditdomain.Record{ + EventID: "evt_payload_policy_0001", RequestID: "payload-policy", ClientKeyID: 1, ModelRouteID: 1, StatusCode: 200, + RequestMethod: "POST", RequestPath: "/v1/responses?api_key=query-secret", + RequestHeaders: map[string][]string{ + "Authorization": {"Bearer client-secret"}, + "X-Api-Key": {"client-key"}, + "X-Goog-Api-Key": {"google-key"}, + "X-Session-ID": {"session-secret"}, + "User-Agent": {"audit-test"}, + "X-Oversized": {strings.Repeat("x", requestHeaderValueLimit+100)}, + }, + } + if err := service.Create(context.Background(), record); err != nil { + t.Fatal(err) + } + stored := <-repo.started + if len(stored) != 1 || stored[0].RequestMethod != "POST" || stored[0].RequestPath != "/v1/responses" { + t.Fatalf("request metadata = %#v", stored) + } + if stored[0].RequestHeaders["Authorization"][0] != "[REDACTED]" || stored[0].RequestHeaders["X-Api-Key"][0] != "[REDACTED]" || stored[0].RequestHeaders["X-Goog-Api-Key"][0] != "[REDACTED]" || stored[0].RequestHeaders["X-Session-ID"][0] != "[REDACTED]" || stored[0].RequestHeaders["User-Agent"][0] != "audit-test" { + t.Fatalf("sanitized request headers = %#v", stored[0].RequestHeaders) + } + if len([]rune(stored[0].RequestHeaders["X-Oversized"][0])) != requestHeaderValueLimit { + t.Fatalf("oversized header was not bounded: %d", len([]rune(stored[0].RequestHeaders["X-Oversized"][0]))) + } +} + func TestAuditBatchRetriesTransientDatabaseFailure(t *testing.T) { repo := &flakyAuditRepository{failures: 5} service := NewService(repo, slog.Default(), 8, 4, time.Hour) diff --git a/backend/internal/application/gateway/image.go b/backend/internal/application/gateway/image.go index 005da5450..258a05da4 100644 --- a/backend/internal/application/gateway/image.go +++ b/backend/internal/application/gateway/image.go @@ -31,7 +31,6 @@ type ImageGenerationInput struct { ResponseFormat string Streaming bool PartialImages int - RequestBody string Method string Path string Headers map[string][]string @@ -52,7 +51,6 @@ type ImageEditInput struct { ResponseFormat string Streaming bool PartialImages int - RequestBody string Method string Path string Headers map[string][]string @@ -77,7 +75,7 @@ func (s *Service) GenerateImage(ctx context.Context, input ImageGenerationInput) Size: input.Size, AspectRatio: input.AspectRatio, Resolution: input.Resolution, Quality: input.Quality, ResponseFormat: input.ResponseFormat, Streaming: input.Streaming, PartialImages: input.PartialImages, }) - }, input.Streaming, input.Resolution, input.Quality, input.Count, 0, input.RequestBody, input.Method, input.Path, input.Headers) + }, input.Streaming, input.Resolution, input.Quality, input.Count, 0, input.Method, input.Path, input.Headers) } // EditImage 选择支持图片编辑的路由和账号,并返回可统一审计的上游响应。 @@ -96,7 +94,7 @@ func (s *Service) EditImage(ctx context.Context, input ImageEditInput) (*Result, Resolution: input.Resolution, Quality: input.Quality, ResponseFormat: input.ResponseFormat, Streaming: input.Streaming, PartialImages: input.PartialImages, }) - }, input.Streaming, input.Resolution, input.Quality, input.Count, len(input.ImageURLs), input.RequestBody, input.Method, input.Path, input.Headers) + }, input.Streaming, input.Resolution, input.Quality, input.Count, len(input.ImageURLs), input.Method, input.Path, input.Headers) } func (s *Service) executeImage( @@ -113,7 +111,6 @@ func (s *Service) executeImage( quality string, requestedCount int, inputImageCount int, - requestBody string, method string, path string, headers map[string][]string, @@ -142,10 +139,7 @@ func (s *Service) executeImage( ClientIP: requestmeta.ClientIP(ctx), ModelRouteID: route.ID, ModelPublicID: externalModel, ModelUpstreamModel: modeldomain.DisplayUpstreamModel(route.Provider, route.UpstreamModel), Provider: string(route.Provider), Operation: operation, UsageSource: audit.UsageSourceNone, Streaming: streaming, - RequestMethod: method, - RequestPath: path, - RequestHeaders: headers, - RequestBody: requestBody, + RequestMethod: method, RequestPath: path, RequestHeaders: headers, } if operation == audit.OperationImageEdit { auditBase.MediaInputImages = int64(max(0, inputImageCount)) @@ -303,7 +297,7 @@ func (s *Service) executeImage( effectiveQuotaMode := lease.QuotaMode accountID := credential.ID var once sync.Once - finalizeWithBody := func(_ Usage, _ string, errorCode string, responseBody string) { + finalize := func(_ Usage, _ string, errorCode string) { once.Do(func() { successful := auditRequestSucceeded(response.StatusCode, errorCode) lease.completeSelectorObservation(successful) @@ -312,7 +306,6 @@ func (s *Service) executeImage( record := auditBase record.AccountID, record.AccountName, record.StatusCode = &accountID, credential.Name, response.StatusCode record.ErrorCode = errorCode - record.ResponseBody = responseBody record.DurationMS, record.CreatedAt = time.Since(startedAt).Milliseconds(), time.Now().UTC() applyAuditEgress(&record, egressTrace, route.Provider) if successful { @@ -357,15 +350,8 @@ func (s *Service) executeImage( } }) } - finalize := func(usage Usage, responseID string, errorCode string) { - finalizeWithBody(usage, responseID, errorCode, "") - } finalizationOwnsReservation = true - return &Result{ - StatusCode: response.StatusCode, Status: response.Status, Header: response.Header, - Body: &finalizingBody{ReadCloser: response.Body, finalize: func() { finalize(Usage{}, "", "stream_closed") }}, - Finalize: finalize, FinalizeWithBody: finalizeWithBody, - }, nil + return &Result{StatusCode: response.StatusCode, Status: response.Status, Header: response.Header, Body: &finalizingBody{ReadCloser: response.Body, finalize: func() { finalize(Usage{}, "", "stream_closed") }}, Finalize: finalize}, nil } // quotaFinalizationModes separates the immediate local consumption fence from diff --git a/backend/internal/application/gateway/service.go b/backend/internal/application/gateway/service.go index c587a0111..3fae96164 100644 --- a/backend/internal/application/gateway/service.go +++ b/backend/internal/application/gateway/service.go @@ -115,12 +115,9 @@ type Input struct { // GrokTurnIndex forwards only the turn supplied by a real Grok Shell client; the server never infers or increments it. GrokTurnIndex string Operation audit.Operation - // Method is the client HTTP request method. - Method string - // Path is the client HTTP request path. - Path string - // Headers is the client HTTP request headers. - Headers map[string][]string + Method string + Path string + Headers map[string][]string // auditOperation may classify a normal protocol request differently for // operator visibility without changing routing or Provider semantics. auditOperation audit.Operation @@ -164,7 +161,6 @@ type Result struct { MarkFirstToken func() RecordStreamFailure func(StreamFailureDiagnostic) Finalize func(usage Usage, responseID, errorCode string) - FinalizeWithBody func(usage Usage, responseID, errorCode, responseBody string) } // StreamFailureDiagnostic safely projects a failure termination event returned in-stream after downstream 2xx headers. @@ -960,10 +956,7 @@ func (s *Service) createResponseAt(ctx context.Context, input Input, path string ModelRouteID: route.ID, ModelPublicID: publicModel, ModelUpstreamModel: modeldomain.DisplayUpstreamModel(route.Provider, route.UpstreamModel), Provider: string(route.Provider), Operation: auditOperation, UsageSource: audit.UsageSourceNone, Streaming: input.Streaming, MediaInputImages: mediaSummary.InputImages, - RequestMethod: input.Method, - RequestPath: input.Path, - RequestHeaders: input.Headers, - RequestBody: string(input.Body), + RequestMethod: input.Method, RequestPath: input.Path, RequestHeaders: input.Headers, } if errors.Is(routeErr, clientkeyapp.ErrModelNotAllowed) { record := auditBase @@ -1072,7 +1065,7 @@ func (s *Service) createResponseAt(ctx context.Context, input Input, path string handoffResponse := func(response *provider.Response, lease *accountLease, credential accountdomain.Credential, upstreamStartedAt time.Time) *Result { accountID := credential.ID var once sync.Once - finalizeWithBody := func(usage Usage, responseID, errorCode, responseBody string) { + finalize := func(usage Usage, responseID, errorCode string) { once.Do(func() { // HTTP 状态码保留线上真实值;流在 2xx 响应头之后失败时由 errorCode // 决定最终结果,避免把协议状态与业务结果混为一谈。 @@ -1125,7 +1118,6 @@ func (s *Service) createResponseAt(ctx context.Context, input Input, path string } record.DurationMS = time.Since(startedAt).Milliseconds() record.ErrorCode = errorCode - record.ResponseBody = responseBody attempts := failureAttempts.snapshot() if !successful && len(attempts) == 0 { statusCode := response.StatusCode @@ -1197,9 +1189,6 @@ func (s *Service) createResponseAt(ctx context.Context, input Input, path string timing.finish(s.logger, outcome) }) } - finalize := func(usage Usage, responseID, errorCode string) { - finalizeWithBody(usage, responseID, errorCode, "") - } response.Body = &firstByteReadCloser{ReadCloser: response.Body, mark: timing.markFirstBody} recordStreamFailure := func(diagnostic StreamFailureDiagnostic) { failureAttempts.captureStreamFailure(credential, upstreamStartedAt, response, diagnostic) @@ -1209,12 +1198,7 @@ func (s *Service) createResponseAt(ctx context.Context, input Input, path string markFirstToken = firstToken.mark } timingHandedOff = true - return &Result{ - StatusCode: response.StatusCode, Status: response.Status, Header: response.Header, - Body: &finalizingBody{ReadCloser: response.Body, finalize: func() { finalize(Usage{}, "", "stream_closed") }}, - MarkFirstToken: markFirstToken, RecordStreamFailure: recordStreamFailure, - Finalize: finalize, FinalizeWithBody: finalizeWithBody, - } + return &Result{StatusCode: response.StatusCode, Status: response.Status, Header: response.Header, Body: &finalizingBody{ReadCloser: response.Body, finalize: func() { finalize(Usage{}, "", "stream_closed") }}, MarkFirstToken: markFirstToken, RecordStreamFailure: recordStreamFailure, Finalize: finalize} } // fail_open retains at most one successful no-thinking stream. The account // lease is released immediately; the read pump applies upstream backpressure diff --git a/backend/internal/application/gateway/service_test.go b/backend/internal/application/gateway/service_test.go index 8e59945e6..9e749ef8c 100644 --- a/backend/internal/application/gateway/service_test.go +++ b/backend/internal/application/gateway/service_test.go @@ -1234,7 +1234,7 @@ func TestUnpricedVoiceRemainsAvailableToFiniteClientKey(t *testing.T) { accountService := accountapp.NewService(accountRepo, auditRepo, memory.NewDeviceSessionStore(), sticky, registry, testCipher(t), nil) service := NewService(modelRepo, auditRepo, accountService, clientkeyapp.NewService(nil, nil, nil, 60, 4, nil), registry, selector, nil, 1) executed := false - result, err := service.executeVoice(ctx, "req-voice-billing", clientkey.Key{ID: 1, BillingLimitUSDTicks: 1}, voiceModel, audit.OperationTTS, modeldomain.CapabilityTTS, true, audit.PricingResult{}, "", "", "", nil, func(account.Provider) bool { + result, err := service.executeVoice(ctx, "req-voice-billing", clientkey.Key{ID: 1, BillingLimitUSDTicks: 1}, voiceModel, audit.OperationTTS, modeldomain.CapabilityTTS, true, audit.PricingResult{}, "", "", nil, func(account.Provider) bool { return true }, func(context.Context, account.Provider, account.Credential, string) (voiceExecutionResult, error) { executed = true @@ -1305,7 +1305,7 @@ func TestVoicePricingSettlesTTSAndRESTSTTUsage(t *testing.T) { if !ok { t.Fatal("TTS pricing unavailable") } - ttsResult, err := service.executeVoice(ctx, "req-priced-tts", limitedKey, ttsModel, audit.OperationTTS, modeldomain.CapabilityTTS, true, ttsPricing, "", "", "", nil, func(account.Provider) bool { + ttsResult, err := service.executeVoice(ctx, "req-priced-tts", limitedKey, ttsModel, audit.OperationTTS, modeldomain.CapabilityTTS, true, ttsPricing, "", "", nil, func(account.Provider) bool { return true }, func(context.Context, account.Provider, account.Credential, string) (voiceExecutionResult, error) { return voiceExecutionResult{response: jsonVoiceResponse(http.StatusOK, map[string]any{"ok": true}), pricing: ttsPricing}, nil @@ -1328,7 +1328,7 @@ func TestVoicePricingSettlesTTSAndRESTSTTUsage(t *testing.T) { if !ok { t.Fatal("STT pricing unavailable") } - sttResult, err := service.executeVoice(ctx, "req-priced-stt", limitedKey, sttModel, audit.OperationSTT, modeldomain.CapabilitySTT, true, audit.PricingResult{}, "", "", "", nil, func(account.Provider) bool { + sttResult, err := service.executeVoice(ctx, "req-priced-stt", limitedKey, sttModel, audit.OperationSTT, modeldomain.CapabilitySTT, true, audit.PricingResult{}, "", "", nil, func(account.Provider) bool { return true }, func(context.Context, account.Provider, account.Credential, string) (voiceExecutionResult, error) { return voiceExecutionResult{response: jsonVoiceResponse(http.StatusOK, map[string]any{"text": "hello"}), pricing: sttPricing}, nil @@ -1369,7 +1369,7 @@ func TestVoicePricingSettlesTTSAndRESTSTTUsage(t *testing.T) { t.Fatal(err) } executed := false - _, err = service.executeVoice(ctx, "req-capped-tts", cappedKey, ttsModel, audit.OperationTTS, modeldomain.CapabilityTTS, true, ttsPricing, "", "", "", nil, func(account.Provider) bool { + _, err = service.executeVoice(ctx, "req-capped-tts", cappedKey, ttsModel, audit.OperationTTS, modeldomain.CapabilityTTS, true, ttsPricing, "", "", nil, func(account.Provider) bool { return true }, func(context.Context, account.Provider, account.Credential, string) (voiceExecutionResult, error) { executed = true diff --git a/backend/internal/application/gateway/video.go b/backend/internal/application/gateway/video.go index 63e1cf90e..aff49176a 100644 --- a/backend/internal/application/gateway/video.go +++ b/backend/internal/application/gateway/video.go @@ -993,8 +993,8 @@ func (s *Service) recordVideoAudit(ctx context.Context, job media.Job, durationM AccountID: accountID, AccountName: job.AccountName, StatusCode: statusCode, ErrorCode: job.ErrorCode, EgressNodeID: job.EgressNodeID, EgressNodeName: job.EgressNodeName, EgressScope: job.EgressScope, EgressMode: audit.EgressMode(job.EgressMode), MediaInputImages: int64(job.InputImageCount), - RequestBody: job.InputJSON, DurationMS: durationMS, AttemptCount: len(attempts), Attempts: append([]audit.Attempt(nil), attempts...), CreatedAt: createdAt, + RequestMethod: http.MethodPost, RequestPath: "/v1/videos/generations", } if job.Status == media.StatusCompleted && job.Seconds > 0 { record.MediaOutputSeconds = int64(max(0, job.Seconds)) diff --git a/backend/internal/application/gateway/voice.go b/backend/internal/application/gateway/voice.go index 75aed96e0..dd714e475 100644 --- a/backend/internal/application/gateway/voice.go +++ b/backend/internal/application/gateway/voice.go @@ -35,7 +35,6 @@ type TTSInput struct { OptimizeStreamingLatency int TextNormalization bool WithTimestamps bool - RequestBody string Method string Path string Headers map[string][]string @@ -62,7 +61,6 @@ type STTInput struct { // ResponseFormat is empty for the native Console-compatible response, or an // OpenAI-compatible format normalized by the HTTP transport. ResponseFormat string - RequestBody string Method string Path string Headers map[string][]string @@ -90,7 +88,7 @@ type voiceExecutionResult struct { func (s *Service) SynthesizeSpeech(ctx context.Context, input TTSInput) (*Result, error) { reservation, _ := audit.EstimateOfficialTTSCost(input.Text) - return s.executeVoice(ctx, input.RequestID, input.ClientKey, input.PublicModel, audit.OperationTTS, modeldomain.CapabilityTTS, true, reservation, input.RequestBody, input.Method, input.Path, input.Headers, func(providerValue accountdomain.Provider) bool { + return s.executeVoice(ctx, input.RequestID, input.ClientKey, input.PublicModel, audit.OperationTTS, modeldomain.CapabilityTTS, true, reservation, input.Method, input.Path, input.Headers, func(providerValue accountdomain.Provider) bool { _, ok := s.providers.TTS(providerValue) return ok }, func(executionCtx context.Context, providerValue accountdomain.Provider, credential accountdomain.Credential, upstream string) (voiceExecutionResult, error) { @@ -129,12 +127,13 @@ func (s *Service) SynthesizeSpeech(ctx context.Context, input TTSInput) (*Result Status: fmt.Sprintf("%d %s", http.StatusOK, http.StatusText(http.StatusOK)), Header: header, Body: io.NopCloser(bytes.NewReader(result.Audio)), + QuotaUnits: 1, }, pricing: reservation}, nil }) } func (s *Service) ListTTSVoices(ctx context.Context, input VoiceListInput) (*Result, error) { - return s.executeVoice(ctx, input.RequestID, input.ClientKey, input.PublicModel, audit.OperationTTS, modeldomain.CapabilityTTS, false, audit.PricingResult{}, "", "", "", nil, func(providerValue accountdomain.Provider) bool { + return s.executeVoice(ctx, input.RequestID, input.ClientKey, input.PublicModel, audit.OperationTTS, modeldomain.CapabilityTTS, false, audit.PricingResult{}, "", "", nil, func(providerValue accountdomain.Provider) bool { _, ok := s.providers.TTS(providerValue) return ok }, func(executionCtx context.Context, providerValue accountdomain.Provider, credential accountdomain.Credential, _ string) (voiceExecutionResult, error) { @@ -163,7 +162,7 @@ func (s *Service) ListTTSVoices(ctx context.Context, input VoiceListInput) (*Res } func (s *Service) GetTTSVoice(ctx context.Context, input VoiceIDInput) (*Result, error) { - return s.executeVoice(ctx, input.RequestID, input.ClientKey, input.PublicModel, audit.OperationTTS, modeldomain.CapabilityTTS, false, audit.PricingResult{}, "", "", "", nil, func(providerValue accountdomain.Provider) bool { + return s.executeVoice(ctx, input.RequestID, input.ClientKey, input.PublicModel, audit.OperationTTS, modeldomain.CapabilityTTS, false, audit.PricingResult{}, "", "", nil, func(providerValue accountdomain.Provider) bool { _, ok := s.providers.TTS(providerValue) return ok }, func(executionCtx context.Context, providerValue accountdomain.Provider, credential accountdomain.Credential, _ string) (voiceExecutionResult, error) { @@ -188,7 +187,7 @@ func (s *Service) GetTTSVoice(ctx context.Context, input VoiceIDInput) (*Result, } func (s *Service) TranscribeSpeech(ctx context.Context, input STTInput) (*Result, error) { - return s.executeVoice(ctx, input.RequestID, input.ClientKey, input.PublicModel, audit.OperationSTT, modeldomain.CapabilitySTT, true, audit.PricingResult{}, input.RequestBody, input.Method, input.Path, input.Headers, func(providerValue accountdomain.Provider) bool { + return s.executeVoice(ctx, input.RequestID, input.ClientKey, input.PublicModel, audit.OperationSTT, modeldomain.CapabilitySTT, true, audit.PricingResult{}, input.Method, input.Path, input.Headers, func(providerValue accountdomain.Provider) bool { _, ok := s.providers.STT(providerValue) return ok }, func(executionCtx context.Context, providerValue accountdomain.Provider, credential accountdomain.Credential, upstream string) (voiceExecutionResult, error) { @@ -284,7 +283,6 @@ func (s *Service) executeVoice( capability modeldomain.Capability, consumesQuota bool, reservation audit.PricingResult, - requestBody string, method string, path string, headers map[string][]string, @@ -314,10 +312,7 @@ func (s *Service) executeVoice( ClientIP: requestmeta.ClientIP(ctx), ModelRouteID: route.ID, ModelPublicID: externalModel, ModelUpstreamModel: modeldomain.DisplayUpstreamModel(route.Provider, route.UpstreamModel), Provider: string(route.Provider), Operation: operation, UsageSource: audit.UsageSourceNone, - RequestMethod: method, - RequestPath: path, - RequestHeaders: headers, - RequestBody: requestBody, + RequestMethod: method, RequestPath: path, RequestHeaders: headers, } if err := s.checkLedgerReady(); err != nil { return nil, err @@ -475,7 +470,7 @@ func (s *Service) executeVoice( } accountID := credential.ID var once sync.Once - finalizeWithBody := func(_ Usage, _ string, errorCode string, responseBody string) { + finalize := func(_ Usage, _ string, errorCode string) { once.Do(func() { successful := auditRequestSucceeded(response.StatusCode, errorCode) lease.completeSelectorObservation(successful) @@ -484,7 +479,6 @@ func (s *Service) executeVoice( record := auditBase record.AccountID, record.AccountName, record.StatusCode = &accountID, credential.Name, response.StatusCode record.ErrorCode = errorCode - record.ResponseBody = responseBody record.DurationMS, record.CreatedAt = time.Since(startedAt).Milliseconds(), time.Now().UTC() applyAuditEgress(&record, egressTrace, route.Provider) if successful && completedPricing.CostInUSDTicks > 0 { @@ -518,14 +512,7 @@ func (s *Service) executeVoice( } }) } - finalize := func(usage Usage, responseID string, errorCode string) { - finalizeWithBody(usage, responseID, errorCode, "") - } - return &Result{ - StatusCode: response.StatusCode, Status: response.Status, Header: response.Header, - Body: &finalizingBody{ReadCloser: response.Body, finalize: func() { finalize(Usage{}, "", "stream_closed") }}, - Finalize: finalize, FinalizeWithBody: finalizeWithBody, - }, nil + return &Result{StatusCode: response.StatusCode, Status: response.Status, Header: response.Header, Body: &finalizingBody{ReadCloser: response.Body, finalize: func() { finalize(Usage{}, "", "stream_closed") }}, Finalize: finalize}, nil } func jsonVoiceResponse(status int, value any) *provider.Response { diff --git a/backend/internal/application/settings/service.go b/backend/internal/application/settings/service.go index 8ac8fa9b9..bafcdc9f7 100644 --- a/backend/internal/application/settings/service.go +++ b/backend/internal/application/settings/service.go @@ -119,16 +119,12 @@ type SegmentedSelectorConfig struct { // AuditConfig 是管理接口使用的审计可编辑输入。 type AuditConfig struct { - BufferSize int - BatchSize int - FlushInterval string - CommitDelayMS int - RetentionDays int - RetentionDaysProvided bool - RecordRequestBody bool - RecordRequestBodyProvided bool - RecordResponseBody bool - RecordResponseBodyProvided bool + BufferSize int + BatchSize int + FlushInterval string + CommitDelayMS int + RetentionDays int + RetentionDaysProvided bool } // ClientKeyDefaultsConfig 是管理接口使用的密钥默认限制输入。 @@ -410,14 +406,13 @@ func applyDomainConfig(base config.Config, value settingsdomain.Config) config.C commitDelay = value.Audit.CommitDelay } retentionDays := base.Audit.RetentionDays - if value.Audit.RetentionDays > 0 { - retentionDays = value.Audit.RetentionDays + if value.Audit.RetentionDays != nil { + retentionDays = *value.Audit.RetentionDays } base.Audit = config.AuditConfig{ BufferSize: value.Audit.BufferSize, BatchSize: value.Audit.BatchSize, FlushInterval: config.Duration(value.Audit.FlushInterval), CommitDelay: config.Duration(commitDelay), RetentionDays: retentionDays, - RecordRequestBody: value.Audit.RecordRequestBody, RecordResponseBody: value.Audit.RecordResponseBody, - LedgerMode: base.Audit.LedgerMode, LedgerFailureThreshold: base.Audit.LedgerFailureThreshold, + LedgerMode: base.Audit.LedgerMode, LedgerFailureThreshold: base.Audit.LedgerFailureThreshold, LedgerUnhealthyGrace: base.Audit.LedgerUnhealthyGrace, LedgerQueueHighWatermarkPct: base.Audit.LedgerQueueHighWatermarkPct, } base.ClientKeyDefaults = config.ClientKeyDefaultsConfig{ @@ -493,7 +488,7 @@ func toDomainConfig(value config.Config) settingsdomain.Config { }, Audit: settingsdomain.AuditConfig{ BufferSize: value.Audit.BufferSize, BatchSize: value.Audit.BatchSize, FlushInterval: value.Audit.FlushInterval.Value(), CommitDelay: value.Audit.CommitDelay.Value(), - RetentionDays: value.Audit.RetentionDays, RecordRequestBody: value.Audit.RecordRequestBody, RecordResponseBody: value.Audit.RecordResponseBody, + RetentionDays: intPointer(value.Audit.RetentionDays), }, ClientKeyDefaults: settingsdomain.ClientKeyDefaultsConfig{ RPMLimit: value.ClientKeyDefaults.RPMLimit, MaxConcurrent: value.ClientKeyDefaults.MaxConcurrent, @@ -510,6 +505,8 @@ func toDomainConfig(value config.Config) settingsdomain.Config { } } +func intPointer(value int) *int { return &value } + func (s *Service) snapshotLocked() Snapshot { restartRequired := []string{} if s.cfg.Audit.BufferSize != s.activeBufferSize { @@ -589,12 +586,6 @@ func mergeEditable(current config.Config, input EditableConfig) (config.Config, if input.Audit.RetentionDaysProvided { next.Audit.RetentionDays = input.Audit.RetentionDays } - if input.Audit.RecordRequestBodyProvided { - next.Audit.RecordRequestBody = input.Audit.RecordRequestBody - } - if input.Audit.RecordResponseBodyProvided { - next.Audit.RecordResponseBody = input.Audit.RecordResponseBody - } next.ClientKeyDefaults.RPMLimit = input.ClientKeyDefaults.RPMLimit next.ClientKeyDefaults.MaxConcurrent = input.ClientKeyDefaults.MaxConcurrent if input.AccountsProvided { @@ -733,8 +724,6 @@ func toEditable(cfg config.Config) EditableConfig { Audit: AuditConfig{ BufferSize: cfg.Audit.BufferSize, BatchSize: cfg.Audit.BatchSize, FlushInterval: cfg.Audit.FlushInterval.String(), CommitDelayMS: int(cfg.Audit.CommitDelay.Value() / time.Millisecond), RetentionDays: cfg.Audit.RetentionDays, RetentionDaysProvided: true, - RecordRequestBody: cfg.Audit.RecordRequestBody, RecordRequestBodyProvided: true, - RecordResponseBody: cfg.Audit.RecordResponseBody, RecordResponseBodyProvided: true, }, ClientKeyDefaults: ClientKeyDefaultsConfig{RPMLimit: cfg.ClientKeyDefaults.RPMLimit, MaxConcurrent: cfg.ClientKeyDefaults.MaxConcurrent}, Accounts: AccountsConfig{ diff --git a/backend/internal/application/settings/service_test.go b/backend/internal/application/settings/service_test.go index cf7111781..9e068f8df 100644 --- a/backend/internal/application/settings/service_test.go +++ b/backend/internal/application/settings/service_test.go @@ -706,6 +706,50 @@ func TestUpdateAuditCommitDelayRoundTrip(t *testing.T) { } } +func TestUpdateAuditRetentionPreservesExplicitZero(t *testing.T) { + cfg := testConfig(t) + cfg.Audit.RetentionDays = 7 + repo := &runtimeSettingsRepositoryStub{} + var applied config.Config + service := NewService(cfg, time.Time{}, 0, repo, nil, func(next config.Config) { applied = next }) + input := service.Get().Config + input.Audit.RetentionDays = 0 + input.Audit.RetentionDaysProvided = true + + if _, err := service.Update(context.Background(), service.Get().Revision, input); err != nil { + t.Fatal(err) + } + if applied.Audit.RetentionDays != 0 { + t.Fatalf("applied audit policy = %#v", applied.Audit) + } + if repo.value.Audit.RetentionDays == nil || *repo.value.Audit.RetentionDays != 0 { + t.Fatalf("persisted audit policy = %#v", repo.value.Audit) + } + reloaded, _, _, err := LoadPersisted(context.Background(), cfg, repo) + if err != nil { + t.Fatal(err) + } + if reloaded.Audit.RetentionDays != 0 { + t.Fatalf("reloaded audit policy = %#v", reloaded.Audit) + } +} + +func TestLoadPersistedKeepsAuditDefaultsForOlderPayload(t *testing.T) { + cfg := testConfig(t) + cfg.Audit.RetentionDays = 30 + value := toDomainConfig(cfg) + value.Audit.RetentionDays = nil + repo := &runtimeSettingsRepositoryStub{value: value, found: true} + + loaded, _, _, err := LoadPersisted(context.Background(), cfg, repo) + if err != nil { + t.Fatal(err) + } + if loaded.Audit.RetentionDays != 30 { + t.Fatalf("legacy audit defaults = %#v", loaded.Audit) + } +} + func TestUpdateRejectsNegativeAuditCommitDelay(t *testing.T) { cfg := testConfig(t) service := NewService(cfg, time.Time{}, 0, &runtimeSettingsRepositoryStub{}, nil, nil) diff --git a/backend/internal/domain/audit/audit.go b/backend/internal/domain/audit/audit.go index 3c17f39ba..13fe4e064 100644 --- a/backend/internal/domain/audit/audit.go +++ b/backend/internal/domain/audit/audit.go @@ -114,8 +114,6 @@ type Record struct { RequestMethod string RequestPath string RequestHeaders map[string][]string - RequestBody string - ResponseBody string AttemptCount int Attempts []Attempt CreatedAt time.Time diff --git a/backend/internal/domain/settings/settings.go b/backend/internal/domain/settings/settings.go index b699ea07c..7acfaf850 100644 --- a/backend/internal/domain/settings/settings.go +++ b/backend/internal/domain/settings/settings.go @@ -98,10 +98,10 @@ type ProviderBuildConfig struct { // RoutingConfig 定义会话粘性、冷却和故障切换边界。 type RoutingConfig struct { - StickyTTL time.Duration - CooldownBase time.Duration - CooldownMax time.Duration - CapacityWait time.Duration + StickyTTL time.Duration + CooldownBase time.Duration + CooldownMax time.Duration + CapacityWait time.Duration MaxAttempts int VideoMaxAttempts int PreferFreeBuild bool @@ -120,13 +120,11 @@ type SegmentedSelectorConfig struct { } type AuditConfig struct { - BufferSize int - BatchSize int - FlushInterval time.Duration - CommitDelay time.Duration - RetentionDays int - RecordRequestBody bool - RecordResponseBody bool + BufferSize int + BatchSize int + FlushInterval time.Duration + CommitDelay time.Duration + RetentionDays *int } // ClientKeyDefaultsConfig 定义新建客户端密钥的默认限制。 diff --git a/backend/internal/infra/config/config.go b/backend/internal/infra/config/config.go index d4052380e..3cfa79d82 100644 --- a/backend/internal/infra/config/config.go +++ b/backend/internal/infra/config/config.go @@ -249,8 +249,6 @@ type AuditConfig struct { FlushInterval Duration `yaml:"flushInterval"` CommitDelay Duration `yaml:"commitDelay"` RetentionDays int `yaml:"retentionDays"` - RecordRequestBody bool `yaml:"recordRequestBody"` - RecordResponseBody bool `yaml:"recordResponseBody"` LedgerMode string `yaml:"ledgerMode"` LedgerFailureThreshold int `yaml:"ledgerFailureThreshold"` LedgerUnhealthyGrace Duration `yaml:"ledgerUnhealthyGrace"` @@ -691,6 +689,9 @@ func (c Config) Validate() error { if c.Audit.CommitDelay.Value() < minAuditCommitDelay || c.Audit.CommitDelay.Value() > maxAuditCommitDelay { return errors.New("audit.commitDelay 必须在 1ms 到 50ms 之间") } + if c.Audit.RetentionDays < 0 || c.Audit.RetentionDays > 365 { + return errors.New("audit.retentionDays 必须在 0 到 365 之间") + } if c.Audit.LedgerMode != "observe" && c.Audit.LedgerMode != "enforce" { return errors.New("audit.ledgerMode 必须是 observe 或 enforce") } @@ -929,8 +930,8 @@ func defaultConfig() Config { }, Audit: AuditConfig{ BufferSize: 16384, BatchSize: 256, FlushInterval: Duration(250 * time.Millisecond), CommitDelay: Duration(5 * time.Millisecond), - RetentionDays: 7, RecordRequestBody: true, RecordResponseBody: true, - LedgerMode: "enforce", LedgerFailureThreshold: 1, + RetentionDays: 7, + LedgerMode: "enforce", LedgerFailureThreshold: 1, LedgerUnhealthyGrace: Duration(10 * time.Second), LedgerQueueHighWatermarkPct: 90, }, QualityGuard: QualityGuardConfig{ diff --git a/backend/internal/infra/config/config_test.go b/backend/internal/infra/config/config_test.go index 6244a68ed..c131d2153 100644 --- a/backend/internal/infra/config/config_test.go +++ b/backend/internal/infra/config/config_test.go @@ -415,6 +415,27 @@ func TestRoutingMaxAttemptsSupportsLargeCredentialPools(t *testing.T) { } } +func TestValidateAuditRetentionDaysRange(t *testing.T) { + for _, days := range []int{-1, 366} { + cfg := defaultConfig() + cfg.Secrets.JWTSecret = "12345678901234567890123456789012" + cfg.Secrets.CredentialEncryptionKey = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + cfg.Audit.RetentionDays = days + if err := cfg.Validate(); err == nil { + t.Fatalf("audit retentionDays %d should be rejected", days) + } + } + for _, days := range []int{0, 7, 365} { + cfg := defaultConfig() + cfg.Secrets.JWTSecret = "12345678901234567890123456789012" + cfg.Secrets.CredentialEncryptionKey = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + cfg.Audit.RetentionDays = days + if err := cfg.Validate(); err != nil { + t.Fatalf("audit retentionDays %d should be valid: %v", days, err) + } + } +} + func TestValidateRejectsInvalidAutoAssignShareConfig(t *testing.T) { cfg := defaultConfig() cfg.Routing.AutoAssignMaxNodeShare = 0.03 diff --git a/backend/internal/infra/persistence/relational/audit_repository.go b/backend/internal/infra/persistence/relational/audit_repository.go index 3025d282e..1c41490f0 100644 --- a/backend/internal/infra/persistence/relational/audit_repository.go +++ b/backend/internal/infra/persistence/relational/audit_repository.go @@ -28,6 +28,7 @@ const ( auditInsertBatchSize = 20 auditLookupBatchSize = 500 attemptInsertBatchSize = 40 + auditPurgeBatchSize = 1000 auditSuccessPredicate = "status_code >= 200 AND status_code < 300 AND (error_code IS NULL OR error_code = '')" auditSuccessAggregate = "COALESCE(SUM(CASE WHEN " + auditSuccessPredicate + " THEN 1 ELSE 0 END), 0)" ) @@ -127,6 +128,15 @@ func validatePreparedAudit(value preparedAudit) error { if row.StatusCode < 100 || row.StatusCode > 599 { return errors.New("status_code must be between 100 and 599") } + if utf8.RuneCountInString(row.RequestMethod) > 16 { + return errors.New("request method exceeds the storage limit") + } + if utf8.RuneCountInString(row.RequestPath) > 2048 { + return errors.New("request path exceeds the storage limit") + } + if utf8.RuneCountInString(row.RequestHeadersJSON) > 65536 { + return errors.New("request headers exceed the storage limit") + } attemptNumbers := make(map[int]struct{}, len(value.attempts)) for index, attempt := range value.attempts { if err := validatePreparedAuditAttempt(attempt); err != nil { @@ -371,13 +381,11 @@ func toAuditModels(value audit.Record) (requestAuditModel, []requestAuditAttempt EstimatedCostInUSDTicks: nonNegative(value.EstimatedCostInUSDTicks), PricingModel: truncate(value.PricingModel, 100), PricingVersion: truncate(value.PricingVersion, 20), NumSourcesUsed: nonNegative(value.NumSourcesUsed), NumServerSideToolsUsed: nonNegative(value.NumServerSideToolsUsed), ContextInputTokens: nonNegative(value.ContextInputTokens), ContextOutputTokens: nonNegative(value.ContextOutputTokens), FirstTokenMS: normalizedFirstToken(value), DurationMS: nonNegative(value.DurationMS), - ErrorCode: truncate(value.ErrorCode, 100), - RequestMethod: truncate(value.RequestMethod, 16), - RequestPath: truncate(value.RequestPath, 2048), + ErrorCode: truncate(value.ErrorCode, 100), + RequestMethod: truncate(value.RequestMethod, 16), + RequestPath: truncate(value.RequestPath, 2048), RequestHeadersJSON: truncate(requestHeadersJSON, 65536), - RequestBody: truncate(value.RequestBody, 16777216), - ResponseBody: truncate(value.ResponseBody, 16777216), - AttemptCount: len(value.Attempts), CreatedAt: value.CreatedAt, + AttemptCount: len(value.Attempts), CreatedAt: value.CreatedAt, } attempts := make([]requestAuditAttemptModel, 0, len(value.Attempts)) for _, attempt := range value.Attempts { @@ -588,7 +596,7 @@ func (r *AuditRepository) List(ctx context.Context, offset, limit int) ([]audit. return nil, 0, err } var rows []requestAuditModel - if err := query.Order("created_at DESC, id DESC").Offset(offset).Limit(limit).Find(&rows).Error; err != nil { + if err := query.Omit("request_headers_json").Order("created_at DESC, id DESC").Offset(offset).Limit(limit).Find(&rows).Error; err != nil { return nil, 0, err } out := make([]audit.Record, 0, len(rows)) @@ -644,7 +652,7 @@ func (r *AuditRepository) ListCursor(ctx context.Context, input repository.Audit } var rows []requestAuditModel query = applyStableSort(query, input.Sort, fields, fallback, "request_audits.id") - if err := query.Limit(input.Limit + 1).Find(&rows).Error; err != nil { + if err := query.Omit("request_headers_json").Limit(input.Limit + 1).Find(&rows).Error; err != nil { return nil, false, err } hasMore := len(rows) > input.Limit @@ -1062,15 +1070,45 @@ func applyAuditQuery(query *gorm.DB, search string, start, end time.Time, filter func (r *AuditRepository) PurgeOlderThan(ctx context.Context, cutoff time.Time) (int64, error) { var totalDeleted int64 - err := r.db.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { - _ = tx.Exec("DELETE FROM request_audit_attempts WHERE audit_id IN (SELECT id FROM request_audits WHERE created_at < ?)", cutoff).Error - res := tx.Where("created_at < ?", cutoff).Delete(&requestAuditModel{}) - if res.Error != nil { - return res.Error + for { + var batchDeleted int64 + var batchSelected int + err := r.db.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error { + var ids []uint64 + if err := tx.Model(&requestAuditModel{}). + Select("id"). + Where("created_at < ?", cutoff). + Order("id ASC"). + Limit(auditPurgeBatchSize). + Pluck("id", &ids).Error; err != nil { + return err + } + if len(ids) == 0 { + return nil + } + batchSelected = len(ids) + if err := tx.Where("audit_id IN ?", ids).Delete(&requestAuditAttemptModel{}).Error; err != nil { + return err + } + res := tx.Where("id IN ? AND created_at < ?", ids, cutoff).Delete(&requestAuditModel{}) + if res.Error != nil { + return res.Error + } + batchDeleted = res.RowsAffected + return nil + }) + if err != nil { + return totalDeleted, err } - totalDeleted = res.RowsAffected - return nil - }) - return totalDeleted, err + totalDeleted += batchDeleted + // Use the number selected rather than RowsAffected to decide whether + // another batch may exist. In a multi-instance deployment another + // cleaner can delete part of this batch between selection and deletion. + if batchSelected < auditPurgeBatchSize { + return totalDeleted, nil + } + if err := ctx.Err(); err != nil { + return totalDeleted, err + } + } } - diff --git a/backend/internal/infra/persistence/relational/audit_repository_test.go b/backend/internal/infra/persistence/relational/audit_repository_test.go index 97cb2c8cf..ea8ef4a62 100644 --- a/backend/internal/infra/persistence/relational/audit_repository_test.go +++ b/backend/internal/infra/persistence/relational/audit_repository_test.go @@ -556,7 +556,7 @@ func TestDegradeTimestampScansPostgresTimeValue(t *testing.T) { } } -func TestAuditRepositoryRequestBody(t *testing.T) { +func TestAuditRepositoryRequestMetadata(t *testing.T) { ctx := context.Background() database, err := OpenSQLite(ctx, filepath.Join(t.TempDir(), "audit-request-body.db")) if err != nil { @@ -568,7 +568,6 @@ func TestAuditRepositoryRequestBody(t *testing.T) { } repository := NewAuditRepository(database) now := time.Now().UTC() - reqJSON := `{"model":"grok-4","messages":[{"role":"user","content":"Hello world"}]}` reqHeaders := map[string][]string{ "User-Agent": {"curl/8.4.0"}, "Content-Type": {"application/json"}, @@ -581,7 +580,6 @@ func TestAuditRepositoryRequestBody(t *testing.T) { RequestMethod: "POST", RequestPath: "/v1/chat/completions", RequestHeaders: reqHeaders, - RequestBody: reqJSON, CreatedAt: now, } if err := repository.Create(ctx, record); err != nil { @@ -594,27 +592,67 @@ func TestAuditRepositoryRequestBody(t *testing.T) { if len(items) != 1 { t.Fatalf("items len = %d, want 1", len(items)) } - if items[0].RequestBody != reqJSON { - t.Fatalf("RequestBody = %q, want %q", items[0].RequestBody, reqJSON) + if len(items[0].RequestHeaders) != 0 { + t.Fatalf("list unexpectedly loaded audit payloads: %#v", items[0]) } - if items[0].RequestMethod != "POST" { - t.Fatalf("RequestMethod = %q, want POST", items[0].RequestMethod) + detail, err := repository.Get(ctx, items[0].ID) + if err != nil { + t.Fatal(err) } - if items[0].RequestPath != "/v1/chat/completions" { - t.Fatalf("RequestPath = %q, want /v1/chat/completions", items[0].RequestPath) + if detail.RequestMethod != "POST" || detail.RequestPath != "/v1/chat/completions" || detail.RequestHeaders["User-Agent"][0] != "curl/8.4.0" { + t.Fatalf("Detail HTTP fields mismatch: %#v", detail) } - if items[0].RequestHeaders["User-Agent"][0] != "curl/8.4.0" { - t.Fatalf("RequestHeaders = %#v", items[0].RequestHeaders) +} + +func TestAuditRepositoryPurgeOlderThanBatchesAuditsAndAttempts(t *testing.T) { + ctx := context.Background() + database, err := OpenSQLite(ctx, filepath.Join(t.TempDir(), "audit-purge.db")) + if err != nil { + t.Fatal(err) } - detail, err := repository.Get(ctx, items[0].ID) + defer database.Close() + if err := database.InitializeSchema(ctx); err != nil { + t.Fatal(err) + } + repository := NewAuditRepository(database) + cutoff := time.Now().UTC().Add(-24 * time.Hour) + values := make([]audit.Record, auditPurgeBatchSize+1) + for index := range values { + values[index] = audit.Record{ + EventID: fmt.Sprintf("evt_audit_purge_%04d", index), + RequestID: fmt.Sprintf("purge-%04d", index), + ClientKeyID: 1, + ModelRouteID: 1, + StatusCode: 200, + CreatedAt: cutoff.Add(-time.Hour), + Attempts: []audit.Attempt{{ + Number: 1, Source: audit.AttemptSourceCredential, Stage: "response_stream", StartedAt: cutoff.Add(-time.Hour), + }}, + } + } + if err := repository.CreateBatch(ctx, values); err != nil { + t.Fatal(err) + } + if err := repository.Create(ctx, audit.Record{ + EventID: "evt_audit_purge_new", RequestID: "purge-new", ClientKeyID: 1, ModelRouteID: 1, + StatusCode: 200, CreatedAt: cutoff.Add(time.Hour), + Attempts: []audit.Attempt{{Number: 1, Source: audit.AttemptSourceCredential, Stage: "response", StartedAt: cutoff.Add(time.Hour)}}, + }); err != nil { + t.Fatal(err) + } + + deleted, err := repository.PurgeOlderThan(ctx, cutoff) if err != nil { t.Fatal(err) } - if detail.RequestBody != reqJSON { - t.Fatalf("Detail RequestBody = %q, want %q", detail.RequestBody, reqJSON) + if deleted != int64(len(values)) { + t.Fatalf("deleted = %d, want %d", deleted, len(values)) } - if detail.RequestMethod != "POST" || detail.RequestPath != "/v1/chat/completions" || detail.RequestHeaders["User-Agent"][0] != "curl/8.4.0" { - t.Fatalf("Detail HTTP fields mismatch: %#v", detail) + if count := tableRowCount(t, database, "request_audits"); count != 1 { + t.Fatalf("remaining audits = %d", count) + } + if count := tableRowCount(t, database, "request_audit_attempts"); count != 1 { + t.Fatalf("remaining attempts = %d", count) } } diff --git a/backend/internal/infra/persistence/relational/mapping.go b/backend/internal/infra/persistence/relational/mapping.go index 0f0fc3761..6f0deef78 100644 --- a/backend/internal/infra/persistence/relational/mapping.go +++ b/backend/internal/infra/persistence/relational/mapping.go @@ -270,7 +270,7 @@ func toAuditDomain(value requestAuditModel) audit.Record { EstimatedCostInUSDTicks: value.EstimatedCostInUSDTicks, PricingModel: value.PricingModel, PricingVersion: value.PricingVersion, NumSourcesUsed: value.NumSourcesUsed, NumServerSideToolsUsed: value.NumServerSideToolsUsed, ContextInputTokens: value.ContextInputTokens, ContextOutputTokens: value.ContextOutputTokens, FirstTokenMS: value.FirstTokenMS, DurationMS: value.DurationMS, - ErrorCode: value.ErrorCode, RequestMethod: value.RequestMethod, RequestPath: value.RequestPath, RequestHeaders: requestHeaders, RequestBody: value.RequestBody, ResponseBody: value.ResponseBody, AttemptCount: value.AttemptCount, CreatedAt: value.CreatedAt, + ErrorCode: value.ErrorCode, RequestMethod: value.RequestMethod, RequestPath: value.RequestPath, RequestHeaders: requestHeaders, AttemptCount: value.AttemptCount, CreatedAt: value.CreatedAt, } } diff --git a/backend/internal/infra/persistence/relational/models.go b/backend/internal/infra/persistence/relational/models.go index 8824c6a62..4b8bc3f55 100644 --- a/backend/internal/infra/persistence/relational/models.go +++ b/backend/internal/infra/persistence/relational/models.go @@ -345,8 +345,6 @@ type requestAuditModel struct { RequestMethod string `gorm:"size:16;not null;default:'';check:chk_request_audits_request_method,length(request_method) <= 16"` RequestPath string `gorm:"type:text;not null;default:'';check:chk_request_audits_request_path,length(request_path) <= 2048"` RequestHeadersJSON string `gorm:"type:text;not null;default:'{}';check:chk_request_audits_request_headers,length(request_headers_json) <= 65536"` - RequestBody string `gorm:"type:text;not null;default:'';check:chk_request_audits_request_body,length(request_body) <= 16777216"` - ResponseBody string `gorm:"type:text;not null;default:'';check:chk_request_audits_response_body,length(response_body) <= 16777216"` AttemptCount int `gorm:"not null;default:0;check:chk_request_audits_attempt_count,attempt_count >= 0"` CreatedAt time.Time `gorm:"not null"` } diff --git a/backend/internal/transport/http/audit/handler.go b/backend/internal/transport/http/audit/handler.go index d53f87a39..01e33bbb0 100644 --- a/backend/internal/transport/http/audit/handler.go +++ b/backend/internal/transport/http/audit/handler.go @@ -129,8 +129,6 @@ type auditResponse struct { RequestMethod string `json:"requestMethod,omitempty"` RequestPath string `json:"requestPath,omitempty"` RequestHeaders map[string][]string `json:"requestHeaders,omitempty"` - RequestBody string `json:"requestBody,omitempty"` - ResponseBody string `json:"responseBody,omitempty"` AttemptCount int `json:"attemptCount"` CreatedAt time.Time `json:"createdAt"` } @@ -479,7 +477,7 @@ func newListFilter(c *gin.Context) auditapp.ListFilter { } func newAuditResponse(value auditdomain.Record) auditResponse { - return auditResponse{ + result := auditResponse{ ID: value.ID, RequestID: value.RequestID, ClientKeyID: value.ClientKeyID, ClientKeyName: value.ClientKeyName, ClientIP: value.ClientIP, ModelRouteID: value.ModelRouteID, ModelPublicID: value.ModelPublicID, ModelUpstreamModel: value.ModelUpstreamModel, Provider: value.Provider, Operation: string(value.Operation), UsageSource: string(value.UsageSource), @@ -495,15 +493,11 @@ func newAuditResponse(value auditdomain.Record) auditResponse { NumSourcesUsed: value.NumSourcesUsed, NumServerSideToolsUsed: value.NumServerSideToolsUsed, ContextInputTokens: value.ContextInputTokens, ContextOutputTokens: value.ContextOutputTokens, FirstTokenMS: value.FirstTokenMS, OutputTokensPerSecond: auditOutputTokensPerSecond(value), DurationMS: value.DurationMS, - ErrorCode: value.ErrorCode, - RequestMethod: value.RequestMethod, - RequestPath: value.RequestPath, - RequestHeaders: value.RequestHeaders, - RequestBody: value.RequestBody, - ResponseBody: value.ResponseBody, + ErrorCode: value.ErrorCode, RequestMethod: value.RequestMethod, RequestPath: value.RequestPath, RequestHeaders: value.RequestHeaders, AttemptCount: value.AttemptCount, - CreatedAt: value.CreatedAt, + CreatedAt: value.CreatedAt, } + return result } func newBillingBreakdown(value auditdomain.Record) *billingBreakdownResponse { diff --git a/backend/internal/transport/http/inference/handler.go b/backend/internal/transport/http/inference/handler.go index d2b519af5..dbc44239b 100644 --- a/backend/internal/transport/http/inference/handler.go +++ b/backend/internal/transport/http/inference/handler.go @@ -326,7 +326,7 @@ func (h *Handler) createChatCompletion(c *gin.Context) { AllowClientToolCacheRoute: allowBuildClientToolCacheRoute(c.Request.Header), GrokTurnIndex: c.GetHeader("x-grok-turn-idx"), Method: c.Request.Method, - Path: c.Request.URL.RequestURI(), + Path: c.Request.URL.Path, Headers: c.Request.Header.Clone(), }) if err != nil { @@ -371,7 +371,7 @@ func (h *Handler) createMessage(c *gin.Context) { AllowClientToolCacheRoute: allowBuildClientToolCacheRoute(c.Request.Header), GrokTurnIndex: c.GetHeader("x-grok-turn-idx"), Method: c.Request.Method, - Path: c.Request.URL.RequestURI(), + Path: c.Request.URL.Path, Headers: c.Request.Header.Clone(), }) if err != nil { @@ -438,8 +438,8 @@ func (h *Handler) generateImage(c *gin.Context) { RequestID: requestID, ClientKey: clientKey, PublicModel: request.Model, Prompt: request.Prompt, Count: count, Size: request.Size, AspectRatio: request.AspectRatio, Resolution: request.Resolution, Quality: quality, ResponseFormat: request.ResponseFormat, - Streaming: request.Stream, PartialImages: partialImages, RequestBody: string(body), - Method: c.Request.Method, Path: c.Request.URL.RequestURI(), Headers: c.Request.Header.Clone(), + Streaming: request.Stream, PartialImages: partialImages, + Method: c.Request.Method, Path: c.Request.URL.Path, Headers: c.Request.Header.Clone(), }) if err != nil { writeGatewayError(c, err) @@ -677,8 +677,8 @@ func (h *Handler) editImage(c *gin.Context) { RequestID: requestID, ClientKey: clientKey, PublicModel: model, Prompt: prompt, ImageURLs: imageURLs, Count: count, Size: size, AspectRatio: aspectRatio, Resolution: resolution, Quality: quality, ResponseFormat: request.ResponseFormat, - Streaming: request.Stream, PartialImages: partialImages, RequestBody: string(body), - Method: c.Request.Method, Path: c.Request.URL.RequestURI(), Headers: c.Request.Header.Clone(), + Streaming: request.Stream, PartialImages: partialImages, + Method: c.Request.Method, Path: c.Request.URL.Path, Headers: c.Request.Header.Clone(), }) if err != nil { writeGatewayError(c, err) @@ -1159,7 +1159,7 @@ func (h *Handler) handleCreate(c *gin.Context, compact bool) { AllowClientToolCacheRoute: allowBuildClientToolCacheRoute(c.Request.Header), GrokTurnIndex: c.GetHeader("x-grok-turn-idx"), Method: c.Request.Method, - Path: c.Request.URL.RequestURI(), + Path: c.Request.URL.Path, Headers: c.Request.Header.Clone(), } var result *gateway.Result @@ -1247,15 +1247,8 @@ func (h *Handler) writeProtocolResult(c *gin.Context, result *gateway.Result, st usage := gateway.Usage{} responseID := "" errorCode := "" - responseBody := "" defer result.Body.Close() - defer func() { - if result.FinalizeWithBody != nil { - result.FinalizeWithBody(usage, responseID, errorCode, responseBody) - } else { - result.Finalize(usage, responseID, errorCode) - } - }() + defer func() { result.Finalize(usage, responseID, errorCode) }() if isUpstreamCredentialStatus(result.StatusCode) { errorCode = "upstream_unavailable" clientCode := readCredentialErrorCode(result.StatusCode, result.Body) @@ -1283,13 +1276,13 @@ func (h *Handler) writeProtocolResult(c *gin.Context, result *gateway.Result, st var err error if stream { metadata, copyErr := copyStreamWithFallbackModel(c.Writer, result.Body, protocol, result.MarkFirstToken, fallbackModel) - usage, responseID, responseBody, err = metadata.Usage, metadata.ResponseID, metadata.ResponseBody, copyErr + usage, responseID, err = metadata.Usage, metadata.ResponseID, copyErr if metadata.StreamFailure != nil && result.RecordStreamFailure != nil { result.RecordStreamFailure(*metadata.StreamFailure) } } else { metadata, copyErr := copyJSON(c.Writer, result.Body, protocol) - usage, responseID, responseBody, err = metadata.Usage, metadata.ResponseID, metadata.ResponseBody, copyErr + usage, responseID, err = metadata.Usage, metadata.ResponseID, copyErr } if err != nil { switch { @@ -1315,7 +1308,6 @@ type responseMetadata struct { ResponseID string Model string SequenceNumber int64 - ResponseBody string StreamFailure *gateway.StreamFailureDiagnostic } @@ -1586,38 +1578,19 @@ func copyJSON(writer gin.ResponseWriter, source io.Reader, protocol streamProtoc if readErr != nil { if errors.Is(readErr, io.EOF) { if metadataComplete { - meta := normalizeMetadataUsage(extractMetadata(metadataBody), protocol) - meta.ResponseBody = string(metadataBody) - return meta, nil + return normalizeMetadataUsage(extractMetadata(metadataBody), protocol), nil } - return responseMetadata{ResponseBody: string(metadataBody)}, nil + return responseMetadata{}, nil } - return responseMetadata{ResponseBody: string(metadataBody)}, readErr + return responseMetadata{}, readErr } } } -type streamAccumulator struct { - chatContent strings.Builder - chatReasoning strings.Builder - chatRefusal strings.Builder - chatFinishReason string - chatSystemFingerprint string - anthropicThinking strings.Builder - anthropicText strings.Builder - anthropicStopReason string - responsesText strings.Builder - responsesReasoning strings.Builder - rawSSE strings.Builder -} - -const maxStreamAccumulatorBytes = 524288 // 512KB - type responseInspector struct { protocol streamProtocol pending []byte metadata responseMetadata - acc streamAccumulator onFirstToken func() firstTokenSeen bool firstTokenReady bool @@ -1659,10 +1632,6 @@ func (i *responseInspector) Inspect(chunk []byte) { continue } if bytes.HasPrefix(line, []byte("data:")) { - if i.acc.rawSSE.Len() < maxStreamAccumulatorBytes { - i.acc.rawSSE.Write(line) - i.acc.rawSSE.WriteByte('\n') - } value := bytes.TrimSpace(bytes.TrimPrefix(line, []byte("data:"))) if containsGeneratedDelta(value, i.protocol) { i.metadata.Usage.OutputObserved = true @@ -1670,7 +1639,6 @@ func (i *responseInspector) Inspect(chunk []byte) { i.observeFirstToken(value) i.observeTerminal(value) if !bytes.Equal(value, []byte("[DONE]")) { - i.accumulateStreamChunk(value) metadata := extractMetadata(value) if hasUsageMetadata(metadata.Usage) { if metadata.Usage.ResponseModel == "" { @@ -1696,192 +1664,8 @@ func (i *responseInspector) Inspect(chunk []byte) { } } -func (i *responseInspector) accumulateStreamChunk(data []byte) { - switch i.protocol { - case streamProtocolChat: - var chunk struct { - ID string `json:"id"` - Model string `json:"model"` - SystemFingerprint string `json:"system_fingerprint"` - Choices []struct { - Delta struct { - Content string `json:"content"` - Reasoning string `json:"reasoning"` - ReasoningContent string `json:"reasoning_content"` - ThinkingContent string `json:"thinking_content"` - Refusal string `json:"refusal"` - } `json:"delta"` - FinishReason string `json:"finish_reason"` - } `json:"choices"` - } - if json.Unmarshal(data, &chunk) == nil { - if chunk.SystemFingerprint != "" { - i.acc.chatSystemFingerprint = chunk.SystemFingerprint - } - for _, choice := range chunk.Choices { - if choice.Delta.Content != "" && i.acc.chatContent.Len() < maxStreamAccumulatorBytes { - i.acc.chatContent.WriteString(choice.Delta.Content) - } - reasoning := choice.Delta.ReasoningContent - if reasoning == "" { - reasoning = choice.Delta.Reasoning - } - if reasoning == "" { - reasoning = choice.Delta.ThinkingContent - } - if reasoning != "" && i.acc.chatReasoning.Len() < maxStreamAccumulatorBytes { - i.acc.chatReasoning.WriteString(reasoning) - } - if choice.Delta.Refusal != "" && i.acc.chatRefusal.Len() < maxStreamAccumulatorBytes { - i.acc.chatRefusal.WriteString(choice.Delta.Refusal) - } - if choice.FinishReason != "" { - i.acc.chatFinishReason = choice.FinishReason - } - } - } - case streamProtocolAnthropic: - var evt struct { - Type string `json:"type"` - Delta struct { - Type string `json:"type"` - Text string `json:"text"` - Thinking string `json:"thinking"` - StopReason string `json:"stop_reason"` - } `json:"delta"` - } - if json.Unmarshal(data, &evt) == nil { - if evt.Delta.Type == "text_delta" && evt.Delta.Text != "" && i.acc.anthropicText.Len() < maxStreamAccumulatorBytes { - i.acc.anthropicText.WriteString(evt.Delta.Text) - } - if evt.Delta.Type == "thinking_delta" && evt.Delta.Thinking != "" && i.acc.anthropicThinking.Len() < maxStreamAccumulatorBytes { - i.acc.anthropicThinking.WriteString(evt.Delta.Thinking) - } - if evt.Delta.StopReason != "" { - i.acc.anthropicStopReason = evt.Delta.StopReason - } - } - case streamProtocolResponses: - var evt struct { - Type string `json:"type"` - Delta string `json:"delta"` - } - if json.Unmarshal(data, &evt) == nil { - if (evt.Type == "response.output_text.delta" || evt.Type == "response.text.delta") && evt.Delta != "" && i.acc.responsesText.Len() < maxStreamAccumulatorBytes { - i.acc.responsesText.WriteString(evt.Delta) - } - if (evt.Type == "response.reasoning_text.delta" || evt.Type == "response.reasoning_summary_text.delta") && evt.Delta != "" && i.acc.responsesReasoning.Len() < maxStreamAccumulatorBytes { - i.acc.responsesReasoning.WriteString(evt.Delta) - } - } - } -} - -func (i *responseInspector) buildResponseBody() string { - switch i.protocol { - case streamProtocolChat: - if i.acc.chatContent.Len() > 0 || i.acc.chatReasoning.Len() > 0 || i.acc.chatFinishReason != "" { - msg := map[string]any{ - "role": "assistant", - "content": i.acc.chatContent.String(), - } - if i.acc.chatReasoning.Len() > 0 { - msg["reasoning_content"] = i.acc.chatReasoning.String() - } - if i.acc.chatRefusal.Len() > 0 { - msg["refusal"] = i.acc.chatRefusal.String() - } - finishReason := i.acc.chatFinishReason - if finishReason == "" { - finishReason = "stop" - } - resp := map[string]any{ - "id": i.metadata.ResponseID, - "object": "chat.completion", - "created": time.Now().Unix(), - "model": i.metadata.Model, - "choices": []map[string]any{ - { - "index": 0, - "message": msg, - "finish_reason": finishReason, - }, - }, - } - if i.acc.chatSystemFingerprint != "" { - resp["system_fingerprint"] = i.acc.chatSystemFingerprint - } - if i.metadata.Usage.TotalTokens > 0 || i.metadata.Usage.InputTokens > 0 || i.metadata.Usage.OutputTokens > 0 { - resp["usage"] = map[string]any{ - "prompt_tokens": i.metadata.Usage.InputTokens, - "completion_tokens": i.metadata.Usage.OutputTokens, - "total_tokens": i.metadata.Usage.TotalTokens, - } - } - if raw, err := json.MarshalIndent(resp, "", " "); err == nil { - return string(raw) - } - } - case streamProtocolAnthropic: - if i.acc.anthropicText.Len() > 0 || i.acc.anthropicThinking.Len() > 0 { - blocks := make([]map[string]any, 0, 2) - if i.acc.anthropicThinking.Len() > 0 { - blocks = append(blocks, map[string]any{ - "type": "thinking", - "thinking": i.acc.anthropicThinking.String(), - }) - } - if i.acc.anthropicText.Len() > 0 { - blocks = append(blocks, map[string]any{ - "type": "text", - "text": i.acc.anthropicText.String(), - }) - } - stopReason := i.acc.anthropicStopReason - if stopReason == "" { - stopReason = "end_turn" - } - resp := map[string]any{ - "id": i.metadata.ResponseID, - "type": "message", - "role": "assistant", - "content": blocks, - "model": i.metadata.Model, - "stop_reason": stopReason, - } - if i.metadata.Usage.TotalTokens > 0 || i.metadata.Usage.InputTokens > 0 || i.metadata.Usage.OutputTokens > 0 { - resp["usage"] = map[string]any{ - "input_tokens": i.metadata.Usage.InputTokens, - "output_tokens": i.metadata.Usage.OutputTokens, - } - } - if raw, err := json.MarshalIndent(resp, "", " "); err == nil { - return string(raw) - } - } - case streamProtocolResponses: - if i.acc.responsesText.Len() > 0 || i.acc.responsesReasoning.Len() > 0 { - resp := map[string]any{ - "id": i.metadata.ResponseID, - "object": "response", - "model": i.metadata.Model, - "output_text": i.acc.responsesText.String(), - } - if i.acc.responsesReasoning.Len() > 0 { - resp["reasoning_text"] = i.acc.responsesReasoning.String() - } - if raw, err := json.MarshalIndent(resp, "", " "); err == nil { - return string(raw) - } - } - } - return i.acc.rawSSE.String() -} - func (i *responseInspector) Metadata() responseMetadata { - meta := normalizeMetadataUsage(i.metadata, i.protocol) - meta.ResponseBody = i.buildResponseBody() - return meta + return normalizeMetadataUsage(i.metadata, i.protocol) } func (i *responseInspector) observeReasoningStart() { diff --git a/backend/internal/transport/http/inference/openai_audio_handler.go b/backend/internal/transport/http/inference/openai_audio_handler.go index 5d77093f3..a9df1df52 100644 --- a/backend/internal/transport/http/inference/openai_audio_handler.go +++ b/backend/internal/transport/http/inference/openai_audio_handler.go @@ -116,9 +116,8 @@ func (h *Handler) handleOpenAISpeech(c *gin.Context) { OutputFormat: format, Speed: speed, OptimizeStreamingLatency: optimize, - RequestBody: string(body), Method: c.Request.Method, - Path: c.Request.URL.RequestURI(), + Path: c.Request.URL.Path, Headers: c.Request.Header.Clone(), } if request.TextNormalization != nil { diff --git a/backend/internal/transport/http/inference/voice_handler.go b/backend/internal/transport/http/inference/voice_handler.go index 1ad4f7b4f..6190cdebb 100644 --- a/backend/internal/transport/http/inference/voice_handler.go +++ b/backend/internal/transport/http/inference/voice_handler.go @@ -75,8 +75,8 @@ func (h *Handler) synthesizeSpeech(c *gin.Context) { } input := gateway.TTSInput{ RequestID: requestID, ClientKey: clientKey, PublicModel: model, Text: text, VoiceID: strings.TrimSpace(request.VoiceID), - Language: language, OutputFormat: format, Speed: speed, OptimizeStreamingLatency: optimize, RequestBody: string(body), - Method: c.Request.Method, Path: c.Request.URL.RequestURI(), Headers: c.Request.Header.Clone(), + Language: language, OutputFormat: format, Speed: speed, OptimizeStreamingLatency: optimize, + Method: c.Request.Method, Path: c.Request.URL.Path, Headers: c.Request.Header.Clone(), } if request.TextNormalization != nil { input.TextNormalization = *request.TextNormalization @@ -135,7 +135,9 @@ func (h *Handler) transcribeSpeech(c *gin.Context) { func (h *Handler) transcribeSpeechRequest(c *gin.Context, openAICompatible bool) { c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, h.maxBodyBytes) contentType := strings.ToLower(strings.TrimSpace(c.GetHeader("Content-Type"))) - input := gateway.STTInput{PublicModel: "grok-stt"} + input := gateway.STTInput{ + PublicModel: "grok-stt", Method: c.Request.Method, Path: c.Request.URL.Path, Headers: c.Request.Header.Clone(), + } unsupportedOpenAIParameter := "" if strings.HasPrefix(contentType, "multipart/form-data") { if err := c.Request.ParseMultipartForm(h.maxBodyBytes); err != nil { @@ -291,7 +293,7 @@ func (h *Handler) transcribeSpeechRequest(c *gin.Context, openAICompatible bool) input.ClientKey = clientKey input.RequestID = requestID input.Method = c.Request.Method - input.Path = c.Request.URL.RequestURI() + input.Path = c.Request.URL.Path input.Headers = c.Request.Header.Clone() result, err := h.gateway.TranscribeSpeech(c.Request.Context(), input) if err != nil { diff --git a/backend/internal/transport/http/settings/handler.go b/backend/internal/transport/http/settings/handler.go index 0f15adb7f..de23db4d9 100644 --- a/backend/internal/transport/http/settings/handler.go +++ b/backend/internal/transport/http/settings/handler.go @@ -116,13 +116,11 @@ type segmentedSelectorConfigDTO struct { } type auditConfigDTO struct { - BufferSize int `json:"bufferSize"` - BatchSize int `json:"batchSize"` - FlushInterval string `json:"flushInterval"` - CommitDelayMS int `json:"commitDelayMS"` - RetentionDays *int `json:"retentionDays,omitempty"` - RecordRequestBody *bool `json:"recordRequestBody,omitempty"` - RecordResponseBody *bool `json:"recordResponseBody,omitempty"` + BufferSize int `json:"bufferSize"` + BatchSize int `json:"batchSize"` + FlushInterval string `json:"flushInterval"` + CommitDelayMS int `json:"commitDelayMS"` + RetentionDays *int `json:"retentionDays,omitempty"` } type clientKeyDefaultsConfigDTO struct { @@ -236,12 +234,7 @@ func (value settingsConfigDTO) toApplication() settingsapp.EditableConfig { }, Audit: settingsapp.AuditConfig{ BufferSize: value.Audit.BufferSize, BatchSize: value.Audit.BatchSize, FlushInterval: value.Audit.FlushInterval, CommitDelayMS: value.Audit.CommitDelayMS, - RetentionDays: intValue(value.Audit.RetentionDays), - RetentionDaysProvided: value.Audit.RetentionDays != nil, - RecordRequestBody: boolValue(value.Audit.RecordRequestBody), - RecordRequestBodyProvided: value.Audit.RecordRequestBody != nil, - RecordResponseBody: boolValue(value.Audit.RecordResponseBody), - RecordResponseBodyProvided: value.Audit.RecordResponseBody != nil, + RetentionDays: intValue(value.Audit.RetentionDays), RetentionDaysProvided: value.Audit.RetentionDays != nil, }, ClientKeyDefaults: settingsapp.ClientKeyDefaultsConfig{ RPMLimit: value.ClientKeyDefaults.RPMLimit, MaxConcurrent: value.ClientKeyDefaults.MaxConcurrent, @@ -326,9 +319,7 @@ func newSettingsResponse(value settingsapp.Snapshot) settingsResponse { }, Audit: auditConfigDTO{ BufferSize: config.Audit.BufferSize, BatchSize: config.Audit.BatchSize, FlushInterval: config.Audit.FlushInterval, CommitDelayMS: config.Audit.CommitDelayMS, - RetentionDays: intPointer(config.Audit.RetentionDays), - RecordRequestBody: boolPointer(config.Audit.RecordRequestBody), - RecordResponseBody: boolPointer(config.Audit.RecordResponseBody), + RetentionDays: intPointer(config.Audit.RetentionDays), }, ClientKeyDefaults: clientKeyDefaultsConfigDTO{ RPMLimit: config.ClientKeyDefaults.RPMLimit, MaxConcurrent: config.ClientKeyDefaults.MaxConcurrent, @@ -389,4 +380,3 @@ func stringSlicePointer(value []string) *[]string { cloned := append([]string(nil), value...) return &cloned } - diff --git a/config.example.yaml b/config.example.yaml index 52cc1765b..37db00d46 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -115,6 +115,8 @@ audit: flushInterval: 250ms # [通常不要修改] 需要调用方确认提交的审计最多聚合等待该时长;建议保持 1ms–50ms。 commitDelay: 5ms + # [按需修改] 自动清理超过该天数的请求审计与尝试诊断;0 表示永久保留。 + retentionDays: 7 # [通常不要修改] enforce 会在异常持续超过宽限期后暂停新的推理请求;确认发生数据丢失时会立即暂停。 ledgerMode: enforce # observe | enforce ledgerFailureThreshold: 1 diff --git a/frontend/package-lock.json b/frontend/package-lock.json deleted file mode 100644 index 729512940..000000000 --- a/frontend/package-lock.json +++ /dev/null @@ -1,5131 +0,0 @@ -{ - "name": "grok2api-frontend", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "grok2api-frontend", - "version": "0.1.0", - "dependencies": { - "@hookform/resolvers": "^5.4.0", - "@radix-ui/react-alert-dialog": "^1.1.19", - "@radix-ui/react-checkbox": "^1.3.7", - "@radix-ui/react-dialog": "^1.1.19", - "@radix-ui/react-dropdown-menu": "^2.1.20", - "@radix-ui/react-label": "^2.1.11", - "@radix-ui/react-popover": "^1.1.19", - "@radix-ui/react-select": "^2.3.3", - "@radix-ui/react-slot": "^1.3.0", - "@radix-ui/react-switch": "^1.3.3", - "@radix-ui/react-tabs": "^1.1.17", - "@radix-ui/react-tooltip": "^1.2.12", - "@shadcn/react": "^0.2.1", - "@tanstack/react-query": "^5.101.2", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "date-fns": "^4.4.0", - "i18next": "^26.3.6", - "lucide-react": "^1.24.0", - "marked": "^18.0.6", - "next-themes": "^0.4.6", - "react": "^19.2.7", - "react-day-picker": "^10.0.1", - "react-dom": "^19.2.7", - "react-hook-form": "^7.81.0", - "react-i18next": "^17.0.9", - "react-router-dom": "^7.18.1", - "recharts": "2.15.4", - "sonner": "^2.0.7", - "tailwind-merge": "^3.6.0", - "tw-animate-css": "^1.4.0", - "zod": "^4.4.3" - }, - "devDependencies": { - "@eslint/js": "^10.0.1", - "@tailwindcss/vite": "^4.3.2", - "@types/node": "^26.1.1", - "@types/react": "^19.2.17", - "@types/react-dom": "^19.2.3", - "@vitejs/plugin-react": "^6.0.3", - "eslint": "^10.6.0", - "eslint-plugin-react-hooks": "^7.1.1", - "eslint-plugin-react-refresh": "^0.5.3", - "globals": "^17.7.0", - "tailwindcss": "^4.3.2", - "typescript": "^6.0.2", - "typescript-eslint": "^8.63.0", - "vite": "^8.1.4" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.7", - "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.29.7.tgz", - "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.29.7", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.7", - "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.29.7.tgz", - "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.7", - "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.29.7.tgz", - "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.7", - "@babel/helper-compilation-targets": "^7.29.7", - "@babel/helper-module-transforms": "^7.29.7", - "@babel/helpers": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/template": "^7.29.7", - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.8", - "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.29.8.tgz", - "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.8", - "@babel/types": "^7.29.8", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.29.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", - "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.29.7", - "@babel/helper-validator-option": "^7.29.7", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.29.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.29.7.tgz", - "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.29.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", - "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.29.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", - "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7", - "@babel/traverse": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.29.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", - "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.29.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", - "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.29.7", - "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", - "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.29.7", - "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.29.7.tgz", - "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.8", - "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.8.tgz", - "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.8" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.29.7", - "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.29.7.tgz", - "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.29.7", - "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.29.7.tgz", - "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.8", - "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.29.8.tgz", - "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.7", - "@babel/generator": "^7.29.8", - "@babel/helper-globals": "^7.29.7", - "@babel/parser": "^7.29.8", - "@babel/template": "^7.29.7", - "@babel/types": "^7.29.8", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.8", - "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.8.tgz", - "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.29.7", - "@babel/helper-validator-identifier": "^7.29.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@date-fns/tz": { - "version": "1.5.0", - "resolved": "https://registry.npmmirror.com/@date-fns/tz/-/tz-1.5.0.tgz", - "integrity": "sha512-lwYN/vDPeNRULcepoE/LO2Pgx+7/RV+S9ARfbc9lr2DtGkOD7pAiruHvbR1RX3Qyf6ja47EWJDMsNK5vK08DJg==", - "license": "MIT" - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.10.1", - "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", - "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmmirror.com/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" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.2", - "resolved": "https://registry.npmmirror.com/@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" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.23.5", - "resolved": "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.23.5.tgz", - "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^3.0.5", - "debug": "^4.3.1", - "minimatch": "^10.2.4" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.7.0", - "resolved": "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.7.0.tgz", - "integrity": "sha512-DObd/KKUsU+FaFv4PLxSRenpXfQWmPXXP3pPZ6/K1PCrMu2vQpMDMuQe/BqYeoLcz8ro0bVDF1RxOJgfVEdhUw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/core": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/@eslint/core/-/core-1.2.1.tgz", - "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/js": { - "version": "10.0.1", - "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-10.0.1.tgz", - "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "eslint": "^10.0.0" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/@eslint/object-schema": { - "version": "3.0.5", - "resolved": "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-3.0.5.tgz", - "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.7.2", - "resolved": "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", - "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.2.1", - "levn": "^0.4.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@floating-ui/core": { - "version": "1.8.0", - "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.8.0.tgz", - "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", - "license": "MIT", - "dependencies": { - "@floating-ui/utils": "^0.2.12" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.8.0", - "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.8.0.tgz", - "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", - "license": "MIT", - "dependencies": { - "@floating-ui/core": "^1.8.0", - "@floating-ui/utils": "^0.2.12" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.1.9", - "resolved": "https://registry.npmmirror.com/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", - "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", - "license": "MIT", - "dependencies": { - "@floating-ui/dom": "^1.8.0" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.12", - "resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.12.tgz", - "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", - "license": "MIT" - }, - "node_modules/@hookform/resolvers": { - "version": "5.9.1", - "resolved": "https://registry.npmmirror.com/@hookform/resolvers/-/resolvers-5.9.1.tgz", - "integrity": "sha512-7b7vsbraJxKgjVSA1Nur9tLwj539WGJUBLA7QNvXnFoT2pM5Z7G+6rlukk4B2/QrTZy6huRtH6wKeESPKuIr6w==", - "license": "MIT", - "dependencies": { - "@standard-schema/utils": "^0.3.0" - }, - "peerDependencies": { - "@sinclair/typebox": ">=0.25.24", - "@standard-schema/spec": "^1.0.0", - "@typeschema/main": ">=0.13.7", - "@vinejs/vine": "^2.0.0 || ^3.0.0 || ^4.0.0", - "ajv": "^8.12.0", - "ajv-errors": "^3.0.0", - "ajv-formats": "^2.1.1", - "arktype": "^2.0.0", - "ata-validator": "^1.2.0", - "class-transformer": ">=0.4.0", - "class-validator": ">=0.12.0", - "computed-types": "^1.0.0", - "effect": "^3.10.3", - "fluentvalidation-ts": "^3.0.0", - "fp-ts": "^2.7.0", - "io-ts": "^2.0.0", - "joi": "^17.0.0 || ^18.0.0", - "nope-validator": ">=0.12.0", - "react-hook-form": "^7.55.0", - "superstruct": ">=0.12.0", - "typanion": "^3.3.2", - "valibot": ">=0.31.0 || ^1.0.0-beta.4 || ^1.0.0-rc", - "vest": ">=6.0.0", - "yup": "^1.0.0", - "zod": "^3.25.0 || ^4.0.0" - }, - "peerDependenciesMeta": { - "@sinclair/typebox": { - "optional": true - }, - "@standard-schema/spec": { - "optional": true - }, - "@typeschema/main": { - "optional": true - }, - "@vinejs/vine": { - "optional": true - }, - "ajv": { - "optional": true - }, - "ajv-errors": { - "optional": true - }, - "ajv-formats": { - "optional": true - }, - "arktype": { - "optional": true - }, - "ata-validator": { - "optional": true - }, - "class-transformer": { - "optional": true - }, - "class-validator": { - "optional": true - }, - "computed-types": { - "optional": true - }, - "effect": { - "optional": true - }, - "fluentvalidation-ts": { - "optional": true - }, - "fp-ts": { - "optional": true - }, - "io-ts": { - "optional": true - }, - "joi": { - "optional": true - }, - "nope-validator": { - "optional": true - }, - "superstruct": { - "optional": true - }, - "typanion": { - "optional": true - }, - "valibot": { - "optional": true - }, - "vest": { - "optional": true - }, - "yup": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.2", - "resolved": "https://registry.npmmirror.com/@humanfs/core/-/core-0.19.2.tgz", - "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/types": "^0.15.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.8", - "resolved": "https://registry.npmmirror.com/@humanfs/node/-/node-0.16.8.tgz", - "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.2", - "@humanfs/types": "^0.15.0", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/types": { - "version": "0.15.0", - "resolved": "https://registry.npmmirror.com/@humanfs/types/-/types-0.15.0.tgz", - "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmmirror.com/@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" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmmirror.com/@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" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmmirror.com/@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" - } - }, - "node_modules/@oxc-project/types": { - "version": "0.144.0", - "resolved": "https://registry.npmmirror.com/@oxc-project/types/-/types-0.144.0.tgz", - "integrity": "sha512-nuhZIOLuI6TFQ32I/WnUx+SCPY7SdSKwgnFHydAuoS1+Z4BRcaP+RRJmGzl9lw+0OFF7UmaESf7KQRXaNLHypg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Boshen" - } - }, - "node_modules/@radix-ui/number": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/@radix-ui/number/-/number-1.1.3.tgz", - "integrity": "sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==", - "license": "MIT" - }, - "node_modules/@radix-ui/primitive": { - "version": "1.1.7", - "resolved": "https://registry.npmmirror.com/@radix-ui/primitive/-/primitive-1.1.7.tgz", - "integrity": "sha512-rqWnm76nYT8HoNNqEjpgJ7Pw/DrBj5iBTrmEPo6HTX5+VJyBNOqTdv4g89G63HuR5g0AaENoAcH7Is5fF2kZ8Q==", - "license": "MIT" - }, - "node_modules/@radix-ui/react-alert-dialog": { - "version": "1.1.23", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.23.tgz", - "integrity": "sha512-VAYOiQRqj3GPpYJE0I9J+X8Ip05cyVlNdKOFeiGS2Ou1HHGfpl0BxOyZm6nmVDyU+W+NF3/XLzmjHmVGydhwgA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-context": "1.2.2", - "@radix-ui/react-dialog": "1.1.23", - "@radix-ui/react-primitive": "2.1.10" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-arrow": { - "version": "1.1.15", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-arrow/-/react-arrow-1.1.15.tgz", - "integrity": "sha512-v4zggRcjadnI+ClKDuijlQEW4tw3NoaeHc/PwpKnLoLLKNUG4InLegkstooLcRIUWCs+8L22dGURCVuFfOKfnA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.10" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-checkbox": { - "version": "1.3.11", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-checkbox/-/react-checkbox-1.3.11.tgz", - "integrity": "sha512-Gnptr9pDDQxD3hgq2dtPbtrp/c2qH1mBwIzw3X/ivrMb2e1t0jMTi606fVEqFPaQR1ggXIVQWKj3P2WW9v7zGQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-context": "1.2.2", - "@radix-ui/react-presence": "1.1.10", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-use-controllable-state": "1.2.6", - "@radix-ui/react-use-size": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-collection": { - "version": "1.1.15", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-collection/-/react-collection-1.1.15.tgz", - "integrity": "sha512-9W+B9NPF0NaaPh/1NJd3+KqsnlLqU9H7T2rvww+fp+T/evVXdNAyYcnfRQZFOjkR1ajQp3yORlqnI8soawLvNA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-context": "1.2.2", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-slot": "1.3.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.5", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz", - "integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-context": { - "version": "1.2.2", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-context/-/react-context-1.2.2.tgz", - "integrity": "sha512-RHCUGwKHDr0hDGg4X7ma4JG4/+12qxw8rkh5QKdDldlCvtja6nUx1Ef/8HVrJze81lEsgLQlqjzjGNHantgnQA==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dialog": { - "version": "1.1.23", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-dialog/-/react-dialog-1.1.23.tgz", - "integrity": "sha512-Ksw4WeROkO4rC9k/onilX/Ao2Cr1ku1unMNH+XSCcP4jSXYu7HDsg9n4ojMjVb22XpYjAQ9qfrFlVbru1vXDUA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-context": "1.2.2", - "@radix-ui/react-dismissable-layer": "1.1.19", - "@radix-ui/react-focus-guards": "1.1.6", - "@radix-ui/react-focus-scope": "1.1.16", - "@radix-ui/react-id": "1.1.4", - "@radix-ui/react-portal": "1.1.17", - "@radix-ui/react-presence": "1.1.10", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-slot": "1.3.3", - "@radix-ui/react-use-controllable-state": "1.2.6", - "@radix-ui/react-use-layout-effect": "1.1.4", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.7.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-direction": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-direction/-/react-direction-1.1.4.tgz", - "integrity": "sha512-5pzg4FGQNpExhnhT2zlrP1wZFaYCd1K0nYWoFAdcYoYK868IEigqMX3B3f8yIoRlAhAeDWciLI6ZdCKHF9P4Vg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.19", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.19.tgz", - "integrity": "sha512-8g4pfOL9HoKKLWGiypT+dphVqjFfmcXO5GBnhsG6zI+lxAx/8feQpr+1LSN8Re3hiZ+XkLNS4O9ztK11/LzQ6w==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-use-callback-ref": "1.1.4", - "@radix-ui/react-use-effect-event": "0.0.5" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.24", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.24.tgz", - "integrity": "sha512-geq8l2rJkxvkXsT9RMgtUE3P8pITFpTsvYpbySi1IH4fZEABD/Gp85myayFgxk0ktljGMJnCbeFkyTusvSvv7g==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-context": "1.2.2", - "@radix-ui/react-id": "1.1.4", - "@radix-ui/react-menu": "2.1.24", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-use-controllable-state": "1.2.6" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.6", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.6.tgz", - "integrity": "sha512-RNOJjfZMTyBM6xYmV3IVGXkPjIhcBAuv48POevAXwrGJhkWZ9p1rFoIS1JFooPuT193AZmRsCPhpoVJxx6OPoQ==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.16", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.16.tgz", - "integrity": "sha512-wmRZ2WWLvmt6KHy2rNPOdPUjwq5xOHY02+m+udwJTn0aNIox/rkskAvJTyTLGhPK6KgrUjlJUJpgmx/+wFiFIQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-use-callback-ref": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-id/-/react-id-1.1.4.tgz", - "integrity": "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-label": { - "version": "2.1.15", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-label/-/react-label-2.1.15.tgz", - "integrity": "sha512-o/rdYEwZTTo5tjknnPeyQFU45kUC4i/XyeDPP+HGyi6XqpOP6Zf5Ya5vh/Yfe9Id5JiuWnnAx2XqIeD3UYZt0g==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.10" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-menu": { - "version": "2.1.24", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-menu/-/react-menu-2.1.24.tgz", - "integrity": "sha512-uW7RVuU6Lp/ZtfeY4b3kL32zccgEWvPv1+cf17ubYzHa9cL8AHokmk36cG/XEiH/smbQvumnieXX9j/e9RqJWA==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-collection": "1.1.15", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-context": "1.2.2", - "@radix-ui/react-direction": "1.1.4", - "@radix-ui/react-dismissable-layer": "1.1.19", - "@radix-ui/react-focus-guards": "1.1.6", - "@radix-ui/react-focus-scope": "1.1.16", - "@radix-ui/react-id": "1.1.4", - "@radix-ui/react-popper": "1.3.7", - "@radix-ui/react-portal": "1.1.17", - "@radix-ui/react-presence": "1.1.10", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-roving-focus": "1.1.19", - "@radix-ui/react-slot": "1.3.3", - "@radix-ui/react-use-callback-ref": "1.1.4", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.7.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-popover": { - "version": "1.1.23", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-popover/-/react-popover-1.1.23.tgz", - "integrity": "sha512-mw58MrBlyHWFisTOYignD0vf/3gdcgAR+9of1s9G/38CbFiUwH1nCDkc0AUM9IrXFgN5Ue8n45j9WCgyM1sbiQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-context": "1.2.2", - "@radix-ui/react-dismissable-layer": "1.1.19", - "@radix-ui/react-focus-guards": "1.1.6", - "@radix-ui/react-focus-scope": "1.1.16", - "@radix-ui/react-id": "1.1.4", - "@radix-ui/react-popper": "1.3.7", - "@radix-ui/react-portal": "1.1.17", - "@radix-ui/react-presence": "1.1.10", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-slot": "1.3.3", - "@radix-ui/react-use-controllable-state": "1.2.6", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.7.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-popper": { - "version": "1.3.7", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-popper/-/react-popper-1.3.7.tgz", - "integrity": "sha512-UsJrrd7w4wuKKTdvd/DNERVlwSlUcyXzjhyDwBk+3aPOsCjOY6ZSbxuw8E6lZTjjfP8Cpd0J8VVkrYUWyGYXyg==", - "license": "MIT", - "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.15", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-context": "1.2.2", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-use-callback-ref": "1.1.4", - "@radix-ui/react-use-layout-effect": "1.1.4", - "@radix-ui/react-use-rect": "1.1.4", - "@radix-ui/react-use-size": "1.1.4", - "@radix-ui/rect": "1.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-portal": { - "version": "1.1.17", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-portal/-/react-portal-1.1.17.tgz", - "integrity": "sha512-vKQLcWypUnwZVvfV7UkGahH2g6ySe8M8R+zYBwPrv5byZ9QAW6cQVvNKo7GgmD+p8aYb6D9JBuvy8/WhOno2wQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.10", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-presence/-/react-presence-1.1.10.tgz", - "integrity": "sha512-3wyzCQ6+ubRA+D4uv9m95JYLXxmOHp05qjrkjeA7uKHHtjpPggQzc6DAb0URl7j67oR0K2foO4ip27TiX037Bw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.1.10", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-primitive/-/react-primitive-2.1.10.tgz", - "integrity": "sha512-MucOnzh6hR5mid6VpkbglRAMYMjKLqRnGBbjXkzjK52fuQDd1qbkx78a5P40mkcnVXJdEVxm26E9OPAiUq7nBg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.3.3" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.19", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.19.tgz", - "integrity": "sha512-V9jI6hDjT7l3jsCQD9bLNvDLM3tH/gdbOTp7Tefp3hbbgCGQoK7tUvrWiRlcoBHIZ809ElXwNQwVo0B98LuTXQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-collection": "1.1.15", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-context": "1.2.2", - "@radix-ui/react-direction": "1.1.4", - "@radix-ui/react-id": "1.1.4", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-use-callback-ref": "1.1.4", - "@radix-ui/react-use-controllable-state": "1.2.6", - "@radix-ui/react-use-is-hydrated": "0.1.3", - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-select": { - "version": "2.3.7", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-select/-/react-select-2.3.7.tgz", - "integrity": "sha512-WFGImkmbzcfxeIwq/+4HvRN0pizBwbwQUED4I13ezQsDdfl38ZntN6TmR8XaSzPBqoCToe8rF75j6NPNDSzhbg==", - "license": "MIT", - "dependencies": { - "@radix-ui/number": "1.1.3", - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-collection": "1.1.15", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-context": "1.2.2", - "@radix-ui/react-direction": "1.1.4", - "@radix-ui/react-dismissable-layer": "1.1.19", - "@radix-ui/react-focus-guards": "1.1.6", - "@radix-ui/react-focus-scope": "1.1.16", - "@radix-ui/react-id": "1.1.4", - "@radix-ui/react-popper": "1.3.7", - "@radix-ui/react-portal": "1.1.17", - "@radix-ui/react-presence": "1.1.10", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-slot": "1.3.3", - "@radix-ui/react-use-callback-ref": "1.1.4", - "@radix-ui/react-use-controllable-state": "1.2.6", - "@radix-ui/react-use-layout-effect": "1.1.4", - "@radix-ui/react-use-previous": "1.1.4", - "@radix-ui/react-visually-hidden": "1.2.11", - "aria-hidden": "^1.2.4", - "react-remove-scroll": "^2.7.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-slot": { - "version": "1.3.3", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.3.3.tgz", - "integrity": "sha512-qx7oqnYbxnK9kYI9m317qmFmEgo6ywqWvbTogdj7cL9p3/yx4M48p7Rnw5z3H890cL/ow/EeWJsuTykeZVXP5Q==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.5" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-switch": { - "version": "1.3.7", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-switch/-/react-switch-1.3.7.tgz", - "integrity": "sha512-48tB/4dn2UVLBCYhTu9AuR63IHl73l/qLbLgxd86noTUor4/K4LFDAcYjK+isP5313qxaFpjPVogE7+Y0/V3Kw==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-context": "1.2.2", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-use-controllable-state": "1.2.6", - "@radix-ui/react-use-size": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tabs": { - "version": "1.1.21", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-tabs/-/react-tabs-1.1.21.tgz", - "integrity": "sha512-UKxJlZid7FVtsk/WTxj4i4uSEgj2Au+KBbS7SQyTlzMhhn+86Cz3tISZdTa87bfEfcuvZezf2ZsxD4xuEKtkog==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-context": "1.2.2", - "@radix-ui/react-direction": "1.1.4", - "@radix-ui/react-id": "1.1.4", - "@radix-ui/react-presence": "1.1.10", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-roving-focus": "1.1.19", - "@radix-ui/react-use-controllable-state": "1.2.6" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip": { - "version": "1.2.16", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-tooltip/-/react-tooltip-1.2.16.tgz", - "integrity": "sha512-6EamKFRRnlpdadndbZ6LMwycfwkwPte1B42hs6QA0gYhjaOKqW4PZ4pjaW9UrlDX5eVt/OjncE7BFTPL5nmZhg==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-compose-refs": "1.1.5", - "@radix-ui/react-context": "1.2.2", - "@radix-ui/react-dismissable-layer": "1.1.19", - "@radix-ui/react-id": "1.1.4", - "@radix-ui/react-popper": "1.3.7", - "@radix-ui/react-portal": "1.1.17", - "@radix-ui/react-presence": "1.1.10", - "@radix-ui/react-primitive": "2.1.10", - "@radix-ui/react-slot": "1.3.3", - "@radix-ui/react-use-controllable-state": "1.2.6", - "@radix-ui/react-use-layout-effect": "1.1.4", - "@radix-ui/react-visually-hidden": "1.2.11" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-callback-ref": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.4.tgz", - "integrity": "sha512-R6OUY2e2fA6Yn6s+VSx5KBV6Nx8LQEhu+cz7LCej18rQ1HLyg9PSC9jP/ZNx0o6FAIK9c0F1kHylzSxKsdlkrQ==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-controllable-state": { - "version": "1.2.6", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.6.tgz", - "integrity": "sha512-uEQJGT97ZA/TgP/Hydw47lHu+/vQj6z/0jA+WeTbK1o9Rx45GImjpD0tc3W5ad3D6XTSR6e1yEO0FvGq6WQfVQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/primitive": "1.1.7", - "@radix-ui/react-use-effect-event": "0.0.5", - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-effect-event": { - "version": "0.0.5", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz", - "integrity": "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-is-hydrated": { - "version": "0.1.3", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.3.tgz", - "integrity": "sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-layout-effect": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz", - "integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-previous": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-previous/-/react-use-previous-1.1.4.tgz", - "integrity": "sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-rect": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-rect/-/react-use-rect-1.1.4.tgz", - "integrity": "sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/rect": "1.1.3" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-use-size": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-use-size/-/react-use-size-1.1.4.tgz", - "integrity": "sha512-D3anSY15EJoxrihpsXI6SMrmmonnQtR2ni7arO+Lfdg3O95b9hNXxONk8jA5C8ANdF/h5HMAxejgs8PWJ6rlhw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.4" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.2.11", - "resolved": "https://registry.npmmirror.com/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.11.tgz", - "integrity": "sha512-NFS86RYYZb4/exihaESBGOpMJFz8MGLAfu3mOBSGByVnVPC9JPASfYubxd/8KbkQK0sYAv8lVQDEQukDX/qXvQ==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.10" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/rect": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/@radix-ui/rect/-/rect-1.1.3.tgz", - "integrity": "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==", - "license": "MIT" - }, - "node_modules/@rolldown/binding-android-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.4.tgz", - "integrity": "sha512-jHC2cnyKz5xU2fhECtFl8OZ83cYNt13GZQD+0uMJ/X3o+ijmd56okHhTUwxVSHPx1IRVIJEZ1/1pPzeLCU6XKA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-Dc5mPD8F5F/FS8i01syd7FTF6yB2fVthH/TRkjwJkzUK6EpoxHtqvZQP5Zwq80/5z19TWYHIg1KOHboCgVx/aQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.4.tgz", - "integrity": "sha512-fpDm4oBo6SqLvWUYCmFhdde3U9KH2fRNNMeAnAPAIwxRL345xutL0EtEUcuoxsoazdJGv/MuDBQHlCDrtbvqOg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.4.tgz", - "integrity": "sha512-rSJoreDE/HoIzoaib6MTp5jQtCTdMHKIvItAKT/ImS6Y6Ww76oUaeMyp4Vc/fAgd/ehji068IxetHXAnqUwN9A==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.4.tgz", - "integrity": "sha512-/jm8OGHgn7oGaJu3i/qZI9spUGcJ+y/lk43ttQ/iO1tOd9NissG6o97bighBCiL+BKRngmcDuR6ikfwYdJmVuQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.4.tgz", - "integrity": "sha512-tIP06BeD9EqvECBrPZ+sqdPlYrT+aYaAiu1wYziVx5elRK/ftm33JxVDy2bXGbr6J0CrtirCkR87/X5a2euEng==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.4.tgz", - "integrity": "sha512-Ql1Q0EQqVThvn9VAVlwNzsUvbSFtCMGjLpRRi4pk5i7NZZ4n5ISiLMjHYtus4VQ2PvkSw24zyaCVsiS+sXPj1w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.4.tgz", - "integrity": "sha512-GjbjXD4XXfN19D0LZNbmiCBUoDiRACsYHr0yaIbbn8aFsXjHZifcYqu/W5Er5X2X990WjHXFrxarn5chzItorQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.4.tgz", - "integrity": "sha512-p5WR0NOwaRmJ/B1b6IjEFLLivwEsf3PrdBIhRbhTCQisbo2SvHHpG4ELB/+FgQNnB88LTOF86upmJmbvZdQ2lw==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.4.tgz", - "integrity": "sha512-4/GyVjmhR+Tc6HLJvwc1sOhPqAZtySiSMesOZyX6JQ5XBxoTDEMKQzvo07NIK6nTon/SivlZqvhzvuVBNQhObQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.4.tgz", - "integrity": "sha512-l9eeLsCNvPpmSXUej0etw/J1eqV0Jj1D5G/xG6YTijmE6dkv6E2QezgWbTfQk63v952DPqrjOCoiqxq7Bw0YUQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.4.tgz", - "integrity": "sha512-e0F355MSTMm3+UOqtV3L24gFUp2N5m1f8L/7d56deik6va+AXdrt9F8LbzGpeWGWRbZEDq4m8NVnJDeBtf9DZg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.4.tgz", - "integrity": "sha512-AWLi0uBRYh6QlE7OKhiz+phZC0qwtij2QZmhmOdsLdFn64m7oMpooE9ICE3lhm9xMb4SpDo2WbHcxX1iFLFtqw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.4.tgz", - "integrity": "sha512-UwSDJOg3dqCAejWdxclJjCsh3Qq4vLYMDxmyHqo1btz3stK2VqgwNd3mm5tuIwzSlGIQ/1H9Hr+Zn09mrezNqQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", - "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@shadcn/react": { - "version": "0.2.1", - "resolved": "https://registry.npmmirror.com/@shadcn/react/-/react-0.2.1.tgz", - "integrity": "sha512-5krgi3dRMKb5jH6a+qPzVJUy/54s0kKE4Rw4LjDfLqOdVQTWKUgxWf1kW8r912I0jX/Lzxqc+pgjkjWxUIK5BQ==", - "license": "MIT", - "peerDependencies": { - "@types/react": ">=19", - "react": ">=19" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "react": { - "optional": true - } - } - }, - "node_modules/@standard-schema/utils": { - "version": "0.3.0", - "resolved": "https://registry.npmmirror.com/@standard-schema/utils/-/utils-0.3.0.tgz", - "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", - "license": "MIT" - }, - "node_modules/@tailwindcss/node": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/@tailwindcss/node/-/node-4.3.3.tgz", - "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "^5.24.1", - "jiti": "^2.7.0", - "lightningcss": "1.32.0", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.3.3" - } - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide/-/oxide-4.3.3.tgz", - "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 20" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.3.3", - "@tailwindcss/oxide-darwin-arm64": "4.3.3", - "@tailwindcss/oxide-darwin-x64": "4.3.3", - "@tailwindcss/oxide-freebsd-x64": "4.3.3", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", - "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", - "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", - "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", - "@tailwindcss/oxide-linux-x64-musl": "4.3.3", - "@tailwindcss/oxide-wasm32-wasi": "4.3.3", - "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", - "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", - "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", - "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", - "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", - "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", - "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", - "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", - "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", - "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", - "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", - "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.11.1", - "@emnapi/runtime": "^1.11.1", - "@emnapi/wasi-threads": "^1.2.2", - "@napi-rs/wasm-runtime": "^1.1.4", - "@tybys/wasm-util": "^0.10.2", - "tslib": "^2.8.1" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", - "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", - "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 20" - } - }, - "node_modules/@tailwindcss/vite": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/@tailwindcss/vite/-/vite-4.3.3.tgz", - "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tailwindcss/node": "4.3.3", - "@tailwindcss/oxide": "4.3.3", - "tailwindcss": "4.3.3" - }, - "peerDependencies": { - "vite": "^5.2.0 || ^6 || ^7 || ^8" - } - }, - "node_modules/@tanstack/query-core": { - "version": "5.101.4", - "resolved": "https://registry.npmmirror.com/@tanstack/query-core/-/query-core-5.101.4.tgz", - "integrity": "sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/react-query": { - "version": "5.101.4", - "resolved": "https://registry.npmmirror.com/@tanstack/react-query/-/react-query-5.101.4.tgz", - "integrity": "sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA==", - "license": "MIT", - "dependencies": { - "@tanstack/query-core": "5.101.4" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": "^18 || ^19" - } - }, - "node_modules/@types/d3-array": { - "version": "3.2.2", - "resolved": "https://registry.npmmirror.com/@types/d3-array/-/d3-array-3.2.2.tgz", - "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", - "license": "MIT" - }, - "node_modules/@types/d3-color": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", - "license": "MIT" - }, - "node_modules/@types/d3-ease": { - "version": "3.0.2", - "resolved": "https://registry.npmmirror.com/@types/d3-ease/-/d3-ease-3.0.2.tgz", - "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", - "license": "MIT" - }, - "node_modules/@types/d3-interpolate": { - "version": "3.0.4", - "resolved": "https://registry.npmmirror.com/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", - "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", - "license": "MIT", - "dependencies": { - "@types/d3-color": "*" - } - }, - "node_modules/@types/d3-path": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/@types/d3-path/-/d3-path-3.1.1.tgz", - "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", - "license": "MIT" - }, - "node_modules/@types/d3-scale": { - "version": "4.0.9", - "resolved": "https://registry.npmmirror.com/@types/d3-scale/-/d3-scale-4.0.9.tgz", - "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", - "license": "MIT", - "dependencies": { - "@types/d3-time": "*" - } - }, - "node_modules/@types/d3-shape": { - "version": "3.1.8", - "resolved": "https://registry.npmmirror.com/@types/d3-shape/-/d3-shape-3.1.8.tgz", - "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", - "license": "MIT", - "dependencies": { - "@types/d3-path": "*" - } - }, - "node_modules/@types/d3-time": { - "version": "3.0.4", - "resolved": "https://registry.npmmirror.com/@types/d3-time/-/d3-time-3.0.4.tgz", - "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", - "license": "MIT" - }, - "node_modules/@types/d3-timer": { - "version": "3.0.2", - "resolved": "https://registry.npmmirror.com/@types/d3-timer/-/d3-timer-3.0.2.tgz", - "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", - "license": "MIT" - }, - "node_modules/@types/esrecurse": { - "version": "4.3.1", - "resolved": "https://registry.npmmirror.com/@types/esrecurse/-/esrecurse-4.3.1.tgz", - "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.9", - "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz", - "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "26.2.0", - "resolved": "https://registry.npmmirror.com/@types/node/-/node-26.2.0.tgz", - "integrity": "sha512-5IviulTZeRNp2vAJ514cc/HUlY5nZ9fCbq9DMyC52BrhFZACo3nI0R7qBxhQmo/d27NFe96ur/b7Wwxklda+kg==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~8.3.0" - } - }, - "node_modules/@types/react": { - "version": "19.2.18", - "resolved": "https://registry.npmmirror.com/@types/react/-/react-19.2.18.tgz", - "integrity": "sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.4", - "resolved": "https://registry.npmmirror.com/@types/react-dom/-/react-dom-19.2.4.tgz", - "integrity": "sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==", - "devOptional": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.67.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.67.0.tgz", - "integrity": "sha512-Un7Heoyj65NREbKAyIrFxeM143NZpExWmy1Nep4DLeQOeLlTeumPjoNKnBrU5D5moWXbPJgRa5Uwcdu0faVNGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.67.0", - "@typescript-eslint/type-utils": "8.67.0", - "@typescript-eslint/utils": "8.67.0", - "@typescript-eslint/visitor-keys": "8.67.0", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.67.0", - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { - "version": "7.0.6", - "resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.6.tgz", - "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.67.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.67.0.tgz", - "integrity": "sha512-fUBfTuuEulWqX6V8+O3PtScV01tzYYRUDTAirHFKoRAt7nOzoGiPt0M/bB47wWNy0coOOcgEwAMUtBpykMxl6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.67.0", - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/typescript-estree": "8.67.0", - "@typescript-eslint/visitor-keys": "8.67.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.67.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.67.0.tgz", - "integrity": "sha512-cvE8c7ulYeXN9fYuszhCeCsbzyVEXuhrRCybnBre7TUmqb5nRmBfQAwCj0O3WJFDeyAZt4VYv51vMCC9LHSdYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.67.0", - "@typescript-eslint/types": "^8.67.0", - "debug": "^4.4.3" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.67.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.67.0.tgz", - "integrity": "sha512-EgvsleTwS4E+WzzSvem8fAUubLwatMNF1B5hHSLQxcvs7q2dtRhGyujHwLJSYlG41niJ7GP24Aha2+0mb1b2kg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/visitor-keys": "8.67.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.67.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.67.0.tgz", - "integrity": "sha512-vV+LUSv5njUWsknE71fqKTlXUva+R76SaeORd6Zojcunk/6DvKFXONU3BrAs2H49mbygUXt6gbYunzwqNwlhdg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.67.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.67.0.tgz", - "integrity": "sha512-aVWDXbRmdXO9siTfX4ditQI1T9+zVcNazT48EJCD0v40/9RIFoUgZ05CmGEq9H2gixRpjUn/iplwvlcvutJW/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/typescript-estree": "8.67.0", - "@typescript-eslint/utils": "8.67.0", - "debug": "^4.4.3", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.67.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.67.0.tgz", - "integrity": "sha512-sBtgslww8nsMYUjhdPBiSyUqSzT8uR6g93A2QXnQC8+cGdjz0CyaOdqHDRJb1AtORbZCNUJBBeFA/tNR2uQmww==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.67.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.67.0.tgz", - "integrity": "sha512-EKQBCE9yNlRJYm7jdTW5AhDacDUmSwQb0FAJAmK2EKYrNXIsa2vxcSZx6PvJ/dEdI6lS+Y9W+EXckLj0iPFGcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.67.0", - "@typescript-eslint/tsconfig-utils": "8.67.0", - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/visitor-keys": "8.67.0", - "debug": "^4.4.3", - "minimatch": "^10.2.2", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.5.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.67.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.67.0.tgz", - "integrity": "sha512-U9D1FdwEWBwok3hxxSdhclMb0twvt9QnjIQ0VfQ1AiX2epnpSgv2ubVDsayOFyY8K6FX+AQ7E0FKWVG3iKsj1A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.67.0", - "@typescript-eslint/types": "8.67.0", - "@typescript-eslint/typescript-estree": "8.67.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.67.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.67.0.tgz", - "integrity": "sha512-fkv8dHRDqfGtTHuJeebdrQ7cX6Ad4WAS00rgHh9UGvMycF1mjBfsxry1XsLIFhWZ6Judlh6UdzK+TYlbpCXgnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.67.0", - "eslint-visitor-keys": "^5.0.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@vitejs/plugin-react": { - "version": "6.0.5", - "resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-6.0.5.tgz", - "integrity": "sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@rolldown/pluginutils": "^1.0.1" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "peerDependencies": { - "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", - "babel-plugin-react-compiler": "^1.0.0", - "vite": "^8.0.0" - }, - "peerDependenciesMeta": { - "@rolldown/plugin-babel": { - "optional": true - }, - "babel-plugin-react-compiler": { - "optional": true - } - } - }, - "node_modules/acorn": { - "version": "8.18.0", - "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.18.0.tgz", - "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/ajv": { - "version": "6.15.0", - "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.15.0.tgz", - "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/aria-hidden": { - "version": "1.2.6", - "resolved": "https://registry.npmmirror.com/aria-hidden/-/aria-hidden-1.2.6.tgz", - "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.11.15", - "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.11.15.tgz", - "integrity": "sha512-FwMjJJ7HnyZpWe+oWxegG0fezZyBZUagI5LZEoO3GCbtbKNwRfMH9Ue5d5v01PNePBy1QSfPSDTTeVL0Hb9EzA==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.cjs" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/browserslist": { - "version": "4.28.8", - "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.8.tgz", - "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.11.12", - "caniuse-lite": "^1.0.30001809", - "electron-to-chromium": "^1.5.402", - "node-releases": "^2.0.53", - "update-browserslist-db": "^1.3.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001809", - "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001809.tgz", - "integrity": "sha512-xxWVywk6a6Arlk+hymeycyn/VgqEfLDxupvhH/xiY5SJ/18kmi9o6MiO320DCUzypORHLtvh0I4i04tUhCNHNQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/class-variance-authority": { - "version": "0.7.1", - "resolved": "https://registry.npmmirror.com/class-variance-authority/-/class-variance-authority-0.7.1.tgz", - "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", - "license": "Apache-2.0", - "dependencies": { - "clsx": "^2.1.1" - }, - "funding": { - "url": "https://polar.sh/cva" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/cookie/-/cookie-1.1.1.tgz", - "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmmirror.com/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", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/csstype": { - "version": "3.2.3", - "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz", - "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT" - }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmmirror.com/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "license": "ISC", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/d3-format/-/d3-format-3.1.2.tgz", - "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "license": "ISC", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "license": "ISC", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "license": "ISC", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "license": "ISC", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/date-fns": { - "version": "4.4.0", - "resolved": "https://registry.npmmirror.com/date-fns/-/date-fns-4.4.0.tgz", - "integrity": "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js-light": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/decimal.js-light/-/decimal.js-light-2.5.1.tgz", - "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", - "license": "MIT" - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz", - "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-node-es": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "license": "MIT" - }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmmirror.com/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.411", - "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.411.tgz", - "integrity": "sha512-gglkxzokjHfawpGxq75XdBV2/l3BAPzrsMs70qgaZdTW5rpV1tC4MdgJVP9fN126bODA4ZJQkn1wryEzJyQXIg==", - "dev": true, - "license": "ISC" - }, - "node_modules/enhanced-resolve": { - "version": "5.24.5", - "resolved": "https://registry.npmmirror.com/enhanced-resolve/-/enhanced-resolve-5.24.5.tgz", - "integrity": "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "10.8.1", - "resolved": "https://registry.npmmirror.com/eslint/-/eslint-10.8.1.tgz", - "integrity": "sha512-wqA7W2jbsC/BnV9Iv1UZpKVFkO1AdNoSmYW8NWG4HNOBbkAMvIqDZ27pI2f07dqn583NcIC44ckjAcOXDL1QbQ==", - "dev": true, - "license": "MIT", - "workspaces": [ - "packages/*" - ], - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.2", - "@eslint/config-array": "^0.23.5", - "@eslint/config-helpers": "^0.7.0", - "@eslint/core": "^1.2.1", - "@eslint/plugin-kit": "^0.7.2", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.14.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^9.1.2", - "eslint-visitor-keys": "^5.0.1", - "espree": "^11.2.0", - "esquery": "^1.7.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "minimatch": "^10.2.5", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "7.1.1", - "resolved": "https://registry.npmmirror.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", - "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.24.4", - "@babel/parser": "^7.24.4", - "hermes-parser": "^0.25.1", - "zod": "^3.25.0 || ^4.0.0", - "zod-validation-error": "^3.5.0 || ^4.0.0" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" - } - }, - "node_modules/eslint-plugin-react-refresh": { - "version": "0.5.4", - "resolved": "https://registry.npmmirror.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.4.tgz", - "integrity": "sha512-7bqTKz7T0r+HKWFarNXByDE9/5+73wI2ru+M3zuqGbR7s/b/5/pQJXZoufWlrngqGqoZto73ZkGumCdLxk+4rw==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "eslint": "^9 || ^10" - } - }, - "node_modules/eslint-scope": { - "version": "9.1.2", - "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-9.1.2.tgz", - "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@types/esrecurse": "^4.3.1", - "@types/estree": "^1.0.8", - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", - "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "11.2.0", - "resolved": "https://registry.npmmirror.com/espree/-/espree-11.2.0.tgz", - "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.16.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^5.0.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.7.0.tgz", - "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/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" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/fast-equals": { - "version": "5.4.1", - "resolved": "https://registry.npmmirror.com/fast-equals/-/fast-equals-5.4.1.tgz", - "integrity": "sha512-DjlFSM5Pk9cGcL0q5QXl66eGzx0N6szNgaswwc5ZphlBohjTVJSnGgI+rJVOgOi65qUoQnDZN4nDqi33udtydQ==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/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" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/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" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/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" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.4.4", - "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.4.4.tgz", - "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-nonce": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/get-nonce/-/get-nonce-1.0.1.tgz", - "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "17.11.0", - "resolved": "https://registry.npmmirror.com/globals/-/globals-17.11.0.tgz", - "integrity": "sha512-Z2I8hM+PbJDXQDq3Icgpzv+mPdwr68iZUU9d5WW4FuXfDUQfkZaZuvjMv42/5crNyw154+9+VWXbYrUgDXbxNw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/hermes-estree": { - "version": "0.25.1", - "resolved": "https://registry.npmmirror.com/hermes-estree/-/hermes-estree-0.25.1.tgz", - "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", - "dev": true, - "license": "MIT" - }, - "node_modules/hermes-parser": { - "version": "0.25.1", - "resolved": "https://registry.npmmirror.com/hermes-parser/-/hermes-parser-0.25.1.tgz", - "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "hermes-estree": "0.25.1" - } - }, - "node_modules/html-parse-stringify": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/html-parse-stringify/-/html-parse-stringify-4.0.1.tgz", - "integrity": "sha512-0zHsZJrK7S3K2aucXWL6ycoYJ/iNtIcFHC/nYQgFklPtrv5LpJctIiSCroWZWeuoXvuyFdzp6KzjJQ+OT5MfFw==", - "license": "MIT", - "funding": { - "url": "https://locize.com" - } - }, - "node_modules/i18next": { - "version": "26.3.6", - "resolved": "https://registry.npmmirror.com/i18next/-/i18next-26.3.6.tgz", - "integrity": "sha512-Bu5Z2nAXgfVyM8xvW3jk9EKRIuX37PudsrBViThNFx7CR7aaYTpP01cxNB/E4c4UUzTDiAZRstEhsRfPOL/8xA==", - "funding": [ - { - "type": "individual", - "url": "https://www.locize.com/i18next" - }, - { - "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" - }, - { - "type": "individual", - "url": "https://www.locize.com" - } - ], - "license": "MIT", - "peerDependencies": { - "typescript": "^5 || ^6 || ^7" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/jiti": { - "version": "2.7.0", - "resolved": "https://registry.npmmirror.com/jiti/-/jiti-2.7.0.tgz", - "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmmirror.com/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" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lightningcss": { - "version": "1.32.0", - "resolved": "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.32.0.tgz", - "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.32.0", - "lightningcss-darwin-arm64": "1.32.0", - "lightningcss-darwin-x64": "1.32.0", - "lightningcss-freebsd-x64": "1.32.0", - "lightningcss-linux-arm-gnueabihf": "1.32.0", - "lightningcss-linux-arm64-gnu": "1.32.0", - "lightningcss-linux-arm64-musl": "1.32.0", - "lightningcss-linux-x64-gnu": "1.32.0", - "lightningcss-linux-x64-musl": "1.32.0", - "lightningcss-win32-arm64-msvc": "1.32.0", - "lightningcss-win32-x64-msvc": "1.32.0" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmmirror.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", - "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.32.0", - "resolved": "https://registry.npmmirror.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", - "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmmirror.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", - "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.32.0", - "resolved": "https://registry.npmmirror.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", - "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.32.0", - "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", - "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", - "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", - "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.32.0", - "resolved": "https://registry.npmmirror.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", - "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.32.0", - "resolved": "https://registry.npmmirror.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", - "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmmirror.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", - "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.32.0", - "resolved": "https://registry.npmmirror.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", - "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "license": "MIT" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lucide-react": { - "version": "1.32.0", - "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-1.32.0.tgz", - "integrity": "sha512-txX56hMFnRxPi1f9/nH69YN8uvAO6a7Y1KSWKjCDAtdD9+soEgmWuCt6iRm1pkxUZo2+YntSdsE1L6bIuKoY8Q==", - "license": "ISC", - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", - "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/marked": { - "version": "18.0.10", - "resolved": "https://registry.npmmirror.com/marked/-/marked-18.0.10.tgz", - "integrity": "sha512-FJeH4bRpYoXiggcgriCGItKCSv3xkngJc4QCZ/rkQCogU3VYaLxYJoZl8Nw/b4+x7iij/pd+09mZ6A1dXzpL0A==", - "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/minimatch": { - "version": "10.2.6", - "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-10.2.6.tgz", - "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.8" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.18", - "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.18.tgz", - "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/next-themes": { - "version": "0.4.6", - "resolved": "https://registry.npmmirror.com/next-themes/-/next-themes-0.4.6.tgz", - "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" - } - }, - "node_modules/node-releases": { - "version": "2.0.53", - "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.53.tgz", - "integrity": "sha512-D9UOmYG3UH1V+ENW56t5QXBwJw1YEY18ruVeus89Rw+SyIgjPkCO84bRzO3uNIYosJbNwiabWVn48o3uJLjxFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmmirror.com/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", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/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" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.5", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.5.tgz", - "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.5.26", - "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.26.tgz", - "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.17", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmmirror.com/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/react": { - "version": "19.2.8", - "resolved": "https://registry.npmmirror.com/react/-/react-19.2.8.tgz", - "integrity": "sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-day-picker": { - "version": "10.0.1", - "resolved": "https://registry.npmmirror.com/react-day-picker/-/react-day-picker-10.0.1.tgz", - "integrity": "sha512-eNh6BlwcYInWaJtRv18mXQ06Ys/H6rdTZAnTaSdOYJuTpwP1JMCHNd1FDRadA+gbeinq+psdULN5Xnowy9mV8w==", - "license": "MIT", - "dependencies": { - "@date-fns/tz": "^1.4.1", - "date-fns": "^4.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "individual", - "url": "https://github.com/sponsors/gpbl" - }, - "peerDependencies": { - "@types/react": ">=16.8.0", - "react": ">=16.8.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-dom": { - "version": "19.2.8", - "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-19.2.8.tgz", - "integrity": "sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==", - "license": "MIT", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.8" - } - }, - "node_modules/react-hook-form": { - "version": "7.85.0", - "resolved": "https://registry.npmmirror.com/react-hook-form/-/react-hook-form-7.85.0.tgz", - "integrity": "sha512-U2MTriFXnclmV4rOE20p2DcRFv5WEg3FIcBFOKcOLFHDVvGIMPvLTkTWefUsonmlaVy23khVDxDWym6uJVGOzw==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/react-hook-form" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17 || ^18 || ^19" - } - }, - "node_modules/react-i18next": { - "version": "17.0.11", - "resolved": "https://registry.npmmirror.com/react-i18next/-/react-i18next-17.0.11.tgz", - "integrity": "sha512-cDtkXgxjuFTWUH6V+aQn1Ve5vDiUztCNPWW5GtSHDccsgRXO1nE6QFWCEmc1KAutrb3OUv87wFShJL5RhUwPXg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.29.2", - "html-parse-stringify": "^4.0.1", - "use-sync-external-store": "^1.6.0" - }, - "peerDependencies": { - "i18next": ">= 26.2.0", - "react": ">= 16.8.0", - "typescript": "^5 || ^6 || ^7" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, - "node_modules/react-remove-scroll": { - "version": "2.7.2", - "resolved": "https://registry.npmmirror.com/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", - "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", - "license": "MIT", - "dependencies": { - "react-remove-scroll-bar": "^2.3.7", - "react-style-singleton": "^2.2.3", - "tslib": "^2.1.0", - "use-callback-ref": "^1.3.3", - "use-sidecar": "^1.1.3" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-remove-scroll-bar": { - "version": "2.3.8", - "resolved": "https://registry.npmmirror.com/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", - "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", - "license": "MIT", - "dependencies": { - "react-style-singleton": "^2.2.2", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-router": { - "version": "7.18.2", - "resolved": "https://registry.npmmirror.com/react-router/-/react-router-7.18.2.tgz", - "integrity": "sha512-aUVMjFm3GAPTTZL7oYr5E7ETiqfQCHRLH+B+5afnICvf0r7kkK4eR6SMuwbSTJw/7t+12khT/Kahij49fqOCIg==", - "license": "MIT", - "dependencies": { - "cookie": "^1.0.1", - "set-cookie-parser": "^2.6.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - } - } - }, - "node_modules/react-router-dom": { - "version": "7.18.2", - "resolved": "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-7.18.2.tgz", - "integrity": "sha512-AIKJ/jgGlFb3EbfCXk5Gzshiwt+l3mqbCrNjmEWMMjqQxNJ3svBa6bgzFyCC2Sw3RA0VWF1kg3uQf2OFhxb8hw==", - "license": "MIT", - "dependencies": { - "react-router": "7.18.2" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - } - }, - "node_modules/react-smooth": { - "version": "4.0.4", - "resolved": "https://registry.npmmirror.com/react-smooth/-/react-smooth-4.0.4.tgz", - "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", - "license": "MIT", - "dependencies": { - "fast-equals": "^5.0.1", - "prop-types": "^15.8.1", - "react-transition-group": "^4.4.5" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/react-style-singleton": { - "version": "2.2.3", - "resolved": "https://registry.npmmirror.com/react-style-singleton/-/react-style-singleton-2.2.3.tgz", - "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", - "license": "MIT", - "dependencies": { - "get-nonce": "^1.0.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmmirror.com/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, - "node_modules/recharts": { - "version": "2.15.4", - "resolved": "https://registry.npmmirror.com/recharts/-/recharts-2.15.4.tgz", - "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", - "license": "MIT", - "dependencies": { - "clsx": "^2.0.0", - "eventemitter3": "^4.0.1", - "lodash": "^4.17.21", - "react-is": "^18.3.1", - "react-smooth": "^4.0.4", - "recharts-scale": "^0.4.4", - "tiny-invariant": "^1.3.1", - "victory-vendor": "^36.6.8" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/recharts-scale": { - "version": "0.4.5", - "resolved": "https://registry.npmmirror.com/recharts-scale/-/recharts-scale-0.4.5.tgz", - "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", - "license": "MIT", - "dependencies": { - "decimal.js-light": "^2.4.1" - } - }, - "node_modules/rolldown": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/rolldown/-/rolldown-1.2.4.tgz", - "integrity": "sha512-rSr7irW0K7QRWzjdJXqZowkcRdDtjRduh43rBltnVKd0VFq839l1lJoDvGJb6gl7+4rTTCrPWu+YfujUL8Ug7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@oxc-project/types": "=0.144.0", - "@rolldown/pluginutils": "^1.0.0" - }, - "bin": { - "rolldown": "bin/cli.mjs" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.2.4", - "@rolldown/binding-darwin-arm64": "1.2.4", - "@rolldown/binding-darwin-x64": "1.2.4", - "@rolldown/binding-freebsd-x64": "1.2.4", - "@rolldown/binding-linux-arm-gnueabihf": "1.2.4", - "@rolldown/binding-linux-arm64-gnu": "1.2.4", - "@rolldown/binding-linux-arm64-musl": "1.2.4", - "@rolldown/binding-linux-ppc64-gnu": "1.2.4", - "@rolldown/binding-linux-s390x-gnu": "1.2.4", - "@rolldown/binding-linux-x64-gnu": "1.2.4", - "@rolldown/binding-linux-x64-musl": "1.2.4", - "@rolldown/binding-openharmony-arm64": "1.2.4", - "@rolldown/binding-win32-arm64-msvc": "1.2.4", - "@rolldown/binding-win32-x64-msvc": "1.2.4" - } - }, - "node_modules/scheduler": { - "version": "0.27.0", - "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.27.0.tgz", - "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/set-cookie-parser": { - "version": "2.7.2", - "resolved": "https://registry.npmmirror.com/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", - "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", - "license": "MIT" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/sonner": { - "version": "2.0.8", - "resolved": "https://registry.npmmirror.com/sonner/-/sonner-2.0.8.tgz", - "integrity": "sha512-UM/ByIoFra8yzV75n1o0Puu0bw5U/9UNnDacrJNspekBewIfsQ3D6ez1nvlWpt7aTsO6rujQtifBpycwIivqlg==", - "license": "MIT", - "peerDependencies": { - "@types/react": "^18.0.0 || ^19.0.0", - "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", - "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/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" - } - }, - "node_modules/tailwind-merge": { - "version": "3.6.0", - "resolved": "https://registry.npmmirror.com/tailwind-merge/-/tailwind-merge-3.6.0.tgz", - "integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, - "node_modules/tailwindcss": { - "version": "4.3.3", - "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-4.3.3.tgz", - "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/tapable": { - "version": "2.3.3", - "resolved": "https://registry.npmmirror.com/tapable/-/tapable-2.3.3.tgz", - "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmmirror.com/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.17", - "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.17.tgz", - "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/ts-api-utils": { - "version": "2.5.0", - "resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-2.5.0.tgz", - "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/tw-animate-css": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/tw-animate-css/-/tw-animate-css-1.4.0.tgz", - "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/Wombosvideo" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/typescript": { - "version": "6.0.3", - "resolved": "https://registry.npmmirror.com/typescript/-/typescript-6.0.3.tgz", - "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.67.0", - "resolved": "https://registry.npmmirror.com/typescript-eslint/-/typescript-eslint-8.67.0.tgz", - "integrity": "sha512-S2udFs8tCKEKffuJ4TB1idGUZiXdCPGi3IPBGWXarbLQ5UPXORV8QEVzJ4gCRduURMb5EkpNCdjbk0eDIuI8Yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.67.0", - "@typescript-eslint/parser": "8.67.0", - "@typescript-eslint/typescript-estree": "8.67.0", - "@typescript-eslint/utils": "8.67.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.1.0" - } - }, - "node_modules/undici-types": { - "version": "8.3.0", - "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-8.3.0.tgz", - "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/update-browserslist-db": { - "version": "1.3.1", - "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.3.1.tgz", - "integrity": "sha512-ZZ61DsRsOnakl74HAmp3oSN4aXUmEWXf+i/yv0h7tIBfICc3VdrFErQKUUKPgu3AMsTUMbcongALEN4l6GSUrQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "devOptional": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/use-callback-ref": { - "version": "1.3.3", - "resolved": "https://registry.npmmirror.com/use-callback-ref/-/use-callback-ref-1.3.3.tgz", - "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-sidecar": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/use-sidecar/-/use-sidecar-1.1.3.tgz", - "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", - "license": "MIT", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/use-sync-external-store": { - "version": "1.6.0", - "resolved": "https://registry.npmmirror.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", - "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/victory-vendor": { - "version": "36.9.2", - "resolved": "https://registry.npmmirror.com/victory-vendor/-/victory-vendor-36.9.2.tgz", - "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", - "license": "MIT AND ISC", - "dependencies": { - "@types/d3-array": "^3.0.3", - "@types/d3-ease": "^3.0.0", - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-shape": "^3.1.0", - "@types/d3-time": "^3.0.0", - "@types/d3-timer": "^3.0.0", - "d3-array": "^3.1.6", - "d3-ease": "^3.0.1", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-shape": "^3.1.0", - "d3-time": "^3.0.0", - "d3-timer": "^3.0.1" - } - }, - "node_modules/vite": { - "version": "8.2.1", - "resolved": "https://registry.npmmirror.com/vite/-/vite-8.2.1.tgz", - "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==", - "dev": true, - "license": "MIT", - "dependencies": { - "lightningcss": "^1.33.0", - "picomatch": "^4.0.5", - "postcss": "^8.5.25", - "rolldown": "~1.2.1", - "tinyglobby": "^0.2.17" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^20.19.0 || >=22.12.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.4.0", - "esbuild": "^0.27.0 || ^0.28.0", - "jiti": ">=1.21.0", - "less": "^4.0.0", - "sass": "^1.70.0", - "sass-embedded": "^1.70.0", - "stylus": ">=0.54.8", - "sugarss": "^5.0.0", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "@vitejs/devtools": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/lightningcss": { - "version": "1.33.0", - "resolved": "https://registry.npmmirror.com/lightningcss/-/lightningcss-1.33.0.tgz", - "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", - "dev": true, - "license": "MPL-2.0", - "dependencies": { - "detect-libc": "^2.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-android-arm64": "1.33.0", - "lightningcss-darwin-arm64": "1.33.0", - "lightningcss-darwin-x64": "1.33.0", - "lightningcss-freebsd-x64": "1.33.0", - "lightningcss-linux-arm-gnueabihf": "1.33.0", - "lightningcss-linux-arm64-gnu": "1.33.0", - "lightningcss-linux-arm64-musl": "1.33.0", - "lightningcss-linux-x64-gnu": "1.33.0", - "lightningcss-linux-x64-musl": "1.33.0", - "lightningcss-win32-arm64-msvc": "1.33.0", - "lightningcss-win32-x64-msvc": "1.33.0" - } - }, - "node_modules/vite/node_modules/lightningcss-android-arm64": { - "version": "1.33.0", - "resolved": "https://registry.npmmirror.com/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", - "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-darwin-arm64": { - "version": "1.33.0", - "resolved": "https://registry.npmmirror.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", - "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-darwin-x64": { - "version": "1.33.0", - "resolved": "https://registry.npmmirror.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", - "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-freebsd-x64": { - "version": "1.33.0", - "resolved": "https://registry.npmmirror.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", - "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.33.0", - "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", - "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.33.0", - "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", - "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-linux-arm64-musl": { - "version": "1.33.0", - "resolved": "https://registry.npmmirror.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", - "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-linux-x64-gnu": { - "version": "1.33.0", - "resolved": "https://registry.npmmirror.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", - "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-linux-x64-musl": { - "version": "1.33.0", - "resolved": "https://registry.npmmirror.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", - "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.33.0", - "resolved": "https://registry.npmmirror.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", - "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/vite/node_modules/lightningcss-win32-x64-msvc": { - "version": "1.33.0", - "resolved": "https://registry.npmmirror.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", - "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zod": { - "version": "4.4.3", - "resolved": "https://registry.npmmirror.com/zod/-/zod-4.4.3.tgz", - "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-validation-error": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/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" - }, - "peerDependencies": { - "zod": "^3.25.0 || ^4.0.0" - } - } - } -} diff --git a/frontend/src/features/audits/request-audit-detail-dialog.tsx b/frontend/src/features/audits/request-audit-detail-dialog.tsx index a9431a9c3..3d99c3292 100644 --- a/frontend/src/features/audits/request-audit-detail-dialog.tsx +++ b/frontend/src/features/audits/request-audit-detail-dialog.tsx @@ -1,28 +1,21 @@ import { useQuery } from "@tanstack/react-query"; import { - Bot, - Check, CheckCircle2, - Code2, - Copy, - FileCode, FileText, Globe2, KeyRound, + ListTree, Network, Server, TriangleAlert, } from "lucide-react"; import { useMemo, useState, type ReactNode } from "react"; import { useTranslation } from "react-i18next"; -import { toast } from "sonner"; import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { getRequestAudit, type AuditAttemptDTO, type AuditDTO } from "@/features/audits/request-audits-api"; -import { copyToClipboard } from "@/shared/clipboard"; import { CopyButton } from "@/shared/components/copy-button"; import { ErrorState, LoadingState } from "@/shared/components/data-state"; import { cn } from "@/shared/lib/cn"; @@ -61,10 +54,10 @@ export function RequestAuditDetailDialog({ return ( - - + +
    - {t("audits.detailTitle")} + {t("audits.detailTitle")} {activeAudit ? ( -
    - - {activeAudit?.requestId} - +
    + {activeAudit?.requestId ? ( + + {activeAudit.requestId} + + ) : null} {activeAudit?.clientIp ? ( - - - {activeAudit.clientIp} - + <> + {activeAudit.requestId ? : null} + + + {activeAudit.clientIp} + + ) : null} {activeAudit?.operation ? ( - - {activeAudit.operation} - + <> + {activeAudit.requestId || activeAudit.clientIp ? : null} + {activeAudit.operation} + ) : null} {activeAudit ? ( - - {formatDateTime(activeAudit.createdAt, i18n.language)} - + <> + {activeAudit.requestId || activeAudit.clientIp || activeAudit.operation ? : null} + {formatDateTime(activeAudit.createdAt, i18n.language)} + ) : null}
    @@ -104,19 +104,15 @@ export function RequestAuditDetailDialog({ {activeAudit ? ( -
    - +
    + {t("audits.requestOverview")} - - - {t("audits.clientRequestBody")} - - - - {t("audits.responseBodyTitle")} + + + {t("audits.requestMetadata")} @@ -130,16 +126,12 @@ export function RequestAuditDetailDialog({
    - + - - - - - - + + @@ -175,8 +167,8 @@ function RequestOverviewPanel({ audit }: { audit: AuditDTO }) { const costTicks = audit.costInUsdTicks > 0 ? audit.costInUsdTicks : audit.estimatedCostInUsdTicks; if (!costTicks) return "$0"; const usd = (costTicks / 100_000_000).toFixed(6); - return `$${usd}${audit.costInUsdTicks <= 0 && audit.estimatedCostInUsdTicks > 0 ? " (估算)" : ""}`; - }, [audit]); + return `$${usd}${audit.costInUsdTicks <= 0 && audit.estimatedCostInUsdTicks > 0 ? ` (${t("audits.estimated")})` : ""}`; + }, [audit, t]); const durationDisplay = useMemo(() => { let text = `${formatNumber(audit.durationMs, i18n.language)} ms`; @@ -187,7 +179,7 @@ function RequestOverviewPanel({ audit }: { audit: AuditDTO }) { }, [audit, t, i18n.language]); return ( -
    +
    0 ? `${t("audits.mediaInput")}: ${audit.mediaInputImages} 张` : "", - audit.mediaOutputImages > 0 ? `${t("audits.mediaOutput")}: ${audit.mediaOutputImages} 张` : "", - audit.mediaOutputSeconds > 0 ? `${audit.mediaOutputSeconds} 秒` : "", + audit.mediaInputImages > 0 ? `${t("audits.mediaInput")}: ${t("audits.imageCount", { count: audit.mediaInputImages })}` : "", + audit.mediaOutputImages > 0 ? `${t("audits.mediaOutput")}: ${t("audits.imageCount", { count: audit.mediaOutputImages })}` : "", + audit.mediaOutputSeconds > 0 ? t("audits.secondsCount", { count: audit.mediaOutputSeconds }) : "", ].filter(Boolean).join(" · ")} /> ) : null} @@ -251,280 +243,31 @@ function RequestOverviewPanel({ audit }: { audit: AuditDTO }) { ); } -function ClientRequestBodyPanel({ audit }: { audit: AuditDTO }) { - const { t } = useTranslation(); - const [activeView, setActiveView] = useState<"raw_http" | "body" | "headers">("raw_http"); - const [copiedType, setCopiedType] = useState(null); - - const rawBody = audit.requestBody ?? ""; - const method = audit.requestMethod || "POST"; - const path = audit.requestPath || ""; - const headers = audit.requestHeaders ?? {}; - - const { formattedJson, headerText, rawHttpText, markdownHttpBlock, markdownJsonBlock, byteSize } = useMemo(() => { - let formatted = rawBody; - if (rawBody) { - try { - const parsed = JSON.parse(rawBody); - formatted = JSON.stringify(parsed, null, 2); - } catch { - formatted = rawBody; - } - } - - const headerLines = Object.entries(headers) - .map(([key, values]) => `${key}: ${values.join(", ")}`) - .join("\n"); - - const requestLine = path ? `${method} ${path} HTTP/1.1` : ""; - const rawHttp = [requestLine, headerLines, "", formatted] - .filter((segment, idx) => idx === 2 ? true : Boolean(segment)) - .join("\n"); - - const mdHttp = "```http\n" + rawHttp + "\n```"; - const mdJson = formatted ? "```json\n" + formatted + "\n```" : ""; - const size = new Blob([rawBody]).size; - - return { - formattedJson: formatted, - headerText: headerLines, - rawHttpText: rawHttp, - markdownHttpBlock: mdHttp, - markdownJsonBlock: mdJson, - byteSize: size, - }; - }, [rawBody, method, path, headers]); - - async function handleCopy(type: "raw" | "json" | "markdown_http" | "markdown_json" | "headers") { - let textToCopy = rawHttpText; - if (type === "json") textToCopy = formattedJson; - else if (type === "markdown_http") textToCopy = markdownHttpBlock; - else if (type === "markdown_json") textToCopy = markdownJsonBlock; - else if (type === "headers") textToCopy = headerText; - - const ok = await copyToClipboard(textToCopy); - if (ok) { - setCopiedType(type); - setTimeout(() => setCopiedType(null), 1800); - toast.success(t("common.copied")); - } else { - toast.error(t("common.copyFailed")); - } - } - - if (!rawBody && Object.keys(headers).length === 0 && !path) { - return } message={t("audits.noRequestBody")} />; - } - - return ( -
    -
    -
    - {method && path ? ( - - {method} {path} - - ) : null} - {byteSize > 0 ? ( - - {formatByteSize(byteSize)} - - ) : null} - {Object.keys(headers).length > 0 ? ( - - {Object.keys(headers).length} headers - - ) : null} -
    - -
    -
    - - - -
    - - - -
    -
    - -
    - {activeView === "raw_http" ? ( -
    -            {rawHttpText}
    -          
    - ) : activeView === "body" ? ( - formattedJson ? ( -
    -              {formattedJson}
    -            
    - ) : ( -

    {t("audits.noRequestBody")}

    - ) - ) : ( - Object.keys(headers).length > 0 ? ( -
    - {Object.entries(headers).map(([key, values]) => ( -
    - {key}: - {values.join(", ")} -
    - ))} -
    - ) : ( -

    {t("audits.noRequestHeaders")}

    - ) - )} -
    -
    - ); -} - -function ResponseBodyPanel({ audit }: { audit: AuditDTO }) { +function RequestMetadataPanel({ audit }: { audit: AuditDTO }) { const { t } = useTranslation(); - const [copiedType, setCopiedType] = useState(null); - - const rawBody = audit.responseBody ?? ""; - - const { formattedJson, isJson, markdownBlock, byteSize } = useMemo(() => { - let formatted = rawBody; - let jsonValid = false; - if (rawBody) { - try { - const parsed = JSON.parse(rawBody); - formatted = JSON.stringify(parsed, null, 2); - jsonValid = true; - } catch { - formatted = rawBody; - jsonValid = false; - } - } + const headers = useMemo(() => audit.requestHeaders ?? {}, [audit.requestHeaders]); - const md = jsonValid ? "```json\n" + formatted + "\n```" : "```\n" + formatted + "\n```"; - const size = new Blob([rawBody]).size; - - return { - formattedJson: formatted, - isJson: jsonValid, - markdownBlock: md, - byteSize: size, - }; - }, [rawBody]); - - async function handleCopy(type: "raw" | "markdown") { - const textToCopy = type === "raw" ? formattedJson : markdownBlock; - const ok = await copyToClipboard(textToCopy); - if (ok) { - setCopiedType(type); - setTimeout(() => setCopiedType(null), 1800); - toast.success(t("common.copied")); - } else { - toast.error(t("common.copyFailed")); - } - } - - if (!rawBody) { - return } message={t("audits.noResponseBody")} />; + if (!audit.requestMethod && !audit.requestPath && Object.keys(headers).length === 0) { + return } message={t("audits.noRequestMetadata")} />; } return ( -
    -
    -
    - {audit.statusCode ? ( - = 400} /> - ) : null} - {byteSize > 0 ? ( - - {formatByteSize(byteSize)} - - ) : null} - {audit.streaming ? ( - - {t("audits.modeStreaming")} - - ) : null} - {audit.outputTokens > 0 ? ( - - {audit.outputTokens} tokens - - ) : null} -
    - -
    - - +
    +
    +

    {t("audits.requestPath")}

    +
    + + {audit.requestMethod || "-"} + + + {audit.requestPath || "-"} + + {audit.requestPath ? : null}
    -
    - -
    -
    -          {formattedJson}
    -        
    -
    + +
    + +
    ); } @@ -570,15 +313,18 @@ function UpstreamAttemptsPanel({ ); } + const terminalAttemptNumber = Math.max(...attempts.map((attempt) => attempt.number)); + return (
    -