diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml deleted file mode 100644 index 5b0f1dd..0000000 --- a/.github/workflows/deploy.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Deploy - -on: - workflow_run: - workflows: ["Build and Publish Docker Image"] - types: - - completed - branches: - - main - -jobs: - deploy: - runs-on: blacksmith-2vcpu-ubuntu-2404 - if: ${{ github.event.workflow_run.conclusion == 'success' }} - steps: - - name: Deploy to Dokku - uses: appleboy/ssh-action@master - with: - host: ${{ secrets.DOKKU_HOST }} - username: deploy - key: ${{ secrets.DEPLOY_KEY }} - script: echo deploy \ No newline at end of file diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index c4df76e..992d2a7 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -5,6 +5,7 @@ import ( "net/http" "os" "os/signal" + "strings" "syscall" "time" @@ -27,7 +28,11 @@ import ( func main() { godotenv.Load("../.env", ".env") - database, err := db.New(getEnv("DATABASE_URL", "margin.db")) + dsn := os.Getenv("DATABASE_URL") + if dsn == "" { + logger.Fatal("DATABASE_URL environment variable is required") + } + database, err := db.New(dsn) if err != nil { logger.Fatal("Failed to connect to database: %v", err) } @@ -70,29 +75,51 @@ func main() { firehose.RelayURL = getEnv("BLOCK_RELAY_URL", "wss://jetstream2.us-east.bsky.network/subscribe") logger.Info("Firehose URL: %s", firehose.RelayURL) + backfillCtx, backfillCancel := context.WithCancel(context.Background()) + defer backfillCancel() + if recService.IsEnabled() { ingester.SetOnAnnotation(recService.OnAnnotation) ingester.SetOnDocument(recService.OnDocument) - go func() { - logger.Info("Starting recommendation backfill...") - if err := recService.BackfillDocumentEmbeddings(200); err != nil { - logger.Error("Document embedding backfill error: %v", err) - } - annCount, err := recService.BackfillAnnotationEmbeddings(200) - if err != nil { - logger.Error("Annotation embedding backfill error: %v", err) - } - hlCount, err := recService.BackfillHighlightEmbeddings(200) - if err != nil { - logger.Error("Highlight embedding backfill error: %v", err) - } - profileCount, err := recService.RebuildAllProfiles() - if err != nil { - logger.Error("Profile rebuild error: %v", err) - } - logger.Info("Recommendation backfill complete (annotations: %d, highlights: %d, profiles: %d)", annCount, hlCount, profileCount) - }() + if getEnv("DISABLE_BACKFILL", "") == "" { + go func() { + time.Sleep(5 * time.Second) + select { + case <-backfillCtx.Done(): + return + default: + } + logger.Info("Starting recommendation backfill...") + if err := recService.BackfillDocumentEmbeddings(200); err != nil { + logger.Error("Document embedding backfill error: %v", err) + } + if backfillCtx.Err() != nil { + return + } + annCount, err := recService.BackfillAnnotationEmbeddings(200) + if err != nil { + logger.Error("Annotation embedding backfill error: %v", err) + } + if backfillCtx.Err() != nil { + return + } + hlCount, err := recService.BackfillHighlightEmbeddings(200) + if err != nil { + logger.Error("Highlight embedding backfill error: %v", err) + } + if backfillCtx.Err() != nil { + return + } + profileCount, err := recService.RebuildAllProfiles() + if err != nil { + logger.Error("Profile rebuild error: %v", err) + } + logger.Info("Recommendation backfill complete (annotations: %d, highlights: %d, profiles: %d)", annCount, hlCount, profileCount) + }() + } else { + logger.Info("Recommendation backfill disabled (DISABLE_BACKFILL is set)") + } } go func() { @@ -111,7 +138,17 @@ func main() { r.Use(middleware.Throttle(100)) r.Use(cors.Handler(cors.Options{ - AllowedOrigins: []string{"https://*", "http://*", "chrome-extension://*"}, + AllowOriginFunc: func(r *http.Request, origin string) bool { + if strings.HasPrefix(origin, "chrome-extension://") || + strings.HasPrefix(origin, "moz-extension://") || + strings.HasPrefix(origin, "safari-web-extension://") { + return true + } + if baseURL := os.Getenv("BASE_URL"); baseURL != "" { + return origin == baseURL + } + return false + }, AllowedMethods: []string{"GET", "POST", "PUT", "DELETE", "OPTIONS"}, AllowedHeaders: []string{"Accept", "Authorization", "Content-Type", "X-CSRF-Token", "X-Session-Token"}, ExposedHeaders: []string{"Link"}, @@ -174,6 +211,7 @@ func main() { <-quit logger.Infoln("Shutting down server...") + backfillCancel() ingester.Stop() ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) diff --git a/backend/go.mod b/backend/go.mod index ce8eeb5..69778a7 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -11,7 +11,6 @@ require ( github.com/ipfs/go-cid v0.6.0 github.com/joho/godotenv v1.5.1 github.com/lib/pq v1.10.9 - github.com/mattn/go-sqlite3 v1.14.22 github.com/multiformats/go-multihash v0.2.3 ) diff --git a/backend/go.sum b/backend/go.sum index 9bdd503..84dca23 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -21,8 +21,6 @@ github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBF github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= -github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= diff --git a/backend/internal/api/apikey.go b/backend/internal/api/apikey.go index af99c9b..016e65d 100644 --- a/backend/internal/api/apikey.go +++ b/backend/internal/api/apikey.go @@ -482,13 +482,13 @@ func (h *APIKeyHandler) authenticateAPIKey(r *http.Request) (*db.APIKey, error) } func (h *APIKeyHandler) getSessionByDID(did string) (*SessionData, error) { - rows, err := h.db.Query(h.db.Rebind(` + rows, err := h.db.Query(` SELECT id, did, handle, access_token, refresh_token, COALESCE(dpop_key, '') FROM sessions - WHERE did = ? AND expires_at > ? + WHERE did = $1 AND expires_at > $2 ORDER BY created_at DESC LIMIT 1 - `), did, time.Now()) + `, did, time.Now()) if err != nil { return nil, err } diff --git a/backend/internal/api/handler.go b/backend/internal/api/handler.go index 43455ad..1ef9580 100644 --- a/backend/internal/api/handler.go +++ b/backend/internal/api/handler.go @@ -10,6 +10,7 @@ import ( "sort" "strconv" "strings" + "sync" "time" "github.com/go-chi/chi/v5" @@ -22,6 +23,59 @@ import ( "margin.at/internal/xrpc" ) +type urlMetaCacheEntry struct { + data map[string]string + expiresAt time.Time +} + +type urlMetaCache struct { + mu sync.RWMutex + entries map[string]urlMetaCacheEntry + inflight sync.Map +} + +type singleflight struct { + wg sync.WaitGroup + data map[string]string + err error +} + +func newURLMetaCache() *urlMetaCache { + c := &urlMetaCache{entries: make(map[string]urlMetaCacheEntry)} + go c.evictLoop() + return c +} + +func (c *urlMetaCache) get(key string) (map[string]string, bool) { + c.mu.RLock() + defer c.mu.RUnlock() + e, ok := c.entries[key] + if !ok || time.Now().After(e.expiresAt) { + return nil, false + } + return e.data, true +} + +func (c *urlMetaCache) set(key string, data map[string]string, ttl time.Duration) { + c.mu.Lock() + defer c.mu.Unlock() + c.entries[key] = urlMetaCacheEntry{data: data, expiresAt: time.Now().Add(ttl)} +} + +func (c *urlMetaCache) evictLoop() { + ticker := time.NewTicker(5 * time.Minute) + for range ticker.C { + c.mu.Lock() + now := time.Now() + for k, e := range c.entries { + if now.After(e.expiresAt) { + delete(c.entries, k) + } + } + c.mu.Unlock() + } +} + type Handler struct { db *db.DB annotationService *AnnotationService @@ -30,6 +84,8 @@ type Handler struct { syncService *internal_sync.Service moderation *ModerationHandler recommendations *recommendations.Service + metaCache *urlMetaCache + metaSem chan struct{} } func NewHandler(database *db.DB, annotationService *AnnotationService, refresher *TokenRefresher, syncService *internal_sync.Service, recService *recommendations.Service) *Handler { @@ -41,6 +97,8 @@ func NewHandler(database *db.DB, annotationService *AnnotationService, refresher syncService: syncService, moderation: NewModerationHandler(database, refresher), recommendations: recService, + metaCache: newURLMetaCache(), + metaSem: make(chan struct{}, 5), } } @@ -346,9 +404,47 @@ func (h *Handler) GetFeed(w http.ResponseWriter, r *http.Request) { } } - authAnnos, _ := hydrateAnnotations(h.db, annotations, viewerDID) - authHighs, _ := hydrateHighlights(h.db, highlights, viewerDID) - authBooks, _ := hydrateBookmarks(h.db, bookmarks, viewerDID) + allDIDs := make(map[string]bool) + for _, a := range annotations { + allDIDs[a.AuthorDID] = true + } + for _, h := range highlights { + allDIDs[h.AuthorDID] = true + } + for _, b := range bookmarks { + allDIDs[b.AuthorDID] = true + } + for _, ci := range collectionItems { + allDIDs[ci.AuthorDID] = true + } + didSlice := make([]string, 0, len(allDIDs)) + for did := range allDIDs { + didSlice = append(didSlice, did) + } + profiles := fetchProfilesForDIDs(h.db, didSlice) + shared := &hydrationData{profiles: profiles} + + var ( + authAnnos []APIAnnotation + authHighs []APIHighlight + authBooks []APIBookmark + authCollectionItems []APICollectionItem + wg sync.WaitGroup + ) + + wg.Add(3) + go func() { + defer wg.Done() + authAnnos, _ = hydrateAnnotationsWithData(h.db, annotations, viewerDID, shared) + }() + go func() { + defer wg.Done() + authHighs, _ = hydrateHighlightsWithData(h.db, highlights, viewerDID, shared) + }() + go func() { + defer wg.Done() + authBooks, _ = hydrateBookmarksWithData(h.db, bookmarks, viewerDID, shared) + }() if len(collectionItems) > 0 { var sembleURIs []string @@ -362,9 +458,14 @@ func (h *Handler) GetFeed(w http.ResponseWriter, r *http.Request) { defer cancel() ensureSembleCardsIndexed(ctx, h.db, sembleURIs) } + wg.Add(1) + go func() { + defer wg.Done() + authCollectionItems, _ = hydrateCollectionItemsWithData(h.db, collectionItems, viewerDID, shared) + }() } - authCollectionItems, _ := hydrateCollectionItems(h.db, collectionItems, viewerDID) + wg.Wait() collectionItemURIs := make(map[string]string) for _, ci := range authCollectionItems { @@ -1194,22 +1295,92 @@ func (h *Handler) GetURLMetadata(w http.ResponseWriter, r *http.Request) { return } - client := &http.Client{Timeout: 10 * time.Second} - resp, err := client.Get(targetURL) - if err != nil { + if cached, ok := h.metaCache.get(targetURL); ok { w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(map[string]string{"title": "", "error": "failed to fetch"}) + w.Header().Set("X-Cache", "HIT") + json.NewEncoder(w).Encode(cached) return } - defer resp.Body.Close() - body, err := io.ReadAll(io.LimitReader(resp.Body, 500*1024)) - if err != nil { + sfVal, loaded := h.metaCache.inflight.LoadOrStore(targetURL, &singleflight{}) + sf := sfVal.(*singleflight) + if loaded { + sf.wg.Wait() w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(map[string]string{"title": ""}) + w.Header().Set("X-Cache", "DEDUP") + if sf.data != nil { + json.NewEncoder(w).Encode(sf.data) + } else { + json.NewEncoder(w).Encode(map[string]string{"title": "", "error": "failed to fetch"}) + } return } + sf.wg.Add(1) + defer func() { + sf.wg.Done() + go func() { + time.Sleep(100 * time.Millisecond) + h.metaCache.inflight.Delete(targetURL) + }() + }() + + select { + case h.metaSem <- struct{}{}: + defer func() { <-h.metaSem }() + case <-r.Context().Done(): + sf.data = map[string]string{"title": "", "error": "timeout"} + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(sf.data) + return + } + + data := h.fetchURLMetadata(r.Context(), targetURL) + sf.data = data + + ttl := 1 * time.Hour + if data["title"] == "" && data["error"] != "" { + ttl = 2 * time.Minute + } + h.metaCache.set(targetURL, data, ttl) + + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Cache-Control", "public, max-age=3600") + json.NewEncoder(w).Encode(data) +} + +func (h *Handler) fetchURLMetadata(ctx context.Context, targetURL string) map[string]string { + ctx, cancel := context.WithTimeout(ctx, 4*time.Second) + defer cancel() + + req, err := http.NewRequestWithContext(ctx, "GET", targetURL, nil) + if err != nil { + return map[string]string{"title": "", "error": "invalid url"} + } + req.Header.Set("User-Agent", "Margin/1.0 (metadata fetcher)") + req.Header.Set("Accept", "text/html") + + client := &http.Client{ + Timeout: 4 * time.Second, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + if len(via) >= 3 { + return fmt.Errorf("too many redirects") + } + return nil + }, + } + + resp, err := client.Do(req) + if err != nil { + return map[string]string{"title": "", "error": "failed to fetch"} + } + defer resp.Body.Close() + + body, err := io.ReadAll(io.LimitReader(resp.Body, 256*1024)) + if err != nil { + return map[string]string{"title": ""} + } + content := string(body) extract := func(key string) string { @@ -1310,15 +1481,12 @@ func (h *Handler) GetURLMetadata(w http.ResponseWriter, r *http.Request) { } } - data := map[string]string{ + return map[string]string{ "title": title, "description": description, "image": image, "icon": favicon, } - - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(data) } func (h *Handler) GetNotifications(w http.ResponseWriter, r *http.Request) { diff --git a/backend/internal/api/hydration.go b/backend/internal/api/hydration.go index 53697e9..21a7516 100644 --- a/backend/internal/api/hydration.go +++ b/backend/internal/api/hydration.go @@ -19,6 +19,8 @@ import ( var ( Cache ProfileCache = NewInMemoryCache(5 * time.Minute) ConstellationClient *constellation.Client = constellation.NewClient() // Enabled by default + + bskyHTTPClient = &http.Client{Timeout: 5 * time.Second} ) func init() { @@ -168,6 +170,11 @@ type APINotification struct { ReadAt *time.Time `json:"readAt,omitempty"` } +type hydrationData struct { + profiles map[string]Author + subscribedLabelers []string +} + func fetchCounts(ctx context.Context, database *db.DB, uris []string, viewerDID string) (likeCounts, replyCounts map[string]int, viewerLikes map[string]bool) { likeCounts = make(map[string]int) replyCounts = make(map[string]int) @@ -177,12 +184,39 @@ func fetchCounts(ctx context.Context, database *db.DB, uris []string, viewerDID return } + var wg sync.WaitGroup + var mu sync.Mutex + if database != nil { - likeCounts, _ = database.GetLikeCounts(uris) - replyCounts, _ = database.GetReplyCounts(uris) + wg.Add(2) + go func() { + defer wg.Done() + if lc, err := database.GetLikeCounts(uris); err == nil { + mu.Lock() + likeCounts = lc + mu.Unlock() + } + }() + go func() { + defer wg.Done() + if rc, err := database.GetReplyCounts(uris); err == nil { + mu.Lock() + replyCounts = rc + mu.Unlock() + } + }() if viewerDID != "" { - viewerLikes, _ = database.GetViewerLikes(viewerDID, uris) + wg.Add(1) + go func() { + defer wg.Done() + if vl, err := database.GetViewerLikes(viewerDID, uris); err == nil { + mu.Lock() + viewerLikes = vl + mu.Unlock() + } + }() } + wg.Wait() } if ConstellationClient != nil && len(uris) <= 5 { @@ -205,28 +239,99 @@ func fetchCounts(ctx context.Context, database *db.DB, uris []string, viewerDID return } +func fetchEngagementData(database *db.DB, uris []string, authorDIDs []string, viewerDID string) ( + likeCounts, replyCounts map[string]int, + viewerLikes map[string]bool, + uriLabels, didLabels map[string][]db.ContentLabel, + editTimes map[string]time.Time, +) { + likeCounts = make(map[string]int) + replyCounts = make(map[string]int) + viewerLikes = make(map[string]bool) + uriLabels = make(map[string][]db.ContentLabel) + didLabels = make(map[string][]db.ContentLabel) + editTimes = make(map[string]time.Time) + + if len(uris) == 0 { + return + } + + subscribedLabelers := getSubscribedLabelers(database, viewerDID) + labelerDIDs := appendUnique(subscribedLabelers, authorDIDs) + + var wg sync.WaitGroup + var mu sync.Mutex + + wg.Add(1) + go func() { + defer wg.Done() + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) + defer cancel() + lc, rc, vl := fetchCounts(ctx, database, uris, viewerDID) + mu.Lock() + likeCounts = lc + replyCounts = rc + viewerLikes = vl + mu.Unlock() + }() + + wg.Add(1) + go func() { + defer wg.Done() + if ul, err := database.GetContentLabelsForURIs(uris, labelerDIDs); err == nil { + mu.Lock() + uriLabels = ul + mu.Unlock() + } + }() + + wg.Add(1) + go func() { + defer wg.Done() + if dl, err := database.GetContentLabelsForDIDs(authorDIDs, labelerDIDs); err == nil { + mu.Lock() + didLabels = dl + mu.Unlock() + } + }() + + wg.Add(1) + go func() { + defer wg.Done() + if et, err := database.GetLatestEditTimes(uris); err == nil { + mu.Lock() + editTimes = et + mu.Unlock() + } + }() + + wg.Wait() + return +} + func hydrateAnnotations(database *db.DB, annotations []db.Annotation, viewerDID string) ([]APIAnnotation, error) { + return hydrateAnnotationsWithData(database, annotations, viewerDID, nil) +} + +func hydrateAnnotationsWithData(database *db.DB, annotations []db.Annotation, viewerDID string, shared *hydrationData) ([]APIAnnotation, error) { if len(annotations) == 0 { return []APIAnnotation{}, nil } - profiles := fetchProfilesForDIDs(database, collectDIDs(annotations, func(a db.Annotation) string { return a.AuthorDID })) + var profiles map[string]Author + if shared != nil && shared.profiles != nil { + profiles = shared.profiles + } else { + profiles = fetchProfilesForDIDs(database, collectDIDs(annotations, func(a db.Annotation) string { return a.AuthorDID })) + } uris := make([]string, len(annotations)) for i, a := range annotations { uris[i] = a.URI } - - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) - defer cancel() - likeCounts, replyCounts, viewerLikes := fetchCounts(ctx, database, uris, viewerDID) - - subscribedLabelers := getSubscribedLabelers(database, viewerDID) authorDIDs := collectDIDs(annotations, func(a db.Annotation) string { return a.AuthorDID }) - labelerDIDs := appendUnique(subscribedLabelers, authorDIDs) - uriLabels, _ := database.GetContentLabelsForURIs(uris, labelerDIDs) - didLabels, _ := database.GetContentLabelsForDIDs(authorDIDs, labelerDIDs) - editTimes, _ := database.GetLatestEditTimes(uris) + + likeCounts, replyCounts, viewerLikes, uriLabels, didLabels, editTimes := fetchEngagementData(database, uris, authorDIDs, viewerDID) result := make([]APIAnnotation, len(annotations)) for i, a := range annotations { @@ -303,27 +408,28 @@ func hydrateAnnotations(database *db.DB, annotations []db.Annotation, viewerDID } func hydrateHighlights(database *db.DB, highlights []db.Highlight, viewerDID string) ([]APIHighlight, error) { + return hydrateHighlightsWithData(database, highlights, viewerDID, nil) +} + +func hydrateHighlightsWithData(database *db.DB, highlights []db.Highlight, viewerDID string, shared *hydrationData) ([]APIHighlight, error) { if len(highlights) == 0 { return []APIHighlight{}, nil } - profiles := fetchProfilesForDIDs(database, collectDIDs(highlights, func(h db.Highlight) string { return h.AuthorDID })) + var profiles map[string]Author + if shared != nil && shared.profiles != nil { + profiles = shared.profiles + } else { + profiles = fetchProfilesForDIDs(database, collectDIDs(highlights, func(h db.Highlight) string { return h.AuthorDID })) + } uris := make([]string, len(highlights)) for i, h := range highlights { uris[i] = h.URI } - - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) - defer cancel() - likeCounts, replyCounts, viewerLikes := fetchCounts(ctx, database, uris, viewerDID) - - subscribedLabelers := getSubscribedLabelers(database, viewerDID) authorDIDs := collectDIDs(highlights, func(h db.Highlight) string { return h.AuthorDID }) - labelerDIDs := appendUnique(subscribedLabelers, authorDIDs) - uriLabels, _ := database.GetContentLabelsForURIs(uris, labelerDIDs) - didLabels, _ := database.GetContentLabelsForDIDs(authorDIDs, labelerDIDs) - editTimes, _ := database.GetLatestEditTimes(uris) + + likeCounts, replyCounts, viewerLikes, uriLabels, didLabels, editTimes := fetchEngagementData(database, uris, authorDIDs, viewerDID) result := make([]APIHighlight, len(highlights)) for i, h := range highlights { @@ -385,27 +491,28 @@ func hydrateHighlights(database *db.DB, highlights []db.Highlight, viewerDID str } func hydrateBookmarks(database *db.DB, bookmarks []db.Bookmark, viewerDID string) ([]APIBookmark, error) { + return hydrateBookmarksWithData(database, bookmarks, viewerDID, nil) +} + +func hydrateBookmarksWithData(database *db.DB, bookmarks []db.Bookmark, viewerDID string, shared *hydrationData) ([]APIBookmark, error) { if len(bookmarks) == 0 { return []APIBookmark{}, nil } - profiles := fetchProfilesForDIDs(database, collectDIDs(bookmarks, func(b db.Bookmark) string { return b.AuthorDID })) + var profiles map[string]Author + if shared != nil && shared.profiles != nil { + profiles = shared.profiles + } else { + profiles = fetchProfilesForDIDs(database, collectDIDs(bookmarks, func(b db.Bookmark) string { return b.AuthorDID })) + } uris := make([]string, len(bookmarks)) for i, b := range bookmarks { uris[i] = b.URI } - - ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) - defer cancel() - likeCounts, replyCounts, viewerLikes := fetchCounts(ctx, database, uris, viewerDID) - - subscribedLabelers := getSubscribedLabelers(database, viewerDID) authorDIDs := collectDIDs(bookmarks, func(b db.Bookmark) string { return b.AuthorDID }) - labelerDIDs := appendUnique(subscribedLabelers, authorDIDs) - uriLabels, _ := database.GetContentLabelsForURIs(uris, labelerDIDs) - didLabels, _ := database.GetContentLabelsForDIDs(authorDIDs, labelerDIDs) - editTimes, _ := database.GetLatestEditTimes(uris) + + likeCounts, replyCounts, viewerLikes, uriLabels, didLabels, editTimes := fetchEngagementData(database, uris, authorDIDs, viewerDID) result := make([]APIBookmark, len(bookmarks)) for i, b := range bookmarks { @@ -504,8 +611,11 @@ func collectDIDs[T any](items []T, getDID func(T) string) []string { func fetchProfilesForDIDs(database *db.DB, dids []string) map[string]Author { profiles := make(map[string]Author) - missingDIDs := make([]string, 0) + if len(dids) == 0 { + return profiles + } + missingDIDs := make([]string, 0) for _, did := range dids { if author, ok := Cache.Get(did); ok { profiles[did] = author @@ -514,6 +624,35 @@ func fetchProfilesForDIDs(database *db.DB, dids []string) map[string]Author { } } + if len(missingDIDs) == 0 { + return profiles + } + + if database != nil { + marginProfiles, err := database.GetProfilesByDIDs(missingDIDs) + if err == nil { + for did, mp := range marginProfiles { + author := Author{DID: did} + if mp.DisplayName != nil && *mp.DisplayName != "" { + author.DisplayName = *mp.DisplayName + } + if mp.Avatar != nil && *mp.Avatar != "" { + author.Avatar = getProxiedAvatarURL(did, *mp.Avatar) + } + profiles[did] = author + Cache.Set(did, author) + } + } + + stillMissing := make([]string, 0) + for _, did := range missingDIDs { + if _, ok := profiles[did]; !ok { + stillMissing = append(stillMissing, did) + } + } + missingDIDs = stillMissing + } + if len(missingDIDs) > 0 { batchSize := 25 var wg sync.WaitGroup @@ -532,10 +671,11 @@ func fetchProfilesForDIDs(database *db.DB, dids []string) map[string]Author { fetched, err := fetchProfiles(actors) if err == nil { mu.Lock() - defer mu.Unlock() for k, v := range fetched { profiles[k] = v + Cache.Set(k, v) } + mu.Unlock() } }(batch) } @@ -548,11 +688,8 @@ func fetchProfilesForDIDs(database *db.DB, dids []string) map[string]Author { for did, mp := range marginProfiles { author, exists := profiles[did] if !exists { - author = Author{ - DID: did, - } + author = Author{DID: did} } - if mp.DisplayName != nil && *mp.DisplayName != "" { author.DisplayName = *mp.DisplayName } @@ -560,7 +697,6 @@ func fetchProfilesForDIDs(database *db.DB, dids []string) map[string]Author { author.Avatar = getProxiedAvatarURL(did, *mp.Avatar) } profiles[did] = author - Cache.Set(did, author) } } @@ -579,7 +715,7 @@ func fetchProfiles(dids []string) (map[string]Author, error) { q.Add("actors", did) } - resp, err := http.Get(config.Get().BskyGetProfilesURL() + "?" + q.Encode()) + resp, err := bskyHTTPClient.Get(config.Get().BskyGetProfilesURL() + "?" + q.Encode()) if err != nil { logger.Error("Hydration fetch error: %v", err) return nil, err @@ -618,11 +754,20 @@ func fetchProfiles(dids []string) (map[string]Author, error) { } func hydrateCollectionItems(database *db.DB, items []db.CollectionItem, viewerDID string) ([]APICollectionItem, error) { + return hydrateCollectionItemsWithData(database, items, viewerDID, nil) +} + +func hydrateCollectionItemsWithData(database *db.DB, items []db.CollectionItem, viewerDID string, shared *hydrationData) ([]APICollectionItem, error) { if len(items) == 0 { return []APICollectionItem{}, nil } - profiles := fetchProfilesForDIDs(database, collectDIDs(items, func(i db.CollectionItem) string { return i.AuthorDID })) + var profiles map[string]Author + if shared != nil && shared.profiles != nil { + profiles = shared.profiles + } else { + profiles = fetchProfilesForDIDs(database, collectDIDs(items, func(i db.CollectionItem) string { return i.AuthorDID })) + } var collectionURIs []string var annotationURIs []string @@ -647,7 +792,6 @@ func hydrateCollectionItems(database *db.DB, items []db.CollectionItem, viewerDI if len(collectionURIs) > 0 { colls, err := database.GetCollectionsByURIs(collectionURIs) if err == nil { - collProfiles := fetchProfilesForDIDs(database, collectDIDs(colls, func(c db.Collection) string { return c.AuthorDID })) for _, coll := range colls { icon := "" if coll.Icon != nil { @@ -662,7 +806,7 @@ func hydrateCollectionItems(database *db.DB, items []db.CollectionItem, viewerDI Name: coll.Name, Description: desc, Icon: icon, - Creator: collProfiles[coll.AuthorDID], + Creator: profiles[coll.AuthorDID], CreatedAt: coll.CreatedAt, IndexedAt: coll.IndexedAt, } @@ -670,39 +814,66 @@ func hydrateCollectionItems(database *db.DB, items []db.CollectionItem, viewerDI } } - annotationsMap := make(map[string]APIAnnotation) + var ( + annotationsMap = make(map[string]APIAnnotation) + highlightsMap = make(map[string]APIHighlight) + bookmarksMap = make(map[string]APIBookmark) + wg sync.WaitGroup + mu sync.Mutex + ) + + nestedShared := &hydrationData{profiles: profiles} + if len(annotationURIs) > 0 { - rawAnnos, err := database.GetAnnotationsByURIs(annotationURIs) - if err == nil { - hydrated, _ := hydrateAnnotations(database, rawAnnos, viewerDID) - for _, a := range hydrated { - annotationsMap[a.ID] = a + wg.Add(1) + go func() { + defer wg.Done() + rawAnnos, err := database.GetAnnotationsByURIs(annotationURIs) + if err == nil { + hydrated, _ := hydrateAnnotationsWithData(database, rawAnnos, viewerDID, nestedShared) + mu.Lock() + for _, a := range hydrated { + annotationsMap[a.ID] = a + } + mu.Unlock() } - } + }() } - highlightsMap := make(map[string]APIHighlight) if len(highlightURIs) > 0 { - rawHighlights, err := database.GetHighlightsByURIs(highlightURIs) - if err == nil { - hydrated, _ := hydrateHighlights(database, rawHighlights, viewerDID) - for _, h := range hydrated { - highlightsMap[h.ID] = h + wg.Add(1) + go func() { + defer wg.Done() + rawHighlights, err := database.GetHighlightsByURIs(highlightURIs) + if err == nil { + hydrated, _ := hydrateHighlightsWithData(database, rawHighlights, viewerDID, nestedShared) + mu.Lock() + for _, h := range hydrated { + highlightsMap[h.ID] = h + } + mu.Unlock() } - } + }() } - bookmarksMap := make(map[string]APIBookmark) if len(bookmarkURIs) > 0 { - rawBookmarks, err := database.GetBookmarksByURIs(bookmarkURIs) - if err == nil { - hydrated, _ := hydrateBookmarks(database, rawBookmarks, viewerDID) - for _, b := range hydrated { - bookmarksMap[b.ID] = b + wg.Add(1) + go func() { + defer wg.Done() + rawBookmarks, err := database.GetBookmarksByURIs(bookmarkURIs) + if err == nil { + hydrated, _ := hydrateBookmarksWithData(database, rawBookmarks, viewerDID, nestedShared) + mu.Lock() + for _, b := range hydrated { + bookmarksMap[b.ID] = b + } + mu.Unlock() } - } + }() } + wg.Wait() + var result []APICollectionItem for _, item := range items { apiItem := APICollectionItem{ diff --git a/backend/internal/api/pds.go b/backend/internal/api/pds.go index abe5d90..0601bb5 100644 --- a/backend/internal/api/pds.go +++ b/backend/internal/api/pds.go @@ -11,6 +11,8 @@ import ( "margin.at/internal/xrpc" ) +var pdsClient = &http.Client{Timeout: 10 * time.Second} + func (h *Handler) FetchLatestUserRecords(r *http.Request, did string, collection string, limit int) ([]interface{}, error) { session, err := h.refresher.GetSessionWithAutoRefresh(r) if err != nil { @@ -25,7 +27,7 @@ func (h *Handler) FetchLatestUserRecords(r *http.Request, did string, collection req, _ := http.NewRequestWithContext(r.Context(), "GET", url, nil) req.Header.Set("Authorization", "Bearer "+client.AccessToken) - resp, err := http.DefaultClient.Do(req) + resp, err := pdsClient.Do(req) if err != nil { return fmt.Errorf("failed to fetch %s: %w", collection, err) } diff --git a/backend/internal/db/db.go b/backend/internal/db/db.go index eb5b6a7..c3a1635 100644 --- a/backend/internal/db/db.go +++ b/backend/internal/db/db.go @@ -8,12 +8,10 @@ import ( "time" _ "github.com/lib/pq" - _ "github.com/mattn/go-sqlite3" ) type DB struct { *sql.DB - driver string } type Annotation struct { @@ -204,48 +202,28 @@ type ContentLabel struct { } func New(dsn string) (*DB, error) { - driver := "sqlite3" - if strings.HasPrefix(dsn, "postgres://") || strings.HasPrefix(dsn, "postgresql://") { - driver = "postgres" + if !strings.HasPrefix(dsn, "postgres://") && !strings.HasPrefix(dsn, "postgresql://") { + return nil, fmt.Errorf("only PostgreSQL is supported, DSN must start with postgres:// or postgresql://") } - db, err := sql.Open(driver, dsn) + db, err := sql.Open("postgres", dsn) if err != nil { return nil, fmt.Errorf("failed to open database connection: %w", err) } - if driver == "sqlite3" { - if _, err := db.Exec("PRAGMA journal_mode=WAL;"); err != nil { - return nil, fmt.Errorf("failed to set WAL mode: %w", err) - } - db.Exec("PRAGMA synchronous=NORMAL;") - db.Exec("PRAGMA busy_timeout=5000;") - db.Exec("PRAGMA cache_size=-2000;") - db.Exec("PRAGMA foreign_keys=ON;") - - db.SetMaxOpenConns(25) - db.SetMaxIdleConns(25) - db.SetConnMaxLifetime(5 * time.Minute) - } else { - db.SetMaxOpenConns(50) - db.SetMaxIdleConns(25) - db.SetConnMaxLifetime(10 * time.Minute) - } + db.SetMaxOpenConns(25) + db.SetMaxIdleConns(10) + db.SetConnMaxLifetime(5 * time.Minute) + db.SetConnMaxIdleTime(2 * time.Minute) if err := db.Ping(); err != nil { return nil, fmt.Errorf("failed to ping database: %w", err) } - return &DB{DB: db, driver: driver}, nil + return &DB{DB: db}, nil } func (db *DB) Migrate() error { - - dateType := "DATETIME" - if db.driver == "postgres" { - dateType = "TIMESTAMP" - } - _, err := db.Exec(` CREATE TABLE IF NOT EXISTS annotations ( uri TEXT PRIMARY KEY, @@ -259,8 +237,8 @@ func (db *DB) Migrate() error { target_title TEXT, selector_json TEXT, tags_json TEXT, - created_at ` + dateType + ` NOT NULL, - indexed_at ` + dateType + ` NOT NULL, + created_at TIMESTAMP NOT NULL, + indexed_at TIMESTAMP NOT NULL, cid TEXT )`) if err != nil { @@ -272,6 +250,8 @@ func (db *DB) Migrate() error { db.Exec(`CREATE INDEX IF NOT EXISTS idx_annotations_author_did ON annotations(author_did)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_annotations_motivation ON annotations(motivation)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_annotations_created_at ON annotations(created_at DESC)`) + db.Exec(`CREATE INDEX IF NOT EXISTS idx_annotations_author_created ON annotations(author_did, created_at DESC)`) + db.Exec(`CREATE INDEX IF NOT EXISTS idx_annotations_uri_pattern ON annotations(uri text_pattern_ops)`) db.Exec(`CREATE TABLE IF NOT EXISTS highlights ( uri TEXT PRIMARY KEY, @@ -282,13 +262,15 @@ func (db *DB) Migrate() error { selector_json TEXT, color TEXT, tags_json TEXT, - created_at ` + dateType + ` NOT NULL, - indexed_at ` + dateType + ` NOT NULL, + created_at TIMESTAMP NOT NULL, + indexed_at TIMESTAMP NOT NULL, cid TEXT )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_highlights_target_hash ON highlights(target_hash)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_highlights_author_did ON highlights(author_did)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_highlights_created_at ON highlights(created_at DESC)`) + db.Exec(`CREATE INDEX IF NOT EXISTS idx_highlights_author_created ON highlights(author_did, created_at DESC)`) + db.Exec(`CREATE INDEX IF NOT EXISTS idx_highlights_uri_pattern ON highlights(uri text_pattern_ops)`) db.Exec(`CREATE TABLE IF NOT EXISTS bookmarks ( uri TEXT PRIMARY KEY, @@ -298,13 +280,15 @@ func (db *DB) Migrate() error { title TEXT, description TEXT, tags_json TEXT, - created_at ` + dateType + ` NOT NULL, - indexed_at ` + dateType + ` NOT NULL, + created_at TIMESTAMP NOT NULL, + indexed_at TIMESTAMP NOT NULL, cid TEXT )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_bookmarks_source_hash ON bookmarks(source_hash)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_bookmarks_author_did ON bookmarks(author_did)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_bookmarks_created_at ON bookmarks(created_at DESC)`) + db.Exec(`CREATE INDEX IF NOT EXISTS idx_bookmarks_author_created ON bookmarks(author_did, created_at DESC)`) + db.Exec(`CREATE INDEX IF NOT EXISTS idx_bookmarks_uri_pattern ON bookmarks(uri text_pattern_ops)`) db.Exec(`CREATE TABLE IF NOT EXISTS replies ( uri TEXT PRIMARY KEY, @@ -313,20 +297,21 @@ func (db *DB) Migrate() error { root_uri TEXT NOT NULL, text TEXT NOT NULL, format TEXT DEFAULT 'text/plain', - created_at ` + dateType + ` NOT NULL, - indexed_at ` + dateType + ` NOT NULL, + created_at TIMESTAMP NOT NULL, + indexed_at TIMESTAMP NOT NULL, cid TEXT )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_replies_parent_uri ON replies(parent_uri)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_replies_root_uri ON replies(root_uri)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_replies_created_at ON replies(created_at DESC)`) + db.Exec(`CREATE INDEX IF NOT EXISTS idx_replies_author_did ON replies(author_did)`) db.Exec(`CREATE TABLE IF NOT EXISTS likes ( uri TEXT PRIMARY KEY, author_did TEXT NOT NULL, subject_uri TEXT NOT NULL, - created_at ` + dateType + ` NOT NULL, - indexed_at ` + dateType + ` NOT NULL + created_at TIMESTAMP NOT NULL, + indexed_at TIMESTAMP NOT NULL )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_likes_subject_uri ON likes(subject_uri)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_likes_author_did ON likes(author_did)`) @@ -338,8 +323,8 @@ func (db *DB) Migrate() error { name TEXT NOT NULL, description TEXT, icon TEXT, - created_at ` + dateType + ` NOT NULL, - indexed_at ` + dateType + ` NOT NULL + created_at TIMESTAMP NOT NULL, + indexed_at TIMESTAMP NOT NULL )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_collections_author_did ON collections(author_did)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_collections_created_at ON collections(created_at DESC)`) @@ -350,8 +335,8 @@ func (db *DB) Migrate() error { collection_uri TEXT NOT NULL, annotation_uri TEXT NOT NULL, position INTEGER DEFAULT 0, - created_at ` + dateType + ` NOT NULL, - indexed_at ` + dateType + ` NOT NULL + created_at TIMESTAMP NOT NULL, + indexed_at TIMESTAMP NOT NULL )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_collection_items_collection ON collection_items(collection_uri)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_collection_items_annotation ON collection_items(annotation_uri)`) @@ -364,49 +349,46 @@ func (db *DB) Migrate() error { access_token TEXT NOT NULL, refresh_token TEXT NOT NULL, dpop_key TEXT, - created_at ` + dateType + ` NOT NULL, - expires_at ` + dateType + ` NOT NULL + created_at TIMESTAMP NOT NULL, + expires_at TIMESTAMP NOT NULL )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_sessions_did ON sessions(did)`) - - autoInc := "INTEGER PRIMARY KEY AUTOINCREMENT" - if db.driver == "postgres" { - autoInc = "SERIAL PRIMARY KEY" - } + db.Exec(`CREATE INDEX IF NOT EXISTS idx_sessions_expires ON sessions(expires_at)`) db.Exec(`CREATE TABLE IF NOT EXISTS edit_history ( - id ` + autoInc + `, + id SERIAL PRIMARY KEY, uri TEXT NOT NULL, record_type TEXT NOT NULL, previous_content TEXT NOT NULL, previous_cid TEXT, - edited_at ` + dateType + ` NOT NULL + edited_at TIMESTAMP NOT NULL )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_edit_history_uri ON edit_history(uri)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_edit_history_edited_at ON edit_history(edited_at DESC)`) db.Exec(`CREATE TABLE IF NOT EXISTS notifications ( - id ` + autoInc + `, + id SERIAL PRIMARY KEY, recipient_did TEXT NOT NULL, actor_did TEXT NOT NULL, type TEXT NOT NULL, subject_uri TEXT NOT NULL, - created_at ` + dateType + ` NOT NULL, - read_at ` + dateType + ` + created_at TIMESTAMP NOT NULL, + read_at TIMESTAMP )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_notifications_recipient ON notifications(recipient_did)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_notifications_created_at ON notifications(created_at DESC)`) + db.Exec(`CREATE INDEX IF NOT EXISTS idx_notifications_unread ON notifications(recipient_did) WHERE read_at IS NULL`) db.Exec(`CREATE TABLE IF NOT EXISTS api_keys ( id TEXT PRIMARY KEY, owner_did TEXT NOT NULL, name TEXT NOT NULL, key_hash TEXT NOT NULL, - created_at ` + dateType + ` NOT NULL, - last_used_at ` + dateType + `, + created_at TIMESTAMP NOT NULL, + last_used_at TIMESTAMP, uri TEXT, cid TEXT, - indexed_at ` + dateType + ` DEFAULT CURRENT_TIMESTAMP + indexed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_api_keys_owner ON api_keys(owner_did)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_api_keys_hash ON api_keys(key_hash)`) @@ -419,8 +401,8 @@ func (db *DB) Migrate() error { bio TEXT, website TEXT, links_json TEXT, - created_at ` + dateType + ` NOT NULL, - indexed_at ` + dateType + ` NOT NULL, + created_at TIMESTAMP NOT NULL, + indexed_at TIMESTAMP NOT NULL, cid TEXT )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_profiles_author_did ON profiles(author_did)`) @@ -432,8 +414,8 @@ func (db *DB) Migrate() error { subscribed_labelers TEXT, label_preferences TEXT, disable_external_link_warning BOOLEAN, - created_at ` + dateType + ` NOT NULL, - indexed_at ` + dateType + ` NOT NULL, + created_at TIMESTAMP NOT NULL, + indexed_at TIMESTAMP NOT NULL, cid TEXT )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_preferences_author_did ON preferences(author_did)`) @@ -443,39 +425,39 @@ func (db *DB) Migrate() error { db.Exec(`CREATE TABLE IF NOT EXISTS cursors ( id TEXT PRIMARY KEY, last_cursor BIGINT NOT NULL, - updated_at ` + dateType + ` NOT NULL + updated_at TIMESTAMP NOT NULL )`) db.Exec(`CREATE TABLE IF NOT EXISTS blocks ( - id ` + autoInc + `, + id SERIAL PRIMARY KEY, actor_did TEXT NOT NULL, subject_did TEXT NOT NULL, - created_at ` + dateType + ` NOT NULL, + created_at TIMESTAMP NOT NULL, UNIQUE(actor_did, subject_did) )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_blocks_actor ON blocks(actor_did)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_blocks_subject ON blocks(subject_did)`) db.Exec(`CREATE TABLE IF NOT EXISTS mutes ( - id ` + autoInc + `, + id SERIAL PRIMARY KEY, actor_did TEXT NOT NULL, subject_did TEXT NOT NULL, - created_at ` + dateType + ` NOT NULL, + created_at TIMESTAMP NOT NULL, UNIQUE(actor_did, subject_did) )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_mutes_actor ON mutes(actor_did)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_mutes_subject ON mutes(subject_did)`) db.Exec(`CREATE TABLE IF NOT EXISTS moderation_reports ( - id ` + autoInc + `, + id SERIAL PRIMARY KEY, reporter_did TEXT NOT NULL, subject_did TEXT NOT NULL, subject_uri TEXT, reason_type TEXT NOT NULL, reason_text TEXT, status TEXT NOT NULL DEFAULT 'pending', - created_at ` + dateType + ` NOT NULL, - resolved_at ` + dateType + `, + created_at TIMESTAMP NOT NULL, + resolved_at TIMESTAMP, resolved_by TEXT )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_mod_reports_status ON moderation_reports(status)`) @@ -483,23 +465,23 @@ func (db *DB) Migrate() error { db.Exec(`CREATE INDEX IF NOT EXISTS idx_mod_reports_reporter ON moderation_reports(reporter_did)`) db.Exec(`CREATE TABLE IF NOT EXISTS moderation_actions ( - id ` + autoInc + `, + id SERIAL PRIMARY KEY, report_id INTEGER NOT NULL, actor_did TEXT NOT NULL, action TEXT NOT NULL, comment TEXT, - created_at ` + dateType + ` NOT NULL + created_at TIMESTAMP NOT NULL )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_mod_actions_report ON moderation_actions(report_id)`) db.Exec(`CREATE TABLE IF NOT EXISTS content_labels ( - id ` + autoInc + `, + id SERIAL PRIMARY KEY, src TEXT NOT NULL, uri TEXT NOT NULL, val TEXT NOT NULL, neg INTEGER NOT NULL DEFAULT 0, created_by TEXT NOT NULL, - created_at ` + dateType + ` NOT NULL + created_at TIMESTAMP NOT NULL )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_content_labels_uri ON content_labels(uri)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_content_labels_src ON content_labels(src)`) @@ -511,7 +493,7 @@ func (db *DB) Migrate() error { name TEXT NOT NULL, description TEXT, show_in_discover BOOLEAN NOT NULL DEFAULT true, - indexed_at ` + dateType + ` NOT NULL + indexed_at TIMESTAMP NOT NULL )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_publications_author ON publications(author_did)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_publications_url ON publications(url)`) @@ -526,8 +508,8 @@ func (db *DB) Migrate() error { text_content TEXT, tags_json TEXT, canonical_url TEXT, - published_at ` + dateType + ` NOT NULL, - indexed_at ` + dateType + ` NOT NULL + published_at TIMESTAMP NOT NULL, + indexed_at TIMESTAMP NOT NULL )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_documents_author ON documents(author_did)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_documents_site ON documents(site)`) @@ -544,26 +526,14 @@ func (db *DB) GetProfilesByDIDs(dids []string) (map[string]*Profile, error) { return nil, nil } - query := `SELECT uri, author_did, display_name, bio, avatar, website, links_json, created_at, indexed_at FROM profiles WHERE author_did IN (` - args := make([]interface{}, len(dids)) placeholders := make([]string, len(dids)) - + args := make([]interface{}, len(dids)) for i, did := range dids { placeholders[i] = fmt.Sprintf("$%d", i+1) args[i] = did } - query += strings.Join(placeholders, ",") + ")" - - if db.driver == "sqlite3" { - query = strings.ReplaceAll(query, "$", "?") - - placeholders = make([]string, len(dids)) - for i := range dids { - placeholders[i] = "?" - } - query = `SELECT uri, author_did, display_name, bio, avatar, website, links_json, created_at, indexed_at FROM profiles WHERE author_did IN (` + strings.Join(placeholders, ",") + ")" - } + query := `SELECT uri, author_did, display_name, bio, avatar, website, links_json, created_at, indexed_at FROM profiles WHERE author_did IN (` + strings.Join(placeholders, ",") + ")" rows, err := db.Query(query, args...) if err != nil { @@ -597,10 +567,10 @@ func (db *DB) GetCursor(id string) (int64, error) { func (db *DB) SetCursor(id string, cursor int64) error { query := ` - INSERT INTO cursors (id, last_cursor, updated_at) - VALUES ($1, $2, $3) - ON CONFLICT(id) DO UPDATE SET - last_cursor = EXCLUDED.last_cursor, + INSERT INTO cursors (id, last_cursor, updated_at) + VALUES ($1, $2, $3) + ON CONFLICT(id) DO UPDATE SET + last_cursor = EXCLUDED.last_cursor, updated_at = EXCLUDED.updated_at ` _, err := db.Exec(query, id, cursor, time.Now()) @@ -623,17 +593,17 @@ func (db *DB) GetProfile(did string) (*Profile, error) { func (db *DB) UpsertProfile(p *Profile) error { query := ` - INSERT INTO profiles (uri, author_did, display_name, avatar, bio, website, links_json, created_at, indexed_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) - ON CONFLICT(uri) DO UPDATE SET + INSERT INTO profiles (uri, author_did, display_name, avatar, bio, website, links_json, created_at, indexed_at) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) + ON CONFLICT(uri) DO UPDATE SET display_name = EXCLUDED.display_name, avatar = EXCLUDED.avatar, - bio = EXCLUDED.bio, + bio = EXCLUDED.bio, website = EXCLUDED.website, links_json = EXCLUDED.links_json, indexed_at = EXCLUDED.indexed_at ` - _, err := db.Exec(db.Rebind(query), p.URI, p.AuthorDID, p.DisplayName, p.Avatar, p.Bio, p.Website, p.LinksJSON, p.CreatedAt, p.IndexedAt) + _, err := db.Exec(query, p.URI, p.AuthorDID, p.DisplayName, p.Avatar, p.Bio, p.Website, p.LinksJSON, p.CreatedAt, p.IndexedAt) return err } @@ -672,9 +642,9 @@ func (db *DB) GetPreferences(did string) (*Preferences, error) { func (db *DB) UpsertPreferences(p *Preferences) error { query := ` - INSERT INTO preferences (uri, author_did, external_link_skipped_hostnames, subscribed_labelers, label_preferences, disable_external_link_warning, created_at, indexed_at, cid) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) - ON CONFLICT(uri) DO UPDATE SET + INSERT INTO preferences (uri, author_did, external_link_skipped_hostnames, subscribed_labelers, label_preferences, disable_external_link_warning, created_at, indexed_at, cid) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) + ON CONFLICT(uri) DO UPDATE SET external_link_skipped_hostnames = EXCLUDED.external_link_skipped_hostnames, subscribed_labelers = EXCLUDED.subscribed_labelers, label_preferences = EXCLUDED.label_preferences, @@ -682,7 +652,7 @@ func (db *DB) UpsertPreferences(p *Preferences) error { indexed_at = EXCLUDED.indexed_at, cid = EXCLUDED.cid ` - _, err := db.Exec(db.Rebind(query), p.URI, p.AuthorDID, p.ExternalLinkSkippedHostnames, p.SubscribedLabelers, p.LabelPreferences, p.DisableExternalLinkWarning, p.CreatedAt, p.IndexedAt, p.CID) + _, err := db.Exec(query, p.URI, p.AuthorDID, p.ExternalLinkSkippedHostnames, p.SubscribedLabelers, p.LabelPreferences, p.DisableExternalLinkWarning, p.CreatedAt, p.IndexedAt, p.CID) return err } @@ -697,7 +667,7 @@ func (db *DB) DeletePreferences(uri string) error { } func (db *DB) GetAPIKeyURIs(ownerDID string) ([]string, error) { - rows, err := db.Query(db.Rebind("SELECT uri FROM api_keys WHERE owner_did = ? AND uri IS NOT NULL AND uri != ''"), ownerDID) + rows, err := db.Query("SELECT uri FROM api_keys WHERE owner_did = $1 AND uri IS NOT NULL AND uri != ''", ownerDID) if err != nil { return nil, err } @@ -714,7 +684,7 @@ func (db *DB) GetAPIKeyURIs(ownerDID string) ([]string, error) { } func (db *DB) GetPreferenceURIs(did string) ([]string, error) { - rows, err := db.Query(db.Rebind("SELECT uri FROM preferences WHERE author_did = ? AND uri IS NOT NULL AND uri != ''"), did) + rows, err := db.Query("SELECT uri FROM preferences WHERE author_did = $1 AND uri IS NOT NULL AND uri != ''", did) if err != nil { return nil, err } @@ -731,22 +701,18 @@ func (db *DB) GetPreferenceURIs(did string) ([]string, error) { } func (db *DB) runMigrations() { - dateType := "DATETIME" - if db.driver == "postgres" { - dateType = "TIMESTAMP" - } - db.Exec(`ALTER TABLE sessions ADD COLUMN dpop_key TEXT`) - - db.Exec(`ALTER TABLE annotations ADD COLUMN motivation TEXT`) - db.Exec(`ALTER TABLE annotations ADD COLUMN body_value TEXT`) - db.Exec(`ALTER TABLE annotations ADD COLUMN body_format TEXT DEFAULT 'text/plain'`) - db.Exec(`ALTER TABLE annotations ADD COLUMN body_uri TEXT`) - db.Exec(`ALTER TABLE annotations ADD COLUMN target_source TEXT`) - db.Exec(`ALTER TABLE annotations ADD COLUMN target_hash TEXT`) - db.Exec(`ALTER TABLE annotations ADD COLUMN target_title TEXT`) - db.Exec(`ALTER TABLE annotations ADD COLUMN selector_json TEXT`) - db.Exec(`ALTER TABLE annotations ADD COLUMN tags_json TEXT`) - db.Exec(`ALTER TABLE annotations ADD COLUMN cid TEXT`) + db.Exec(`ALTER TABLE sessions ADD COLUMN IF NOT EXISTS dpop_key TEXT`) + + db.Exec(`ALTER TABLE annotations ADD COLUMN IF NOT EXISTS motivation TEXT`) + db.Exec(`ALTER TABLE annotations ADD COLUMN IF NOT EXISTS body_value TEXT`) + db.Exec(`ALTER TABLE annotations ADD COLUMN IF NOT EXISTS body_format TEXT DEFAULT 'text/plain'`) + db.Exec(`ALTER TABLE annotations ADD COLUMN IF NOT EXISTS body_uri TEXT`) + db.Exec(`ALTER TABLE annotations ADD COLUMN IF NOT EXISTS target_source TEXT`) + db.Exec(`ALTER TABLE annotations ADD COLUMN IF NOT EXISTS target_hash TEXT`) + db.Exec(`ALTER TABLE annotations ADD COLUMN IF NOT EXISTS target_title TEXT`) + db.Exec(`ALTER TABLE annotations ADD COLUMN IF NOT EXISTS selector_json TEXT`) + db.Exec(`ALTER TABLE annotations ADD COLUMN IF NOT EXISTS tags_json TEXT`) + db.Exec(`ALTER TABLE annotations ADD COLUMN IF NOT EXISTS cid TEXT`) db.Exec(`UPDATE annotations SET target_source = url WHERE target_source IS NULL AND url IS NOT NULL`) db.Exec(`UPDATE annotations SET target_hash = url_hash WHERE target_hash IS NULL AND url_hash IS NOT NULL`) @@ -754,46 +720,39 @@ func (db *DB) runMigrations() { db.Exec(`UPDATE annotations SET target_title = title WHERE target_title IS NULL AND title IS NOT NULL`) db.Exec(`UPDATE annotations SET motivation = 'commenting' WHERE motivation IS NULL`) - db.Exec(`ALTER TABLE profiles ADD COLUMN website TEXT`) - db.Exec(`ALTER TABLE profiles ADD COLUMN display_name TEXT`) - db.Exec(`ALTER TABLE profiles ADD COLUMN avatar TEXT`) + db.Exec(`ALTER TABLE profiles ADD COLUMN IF NOT EXISTS website TEXT`) + db.Exec(`ALTER TABLE profiles ADD COLUMN IF NOT EXISTS display_name TEXT`) + db.Exec(`ALTER TABLE profiles ADD COLUMN IF NOT EXISTS avatar TEXT`) - if db.driver == "postgres" { - db.Exec(`ALTER TABLE cursors ALTER COLUMN last_cursor TYPE BIGINT`) - } + db.Exec(`ALTER TABLE cursors ALTER COLUMN last_cursor TYPE BIGINT`) - db.Exec(`ALTER TABLE api_keys ADD COLUMN uri TEXT`) - db.Exec(`ALTER TABLE api_keys ADD COLUMN cid TEXT`) - db.Exec(`ALTER TABLE api_keys ADD COLUMN indexed_at ` + dateType + ` DEFAULT CURRENT_TIMESTAMP`) + db.Exec(`ALTER TABLE api_keys ADD COLUMN IF NOT EXISTS uri TEXT`) + db.Exec(`ALTER TABLE api_keys ADD COLUMN IF NOT EXISTS cid TEXT`) + db.Exec(`ALTER TABLE api_keys ADD COLUMN IF NOT EXISTS indexed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP`) - db.migrateModeration(dateType) + db.migrateModeration() - db.Exec(`ALTER TABLE preferences ADD COLUMN subscribed_labelers TEXT`) - db.Exec(`ALTER TABLE preferences ADD COLUMN label_preferences TEXT`) - db.Exec(`ALTER TABLE preferences ADD COLUMN disable_external_link_warning BOOLEAN`) + db.Exec(`ALTER TABLE preferences ADD COLUMN IF NOT EXISTS subscribed_labelers TEXT`) + db.Exec(`ALTER TABLE preferences ADD COLUMN IF NOT EXISTS label_preferences TEXT`) + db.Exec(`ALTER TABLE preferences ADD COLUMN IF NOT EXISTS disable_external_link_warning BOOLEAN`) } -func (db *DB) migrateModeration(dateType string) { +func (db *DB) migrateModeration() { _, err := db.Exec(`SELECT subject_did FROM moderation_reports LIMIT 0`) if err != nil { db.Exec(`DROP TABLE IF EXISTS moderation_reports`) db.Exec(`DROP TABLE IF EXISTS moderation_actions`) - autoInc := "INTEGER PRIMARY KEY AUTOINCREMENT" - if db.driver == "postgres" { - autoInc = "SERIAL PRIMARY KEY" - } - db.Exec(`CREATE TABLE IF NOT EXISTS moderation_reports ( - id ` + autoInc + `, + id SERIAL PRIMARY KEY, reporter_did TEXT NOT NULL, subject_did TEXT NOT NULL, subject_uri TEXT, reason_type TEXT NOT NULL, reason_text TEXT, status TEXT NOT NULL DEFAULT 'pending', - created_at ` + dateType + ` NOT NULL, - resolved_at ` + dateType + `, + created_at TIMESTAMP NOT NULL, + resolved_at TIMESTAMP, resolved_by TEXT )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_mod_reports_status ON moderation_reports(status)`) @@ -801,28 +760,24 @@ func (db *DB) migrateModeration(dateType string) { db.Exec(`CREATE INDEX IF NOT EXISTS idx_mod_reports_reporter ON moderation_reports(reporter_did)`) db.Exec(`CREATE TABLE IF NOT EXISTS moderation_actions ( - id ` + autoInc + `, + id SERIAL PRIMARY KEY, report_id INTEGER NOT NULL, actor_did TEXT NOT NULL, action TEXT NOT NULL, comment TEXT, - created_at ` + dateType + ` NOT NULL + created_at TIMESTAMP NOT NULL )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_mod_actions_report ON moderation_actions(report_id)`) } - autoInc := "INTEGER PRIMARY KEY AUTOINCREMENT" - if db.driver == "postgres" { - autoInc = "SERIAL PRIMARY KEY" - } db.Exec(`CREATE TABLE IF NOT EXISTS content_labels ( - id ` + autoInc + `, + id SERIAL PRIMARY KEY, src TEXT NOT NULL, uri TEXT NOT NULL, val TEXT NOT NULL, neg INTEGER NOT NULL DEFAULT 0, created_by TEXT NOT NULL, - created_at ` + dateType + ` NOT NULL + created_at TIMESTAMP NOT NULL )`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_content_labels_uri ON content_labels(uri)`) db.Exec(`CREATE INDEX IF NOT EXISTS idx_content_labels_src ON content_labels(src)`) @@ -832,30 +787,6 @@ func (db *DB) Close() error { return db.DB.Close() } -func (db *DB) Rebind(query string) string { - if db.driver != "postgres" { - return query - } - - if !strings.Contains(query, "?") { - return query - } - - var builder strings.Builder - builder.Grow(len(query) + 20) - - paramCount := 1 - for _, r := range query { - if r == '?' { - fmt.Fprintf(&builder, "$%d", paramCount) - paramCount++ - } else { - builder.WriteRune(r) - } - } - return builder.String() -} - func ParseSelector(selectorJSON *string) (*Selector, error) { if selectorJSON == nil || *selectorJSON == "" { return nil, nil diff --git a/backend/internal/db/pg_helpers.go b/backend/internal/db/pg_helpers.go new file mode 100644 index 0000000..06b92bb --- /dev/null +++ b/backend/internal/db/pg_helpers.go @@ -0,0 +1,22 @@ +package db + +import ( + "database/sql/driver" + "fmt" + "strings" +) + +type pqStringArray []string + +func (a pqStringArray) Value() (driver.Value, error) { + if a == nil { + return "{}", nil + } + parts := make([]string, len(a)) + for i, s := range a { + escaped := strings.ReplaceAll(s, "\\", "\\\\") + escaped = strings.ReplaceAll(escaped, "\"", "\\\"") + parts[i] = fmt.Sprintf(`"%s"`, escaped) + } + return "{" + strings.Join(parts, ",") + "}", nil +} diff --git a/backend/internal/db/queries.go b/backend/internal/db/queries.go index 78c95d6..f470d0e 100644 --- a/backend/internal/db/queries.go +++ b/backend/internal/db/queries.go @@ -35,9 +35,9 @@ func scanAnnotations(rows interface { } func (db *DB) AnnotationExists(uri string) bool { - var count int - db.QueryRow(db.Rebind(`SELECT COUNT(*) FROM annotations WHERE uri = ?`), uri).Scan(&count) - return count > 0 + var exists bool + db.QueryRow(`SELECT EXISTS(SELECT 1 FROM annotations WHERE uri = $1)`, uri).Scan(&exists) + return exists } func HashURL(rawURL string) string { @@ -71,17 +71,17 @@ func ToJSON(v interface{}) string { func (db *DB) GetAuthorByURI(uri string) (string, error) { var authorDID string - err := db.QueryRow(db.Rebind(`SELECT author_did FROM annotations WHERE uri = ?`), uri).Scan(&authorDID) + err := db.QueryRow(`SELECT author_did FROM annotations WHERE uri = $1`, uri).Scan(&authorDID) if err == nil { return authorDID, nil } - err = db.QueryRow(db.Rebind(`SELECT author_did FROM highlights WHERE uri = ?`), uri).Scan(&authorDID) + err = db.QueryRow(`SELECT author_did FROM highlights WHERE uri = $1`, uri).Scan(&authorDID) if err == nil { return authorDID, nil } - err = db.QueryRow(db.Rebind(`SELECT author_did FROM bookmarks WHERE uri = ?`), uri).Scan(&authorDID) + err = db.QueryRow(`SELECT author_did FROM bookmarks WHERE uri = $1`, uri).Scan(&authorDID) if err == nil { return authorDID, nil } @@ -89,13 +89,13 @@ func (db *DB) GetAuthorByURI(uri string) (string, error) { return "", fmt.Errorf("uri not found or no author") } -func buildPlaceholders(n int) string { +func buildPlaceholders(n, startAt int) string { if n == 0 { return "" } placeholders := make([]string, n) for i := range placeholders { - placeholders[i] = "?" + placeholders[i] = fmt.Sprintf("$%d", startAt+i) } return strings.Join(placeholders, ", ") } diff --git a/backend/internal/db/queries_annotations.go b/backend/internal/db/queries_annotations.go index 60a9cc9..d2f7790 100644 --- a/backend/internal/db/queries_annotations.go +++ b/backend/internal/db/queries_annotations.go @@ -5,32 +5,32 @@ import ( ) func (db *DB) CreateAnnotation(a *Annotation) error { - _, err := db.Exec(db.Rebind(` + _, err := db.Exec(` INSERT INTO annotations (uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) ON CONFLICT(uri) DO UPDATE SET - motivation = excluded.motivation, - body_value = excluded.body_value, - body_format = excluded.body_format, - body_uri = excluded.body_uri, - target_source = excluded.target_source, - target_hash = excluded.target_hash, - target_title = excluded.target_title, - selector_json = excluded.selector_json, - tags_json = excluded.tags_json, - indexed_at = excluded.indexed_at, - cid = excluded.cid - `), a.URI, a.AuthorDID, a.Motivation, a.BodyValue, a.BodyFormat, a.BodyURI, a.TargetSource, a.TargetHash, a.TargetTitle, a.SelectorJSON, a.TagsJSON, a.CreatedAt, a.IndexedAt, a.CID) + motivation = EXCLUDED.motivation, + body_value = EXCLUDED.body_value, + body_format = EXCLUDED.body_format, + body_uri = EXCLUDED.body_uri, + target_source = EXCLUDED.target_source, + target_hash = EXCLUDED.target_hash, + target_title = EXCLUDED.target_title, + selector_json = EXCLUDED.selector_json, + tags_json = EXCLUDED.tags_json, + indexed_at = EXCLUDED.indexed_at, + cid = EXCLUDED.cid + `, a.URI, a.AuthorDID, a.Motivation, a.BodyValue, a.BodyFormat, a.BodyURI, a.TargetSource, a.TargetHash, a.TargetTitle, a.SelectorJSON, a.TagsJSON, a.CreatedAt, a.IndexedAt, a.CID) return err } func (db *DB) GetAnnotationByURI(uri string) (*Annotation, error) { var a Annotation - err := db.QueryRow(db.Rebind(` + err := db.QueryRow(` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations - WHERE uri = ? - `), uri).Scan(&a.URI, &a.AuthorDID, &a.Motivation, &a.BodyValue, &a.BodyFormat, &a.BodyURI, &a.TargetSource, &a.TargetHash, &a.TargetTitle, &a.SelectorJSON, &a.TagsJSON, &a.CreatedAt, &a.IndexedAt, &a.CID) + WHERE uri = $1 + `, uri).Scan(&a.URI, &a.AuthorDID, &a.Motivation, &a.BodyValue, &a.BodyFormat, &a.BodyURI, &a.TargetSource, &a.TargetHash, &a.TargetTitle, &a.SelectorJSON, &a.TagsJSON, &a.CreatedAt, &a.IndexedAt, &a.CID) if err != nil { return nil, err } @@ -38,13 +38,13 @@ func (db *DB) GetAnnotationByURI(uri string) (*Annotation, error) { } func (db *DB) GetAnnotationsByTargetHash(targetHash string, limit, offset int) ([]Annotation, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations - WHERE target_hash = ? + WHERE target_hash = $1 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), targetHash, limit, offset) + LIMIT $2 OFFSET $3 + `, targetHash, limit, offset) if err != nil { return nil, err } @@ -54,13 +54,13 @@ func (db *DB) GetAnnotationsByTargetHash(targetHash string, limit, offset int) ( } func (db *DB) GetAnnotationsByAuthor(authorDID string, limit, offset int) ([]Annotation, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations - WHERE author_did = ? + WHERE author_did = $1 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, limit, offset) + LIMIT $2 OFFSET $3 + `, authorDID, limit, offset) if err != nil { return nil, err } @@ -70,13 +70,13 @@ func (db *DB) GetAnnotationsByAuthor(authorDID string, limit, offset int) ([]Ann } func (db *DB) GetMarginAnnotationsByAuthor(authorDID string, limit, offset int) ([]Annotation, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations - WHERE author_did = ? AND uri NOT LIKE '%network.cosmik%' + WHERE author_did = $1 AND uri NOT LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, limit, offset) + LIMIT $2 OFFSET $3 + `, authorDID, limit, offset) if err != nil { return nil, err } @@ -86,13 +86,13 @@ func (db *DB) GetMarginAnnotationsByAuthor(authorDID string, limit, offset int) } func (db *DB) GetSembleAnnotationsByAuthor(authorDID string, limit, offset int) ([]Annotation, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations - WHERE author_did = ? AND uri LIKE '%network.cosmik%' + WHERE author_did = $1 AND uri LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, limit, offset) + LIMIT $2 OFFSET $3 + `, authorDID, limit, offset) if err != nil { return nil, err } @@ -102,13 +102,13 @@ func (db *DB) GetSembleAnnotationsByAuthor(authorDID string, limit, offset int) } func (db *DB) GetAnnotationsByMotivation(motivation string, limit, offset int) ([]Annotation, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations - WHERE motivation = ? + WHERE motivation = $1 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), motivation, limit, offset) + LIMIT $2 OFFSET $3 + `, motivation, limit, offset) if err != nil { return nil, err } @@ -118,12 +118,12 @@ func (db *DB) GetAnnotationsByMotivation(motivation string, limit, offset int) ( } func (db *DB) GetRecentAnnotations(limit, offset int) ([]Annotation, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), limit, offset) + LIMIT $1 OFFSET $2 + `, limit, offset) if err != nil { return nil, err } @@ -134,22 +134,22 @@ func (db *DB) GetRecentAnnotations(limit, offset int) ([]Annotation, error) { func (db *DB) GetPopularAnnotations(limit, offset int) ([]Annotation, error) { since := time.Now().AddDate(0, 0, -14) - rows, err := db.Query(db.Rebind(` - SELECT - a.uri, a.author_did, a.motivation, a.body_value, a.body_format, - a.body_uri, a.target_source, a.target_hash, a.target_title, + rows, err := db.Query(` + SELECT + a.uri, a.author_did, a.motivation, a.body_value, a.body_format, + a.body_uri, a.target_source, a.target_hash, a.target_title, a.selector_json, a.tags_json, a.created_at, a.indexed_at, a.cid FROM annotations a - LEFT JOIN ( - SELECT subject_uri, COUNT(*) as cnt FROM likes GROUP BY subject_uri - ) l ON l.subject_uri = a.uri - LEFT JOIN ( - SELECT root_uri, COUNT(*) as cnt FROM replies GROUP BY root_uri - ) r ON r.root_uri = a.uri - WHERE a.created_at > ? AND (COALESCE(l.cnt, 0) + COALESCE(r.cnt, 0)) > 0 - ORDER BY (COALESCE(l.cnt, 0) + COALESCE(r.cnt, 0)) DESC, a.created_at DESC - LIMIT ? OFFSET ? - `), since, limit, offset) + LEFT JOIN LATERAL ( + SELECT COUNT(*) as cnt FROM likes WHERE subject_uri = a.uri + ) l ON true + LEFT JOIN LATERAL ( + SELECT COUNT(*) as cnt FROM replies WHERE root_uri = a.uri + ) r ON true + WHERE a.created_at > $1 AND (l.cnt + r.cnt) > 0 + ORDER BY (l.cnt + r.cnt) DESC, a.created_at DESC + LIMIT $2 OFFSET $3 + `, since, limit, offset) if err != nil { return nil, err } @@ -161,22 +161,18 @@ func (db *DB) GetPopularAnnotations(limit, offset int) ([]Annotation, error) { func (db *DB) GetShelvedAnnotations(limit, offset int) ([]Annotation, error) { olderThan := time.Now().AddDate(0, 0, -1) since := time.Now().AddDate(0, 0, -14) - rows, err := db.Query(db.Rebind(` - SELECT - a.uri, a.author_did, a.motivation, a.body_value, a.body_format, - a.body_uri, a.target_source, a.target_hash, a.target_title, + rows, err := db.Query(` + SELECT + a.uri, a.author_did, a.motivation, a.body_value, a.body_format, + a.body_uri, a.target_source, a.target_hash, a.target_title, a.selector_json, a.tags_json, a.created_at, a.indexed_at, a.cid FROM annotations a - LEFT JOIN ( - SELECT subject_uri, COUNT(*) as cnt FROM likes GROUP BY subject_uri - ) l ON l.subject_uri = a.uri - LEFT JOIN ( - SELECT root_uri, COUNT(*) as cnt FROM replies GROUP BY root_uri - ) r ON r.root_uri = a.uri - WHERE a.created_at < ? AND a.created_at > ? AND (COALESCE(l.cnt, 0) + COALESCE(r.cnt, 0)) = 0 + WHERE a.created_at < $1 AND a.created_at > $2 + AND NOT EXISTS (SELECT 1 FROM likes WHERE subject_uri = a.uri) + AND NOT EXISTS (SELECT 1 FROM replies WHERE root_uri = a.uri) ORDER BY RANDOM() - LIMIT ? OFFSET ? - `), olderThan, since, limit, offset) + LIMIT $3 OFFSET $4 + `, olderThan, since, limit, offset) if err != nil { return nil, err } @@ -186,13 +182,13 @@ func (db *DB) GetShelvedAnnotations(limit, offset int) ([]Annotation, error) { } func (db *DB) GetMarginAnnotations(limit, offset int) ([]Annotation, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations WHERE uri NOT LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), limit, offset) + LIMIT $1 OFFSET $2 + `, limit, offset) if err != nil { return nil, err } @@ -202,13 +198,13 @@ func (db *DB) GetMarginAnnotations(limit, offset int) ([]Annotation, error) { } func (db *DB) GetSembleAnnotations(limit, offset int) ([]Annotation, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations WHERE uri LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), limit, offset) + LIMIT $1 OFFSET $2 + `, limit, offset) if err != nil { return nil, err } @@ -218,14 +214,13 @@ func (db *DB) GetSembleAnnotations(limit, offset int) ([]Annotation, error) { } func (db *DB) GetAnnotationsByTag(tag string, limit, offset int) ([]Annotation, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations - WHERE tags_json LIKE ? + WHERE tags_json::jsonb ? $1 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), pattern, limit, offset) + LIMIT $2 OFFSET $3 + `, tag, limit, offset) if err != nil { return nil, err } @@ -235,14 +230,13 @@ func (db *DB) GetAnnotationsByTag(tag string, limit, offset int) ([]Annotation, } func (db *DB) GetMarginAnnotationsByTag(tag string, limit, offset int) ([]Annotation, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations - WHERE tags_json LIKE ? AND uri NOT LIKE '%network.cosmik%' + WHERE tags_json::jsonb ? $1 AND uri NOT LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), pattern, limit, offset) + LIMIT $2 OFFSET $3 + `, tag, limit, offset) if err != nil { return nil, err } @@ -252,14 +246,13 @@ func (db *DB) GetMarginAnnotationsByTag(tag string, limit, offset int) ([]Annota } func (db *DB) GetSembleAnnotationsByTag(tag string, limit, offset int) ([]Annotation, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations - WHERE tags_json LIKE ? AND uri LIKE '%network.cosmik%' + WHERE tags_json::jsonb ? $1 AND uri LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), pattern, limit, offset) + LIMIT $2 OFFSET $3 + `, tag, limit, offset) if err != nil { return nil, err } @@ -269,28 +262,27 @@ func (db *DB) GetSembleAnnotationsByTag(tag string, limit, offset int) ([]Annota } func (db *DB) DeleteAnnotation(uri string) error { - _, err := db.Exec(db.Rebind(`DELETE FROM annotations WHERE uri = ?`), uri) + _, err := db.Exec(`DELETE FROM annotations WHERE uri = $1`, uri) return err } func (db *DB) UpdateAnnotation(uri, bodyValue, tagsJSON, cid string) error { - _, err := db.Exec(db.Rebind(` - UPDATE annotations - SET body_value = ?, tags_json = ?, cid = ?, indexed_at = ? - WHERE uri = ? - `), bodyValue, tagsJSON, cid, time.Now(), uri) + _, err := db.Exec(` + UPDATE annotations + SET body_value = $1, tags_json = $2, cid = $3, indexed_at = $4 + WHERE uri = $5 + `, bodyValue, tagsJSON, cid, time.Now(), uri) return err } func (db *DB) GetAnnotationsByTagAndAuthor(tag, authorDID string, limit, offset int) ([]Annotation, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations - WHERE author_did = ? AND tags_json LIKE ? + WHERE author_did = $1 AND tags_json::jsonb ? $2 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, pattern, limit, offset) + LIMIT $3 OFFSET $4 + `, authorDID, tag, limit, offset) if err != nil { return nil, err } @@ -300,14 +292,13 @@ func (db *DB) GetAnnotationsByTagAndAuthor(tag, authorDID string, limit, offset } func (db *DB) GetMarginAnnotationsByTagAndAuthor(tag, authorDID string, limit, offset int) ([]Annotation, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations - WHERE author_did = ? AND tags_json LIKE ? AND uri NOT LIKE '%network.cosmik%' + WHERE author_did = $1 AND tags_json::jsonb ? $2 AND uri NOT LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, pattern, limit, offset) + LIMIT $3 OFFSET $4 + `, authorDID, tag, limit, offset) if err != nil { return nil, err } @@ -317,14 +308,13 @@ func (db *DB) GetMarginAnnotationsByTagAndAuthor(tag, authorDID string, limit, o } func (db *DB) GetSembleAnnotationsByTagAndAuthor(tag, authorDID string, limit, offset int) ([]Annotation, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations - WHERE author_did = ? AND tags_json LIKE ? AND uri LIKE '%network.cosmik%' + WHERE author_did = $1 AND tags_json::jsonb ? $2 AND uri LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, pattern, limit, offset) + LIMIT $3 OFFSET $4 + `, authorDID, tag, limit, offset) if err != nil { return nil, err } @@ -334,13 +324,13 @@ func (db *DB) GetSembleAnnotationsByTagAndAuthor(tag, authorDID string, limit, o } func (db *DB) GetAnnotationsByAuthorAndTargetHash(authorDID, targetHash string, limit, offset int) ([]Annotation, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations - WHERE author_did = ? AND target_hash = ? + WHERE author_did = $1 AND target_hash = $2 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, targetHash, limit, offset) + LIMIT $3 OFFSET $4 + `, authorDID, targetHash, limit, offset) if err != nil { return nil, err } @@ -354,18 +344,13 @@ func (db *DB) GetAnnotationsByURIs(uris []string) ([]Annotation, error) { return []Annotation{}, nil } - query := db.Rebind(` + query := ` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations - WHERE uri IN (` + buildPlaceholders(len(uris)) + `) - `) + WHERE uri = ANY($1) + ` - args := make([]interface{}, len(uris)) - for i, uri := range uris { - args[i] = uri - } - - rows, err := db.Query(query, args...) + rows, err := db.Query(query, pqStringArray(uris)) if err != nil { return nil, err } @@ -375,9 +360,9 @@ func (db *DB) GetAnnotationsByURIs(uris []string) ([]Annotation, error) { } func (db *DB) GetAnnotationURIs(authorDID string) ([]string, error) { - rows, err := db.Query(db.Rebind(` - SELECT uri FROM annotations WHERE author_did = ? - `), authorDID) + rows, err := db.Query(` + SELECT uri FROM annotations WHERE author_did = $1 + `, authorDID) if err != nil { return nil, err } diff --git a/backend/internal/db/queries_bookmarks.go b/backend/internal/db/queries_bookmarks.go index ae1ce56..edfe814 100644 --- a/backend/internal/db/queries_bookmarks.go +++ b/backend/internal/db/queries_bookmarks.go @@ -5,28 +5,28 @@ import ( ) func (db *DB) CreateBookmark(b *Bookmark) error { - _, err := db.Exec(db.Rebind(` + _, err := db.Exec(` INSERT INTO bookmarks (uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) ON CONFLICT(uri) DO UPDATE SET - source = excluded.source, - source_hash = excluded.source_hash, - title = excluded.title, - description = excluded.description, - tags_json = excluded.tags_json, - indexed_at = excluded.indexed_at, - cid = excluded.cid - `), b.URI, b.AuthorDID, b.Source, b.SourceHash, b.Title, b.Description, b.TagsJSON, b.CreatedAt, b.IndexedAt, b.CID) + source = EXCLUDED.source, + source_hash = EXCLUDED.source_hash, + title = EXCLUDED.title, + description = EXCLUDED.description, + tags_json = EXCLUDED.tags_json, + indexed_at = EXCLUDED.indexed_at, + cid = EXCLUDED.cid + `, b.URI, b.AuthorDID, b.Source, b.SourceHash, b.Title, b.Description, b.TagsJSON, b.CreatedAt, b.IndexedAt, b.CID) return err } func (db *DB) GetBookmarkByURI(uri string) (*Bookmark, error) { var b Bookmark - err := db.QueryRow(db.Rebind(` + err := db.QueryRow(` SELECT uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid FROM bookmarks - WHERE uri = ? - `), uri).Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID) + WHERE uri = $1 + `, uri).Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID) if err != nil { return nil, err } @@ -34,376 +34,254 @@ func (db *DB) GetBookmarkByURI(uri string) (*Bookmark, error) { } func (db *DB) GetRecentBookmarks(limit, offset int) ([]Bookmark, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid FROM bookmarks ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), limit, offset) + LIMIT $1 OFFSET $2 + `, limit, offset) if err != nil { return nil, err } defer rows.Close() - var bookmarks []Bookmark - for rows.Next() { - var b Bookmark - if err := rows.Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID); err != nil { - return nil, err - } - bookmarks = append(bookmarks, b) - } - return bookmarks, nil + return scanBookmarks(rows) } func (db *DB) GetPopularBookmarks(limit, offset int) ([]Bookmark, error) { since := time.Now().AddDate(0, 0, -14) - rows, err := db.Query(db.Rebind(` - SELECT - b.uri, b.author_did, b.source, b.source_hash, b.title, + rows, err := db.Query(` + SELECT + b.uri, b.author_did, b.source, b.source_hash, b.title, b.description, b.tags_json, b.created_at, b.indexed_at, b.cid FROM bookmarks b - LEFT JOIN ( - SELECT subject_uri, COUNT(*) as cnt FROM likes GROUP BY subject_uri - ) l ON l.subject_uri = b.uri - LEFT JOIN ( - SELECT root_uri, COUNT(*) as cnt FROM replies GROUP BY root_uri - ) r ON r.root_uri = b.uri - WHERE b.created_at > ? AND (COALESCE(l.cnt, 0) + COALESCE(r.cnt, 0)) > 0 - ORDER BY (COALESCE(l.cnt, 0) + COALESCE(r.cnt, 0)) DESC, b.created_at DESC - LIMIT ? OFFSET ? - `), since, limit, offset) + LEFT JOIN LATERAL ( + SELECT COUNT(*) as cnt FROM likes WHERE subject_uri = b.uri + ) l ON true + LEFT JOIN LATERAL ( + SELECT COUNT(*) as cnt FROM replies WHERE root_uri = b.uri + ) r ON true + WHERE b.created_at > $1 AND (l.cnt + r.cnt) > 0 + ORDER BY (l.cnt + r.cnt) DESC, b.created_at DESC + LIMIT $2 OFFSET $3 + `, since, limit, offset) if err != nil { return nil, err } defer rows.Close() - var bookmarks []Bookmark - for rows.Next() { - var b Bookmark - if err := rows.Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID); err != nil { - return nil, err - } - bookmarks = append(bookmarks, b) - } - return bookmarks, nil + return scanBookmarks(rows) } func (db *DB) GetShelvedBookmarks(limit, offset int) ([]Bookmark, error) { olderThan := time.Now().AddDate(0, 0, -1) since := time.Now().AddDate(0, 0, -14) - rows, err := db.Query(db.Rebind(` - SELECT - b.uri, b.author_did, b.source, b.source_hash, b.title, + rows, err := db.Query(` + SELECT + b.uri, b.author_did, b.source, b.source_hash, b.title, b.description, b.tags_json, b.created_at, b.indexed_at, b.cid FROM bookmarks b - LEFT JOIN ( - SELECT subject_uri, COUNT(*) as cnt FROM likes GROUP BY subject_uri - ) l ON l.subject_uri = b.uri - LEFT JOIN ( - SELECT root_uri, COUNT(*) as cnt FROM replies GROUP BY root_uri - ) r ON r.root_uri = b.uri - WHERE b.created_at < ? AND b.created_at > ? AND (COALESCE(l.cnt, 0) + COALESCE(r.cnt, 0)) = 0 + WHERE b.created_at < $1 AND b.created_at > $2 + AND NOT EXISTS (SELECT 1 FROM likes WHERE subject_uri = b.uri) + AND NOT EXISTS (SELECT 1 FROM replies WHERE root_uri = b.uri) ORDER BY RANDOM() - LIMIT ? OFFSET ? - `), olderThan, since, limit, offset) + LIMIT $3 OFFSET $4 + `, olderThan, since, limit, offset) if err != nil { return nil, err } defer rows.Close() - var bookmarks []Bookmark - for rows.Next() { - var b Bookmark - if err := rows.Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID); err != nil { - return nil, err - } - bookmarks = append(bookmarks, b) - } - return bookmarks, nil + return scanBookmarks(rows) } func (db *DB) GetMarginBookmarks(limit, offset int) ([]Bookmark, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid FROM bookmarks WHERE uri NOT LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), limit, offset) + LIMIT $1 OFFSET $2 + `, limit, offset) if err != nil { return nil, err } defer rows.Close() - var bookmarks []Bookmark - for rows.Next() { - var b Bookmark - if err := rows.Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID); err != nil { - return nil, err - } - bookmarks = append(bookmarks, b) - } - return bookmarks, nil + return scanBookmarks(rows) } func (db *DB) GetSembleBookmarks(limit, offset int) ([]Bookmark, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid FROM bookmarks WHERE uri LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), limit, offset) + LIMIT $1 OFFSET $2 + `, limit, offset) if err != nil { return nil, err } defer rows.Close() - var bookmarks []Bookmark - for rows.Next() { - var b Bookmark - if err := rows.Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID); err != nil { - return nil, err - } - bookmarks = append(bookmarks, b) - } - return bookmarks, nil + return scanBookmarks(rows) } func (db *DB) GetBookmarksByTag(tag string, limit, offset int) ([]Bookmark, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid FROM bookmarks - WHERE tags_json LIKE ? + WHERE tags_json::jsonb ? $1 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), pattern, limit, offset) + LIMIT $2 OFFSET $3 + `, tag, limit, offset) if err != nil { return nil, err } defer rows.Close() - var bookmarks []Bookmark - for rows.Next() { - var b Bookmark - if err := rows.Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID); err != nil { - return nil, err - } - bookmarks = append(bookmarks, b) - } - return bookmarks, nil + return scanBookmarks(rows) } func (db *DB) GetMarginBookmarksByTag(tag string, limit, offset int) ([]Bookmark, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid FROM bookmarks - WHERE tags_json LIKE ? AND uri NOT LIKE '%network.cosmik%' + WHERE tags_json::jsonb ? $1 AND uri NOT LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), pattern, limit, offset) + LIMIT $2 OFFSET $3 + `, tag, limit, offset) if err != nil { return nil, err } defer rows.Close() - var bookmarks []Bookmark - for rows.Next() { - var b Bookmark - if err := rows.Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID); err != nil { - return nil, err - } - bookmarks = append(bookmarks, b) - } - return bookmarks, nil + return scanBookmarks(rows) } func (db *DB) GetSembleBookmarksByTag(tag string, limit, offset int) ([]Bookmark, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid FROM bookmarks - WHERE tags_json LIKE ? AND uri LIKE '%network.cosmik%' + WHERE tags_json::jsonb ? $1 AND uri LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), pattern, limit, offset) + LIMIT $2 OFFSET $3 + `, tag, limit, offset) if err != nil { return nil, err } defer rows.Close() - var bookmarks []Bookmark - for rows.Next() { - var b Bookmark - if err := rows.Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID); err != nil { - return nil, err - } - bookmarks = append(bookmarks, b) - } - return bookmarks, nil + return scanBookmarks(rows) } func (db *DB) GetBookmarksByTagAndAuthor(tag, authorDID string, limit, offset int) ([]Bookmark, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid FROM bookmarks - WHERE author_did = ? AND tags_json LIKE ? + WHERE author_did = $1 AND tags_json::jsonb ? $2 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, pattern, limit, offset) + LIMIT $3 OFFSET $4 + `, authorDID, tag, limit, offset) if err != nil { return nil, err } defer rows.Close() - var bookmarks []Bookmark - for rows.Next() { - var b Bookmark - if err := rows.Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID); err != nil { - return nil, err - } - bookmarks = append(bookmarks, b) - } - return bookmarks, nil + return scanBookmarks(rows) } func (db *DB) GetMarginBookmarksByTagAndAuthor(tag, authorDID string, limit, offset int) ([]Bookmark, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid FROM bookmarks - WHERE author_did = ? AND tags_json LIKE ? AND uri NOT LIKE '%network.cosmik%' + WHERE author_did = $1 AND tags_json::jsonb ? $2 AND uri NOT LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, pattern, limit, offset) + LIMIT $3 OFFSET $4 + `, authorDID, tag, limit, offset) if err != nil { return nil, err } defer rows.Close() - var bookmarks []Bookmark - for rows.Next() { - var b Bookmark - if err := rows.Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID); err != nil { - return nil, err - } - bookmarks = append(bookmarks, b) - } - return bookmarks, nil + return scanBookmarks(rows) } func (db *DB) GetSembleBookmarksByTagAndAuthor(tag, authorDID string, limit, offset int) ([]Bookmark, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid FROM bookmarks - WHERE author_did = ? AND tags_json LIKE ? AND uri LIKE '%network.cosmik%' + WHERE author_did = $1 AND tags_json::jsonb ? $2 AND uri LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, pattern, limit, offset) + LIMIT $3 OFFSET $4 + `, authorDID, tag, limit, offset) if err != nil { return nil, err } defer rows.Close() - var bookmarks []Bookmark - for rows.Next() { - var b Bookmark - if err := rows.Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID); err != nil { - return nil, err - } - bookmarks = append(bookmarks, b) - } - return bookmarks, nil + return scanBookmarks(rows) } func (db *DB) GetBookmarksByAuthor(authorDID string, limit, offset int) ([]Bookmark, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid FROM bookmarks - WHERE author_did = ? + WHERE author_did = $1 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, limit, offset) + LIMIT $2 OFFSET $3 + `, authorDID, limit, offset) if err != nil { return nil, err } defer rows.Close() - var bookmarks []Bookmark - for rows.Next() { - var b Bookmark - if err := rows.Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID); err != nil { - return nil, err - } - bookmarks = append(bookmarks, b) - } - return bookmarks, nil + return scanBookmarks(rows) } func (db *DB) GetMarginBookmarksByAuthor(authorDID string, limit, offset int) ([]Bookmark, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid FROM bookmarks - WHERE author_did = ? AND uri NOT LIKE '%network.cosmik%' + WHERE author_did = $1 AND uri NOT LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, limit, offset) + LIMIT $2 OFFSET $3 + `, authorDID, limit, offset) if err != nil { return nil, err } defer rows.Close() - var bookmarks []Bookmark - for rows.Next() { - var b Bookmark - if err := rows.Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID); err != nil { - return nil, err - } - bookmarks = append(bookmarks, b) - } - return bookmarks, nil + return scanBookmarks(rows) } func (db *DB) GetSembleBookmarksByAuthor(authorDID string, limit, offset int) ([]Bookmark, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid FROM bookmarks - WHERE author_did = ? AND uri LIKE '%network.cosmik%' + WHERE author_did = $1 AND uri LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, limit, offset) + LIMIT $2 OFFSET $3 + `, authorDID, limit, offset) if err != nil { return nil, err } defer rows.Close() - var bookmarks []Bookmark - for rows.Next() { - var b Bookmark - if err := rows.Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID); err != nil { - return nil, err - } - bookmarks = append(bookmarks, b) - } - return bookmarks, nil + return scanBookmarks(rows) } func (db *DB) DeleteBookmark(uri string) error { - _, err := db.Exec(db.Rebind(`DELETE FROM bookmarks WHERE uri = ?`), uri) + _, err := db.Exec(`DELETE FROM bookmarks WHERE uri = $1`, uri) return err } func (db *DB) UpdateBookmark(uri, title, description, tagsJSON, cid string) error { - _, err := db.Exec(db.Rebind(` - UPDATE bookmarks - SET title = ?, description = ?, tags_json = ?, cid = ?, indexed_at = ? - WHERE uri = ? - `), title, description, tagsJSON, cid, time.Now(), uri) + _, err := db.Exec(` + UPDATE bookmarks + SET title = $1, description = $2, tags_json = $3, cid = $4, indexed_at = $5 + WHERE uri = $6 + `, title, description, tagsJSON, cid, time.Now(), uri) return err } @@ -412,38 +290,23 @@ func (db *DB) GetBookmarksByURIs(uris []string) ([]Bookmark, error) { return []Bookmark{}, nil } - query := db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid FROM bookmarks - WHERE uri IN (` + buildPlaceholders(len(uris)) + `) - `) - - args := make([]interface{}, len(uris)) - for i, uri := range uris { - args[i] = uri - } - - rows, err := db.Query(query, args...) + WHERE uri = ANY($1) + `, pqStringArray(uris)) if err != nil { return nil, err } defer rows.Close() - var bookmarks []Bookmark - for rows.Next() { - var b Bookmark - if err := rows.Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID); err != nil { - return nil, err - } - bookmarks = append(bookmarks, b) - } - return bookmarks, nil + return scanBookmarks(rows) } func (db *DB) GetBookmarkURIs(authorDID string) ([]string, error) { - rows, err := db.Query(db.Rebind(` - SELECT uri FROM bookmarks WHERE author_did = ? - `), authorDID) + rows, err := db.Query(` + SELECT uri FROM bookmarks WHERE author_did = $1 + `, authorDID) if err != nil { return nil, err } @@ -461,18 +324,25 @@ func (db *DB) GetBookmarkURIs(authorDID string) ([]string, error) { } func (db *DB) GetBookmarksByTargetHash(targetHash string, limit, offset int) ([]Bookmark, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid FROM bookmarks - WHERE source_hash = ? + WHERE source_hash = $1 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), targetHash, limit, offset) + LIMIT $2 OFFSET $3 + `, targetHash, limit, offset) if err != nil { return nil, err } defer rows.Close() + return scanBookmarks(rows) +} + +func scanBookmarks(rows interface { + Next() bool + Scan(...interface{}) error +}) ([]Bookmark, error) { var bookmarks []Bookmark for rows.Next() { var b Bookmark diff --git a/backend/internal/db/queries_collections.go b/backend/internal/db/queries_collections.go index 91d2bfd..e8dbfbf 100644 --- a/backend/internal/db/queries_collections.go +++ b/backend/internal/db/queries_collections.go @@ -3,25 +3,25 @@ package db import "time" func (db *DB) CreateCollection(c *Collection) error { - _, err := db.Exec(db.Rebind(` + _, err := db.Exec(` INSERT INTO collections (uri, author_did, name, description, icon, created_at, indexed_at) - VALUES (?, ?, ?, ?, ?, ?, ?) + VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT(uri) DO UPDATE SET - name = excluded.name, - description = excluded.description, - icon = excluded.icon, - indexed_at = excluded.indexed_at - `), c.URI, c.AuthorDID, c.Name, c.Description, c.Icon, c.CreatedAt, c.IndexedAt) + name = EXCLUDED.name, + description = EXCLUDED.description, + icon = EXCLUDED.icon, + indexed_at = EXCLUDED.indexed_at + `, c.URI, c.AuthorDID, c.Name, c.Description, c.Icon, c.CreatedAt, c.IndexedAt) return err } func (db *DB) GetCollectionsByAuthor(authorDID string) ([]Collection, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, name, description, icon, created_at, indexed_at FROM collections - WHERE author_did = ? + WHERE author_did = $1 ORDER BY created_at DESC - `), authorDID) + `, authorDID) if err != nil { return nil, err } @@ -40,11 +40,11 @@ func (db *DB) GetCollectionsByAuthor(authorDID string) ([]Collection, error) { func (db *DB) GetCollectionByURI(uri string) (*Collection, error) { var c Collection - err := db.QueryRow(db.Rebind(` + err := db.QueryRow(` SELECT uri, author_did, name, description, icon, created_at, indexed_at FROM collections - WHERE uri = ? - `), uri).Scan(&c.URI, &c.AuthorDID, &c.Name, &c.Description, &c.Icon, &c.CreatedAt, &c.IndexedAt) + WHERE uri = $1 + `, uri).Scan(&c.URI, &c.AuthorDID, &c.Name, &c.Description, &c.Icon, &c.CreatedAt, &c.IndexedAt) if err != nil { return nil, err } @@ -52,30 +52,29 @@ func (db *DB) GetCollectionByURI(uri string) (*Collection, error) { } func (db *DB) DeleteCollection(uri string) error { - - db.Exec(db.Rebind(`DELETE FROM collection_items WHERE collection_uri = ?`), uri) - _, err := db.Exec(db.Rebind(`DELETE FROM collections WHERE uri = ?`), uri) + db.Exec(`DELETE FROM collection_items WHERE collection_uri = $1`, uri) + _, err := db.Exec(`DELETE FROM collections WHERE uri = $1`, uri) return err } func (db *DB) AddToCollection(item *CollectionItem) error { - _, err := db.Exec(db.Rebind(` + _, err := db.Exec(` INSERT INTO collection_items (uri, author_did, collection_uri, annotation_uri, position, created_at, indexed_at) - VALUES (?, ?, ?, ?, ?, ?, ?) + VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT(uri) DO UPDATE SET - position = excluded.position, - indexed_at = excluded.indexed_at - `), item.URI, item.AuthorDID, item.CollectionURI, item.AnnotationURI, item.Position, item.CreatedAt, item.IndexedAt) + position = EXCLUDED.position, + indexed_at = EXCLUDED.indexed_at + `, item.URI, item.AuthorDID, item.CollectionURI, item.AnnotationURI, item.Position, item.CreatedAt, item.IndexedAt) return err } func (db *DB) GetCollectionItems(collectionURI string) ([]CollectionItem, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, collection_uri, annotation_uri, position, created_at, indexed_at FROM collection_items - WHERE collection_uri = ? + WHERE collection_uri = $1 ORDER BY position ASC, created_at DESC - `), collectionURI) + `, collectionURI) if err != nil { return nil, err } @@ -93,127 +92,91 @@ func (db *DB) GetCollectionItems(collectionURI string) ([]CollectionItem, error) } func (db *DB) RemoveFromCollection(uri string) error { - _, err := db.Exec(db.Rebind(`DELETE FROM collection_items WHERE uri = ?`), uri) + _, err := db.Exec(`DELETE FROM collection_items WHERE uri = $1`, uri) return err } func (db *DB) GetRecentCollectionItems(limit, offset int) ([]CollectionItem, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, collection_uri, annotation_uri, position, created_at, indexed_at FROM collection_items ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), limit, offset) + LIMIT $1 OFFSET $2 + `, limit, offset) if err != nil { return nil, err } defer rows.Close() - var items []CollectionItem - for rows.Next() { - var item CollectionItem - if err := rows.Scan(&item.URI, &item.AuthorDID, &item.CollectionURI, &item.AnnotationURI, &item.Position, &item.CreatedAt, &item.IndexedAt); err != nil { - return nil, err - } - items = append(items, item) - } - return items, nil + return scanCollectionItems(rows) } func (db *DB) GetPopularCollectionItems(limit, offset int) ([]CollectionItem, error) { since := time.Now().AddDate(0, 0, -14) - rows, err := db.Query(db.Rebind(` - SELECT - c.uri, c.author_did, c.collection_uri, c.annotation_uri, + rows, err := db.Query(` + SELECT + c.uri, c.author_did, c.collection_uri, c.annotation_uri, c.position, c.created_at, c.indexed_at FROM collection_items c - LEFT JOIN ( - SELECT subject_uri, COUNT(*) as cnt FROM likes GROUP BY subject_uri - ) l ON l.subject_uri = c.annotation_uri - LEFT JOIN ( - SELECT root_uri, COUNT(*) as cnt FROM replies GROUP BY root_uri - ) r ON r.root_uri = c.annotation_uri - WHERE c.created_at > ? AND (COALESCE(l.cnt, 0) + COALESCE(r.cnt, 0)) > 0 - ORDER BY (COALESCE(l.cnt, 0) + COALESCE(r.cnt, 0)) DESC, c.created_at DESC - LIMIT ? OFFSET ? - `), since, limit, offset) + LEFT JOIN LATERAL ( + SELECT COUNT(*) as cnt FROM likes WHERE subject_uri = c.annotation_uri + ) l ON true + LEFT JOIN LATERAL ( + SELECT COUNT(*) as cnt FROM replies WHERE root_uri = c.annotation_uri + ) r ON true + WHERE c.created_at > $1 AND (l.cnt + r.cnt) > 0 + ORDER BY (l.cnt + r.cnt) DESC, c.created_at DESC + LIMIT $2 OFFSET $3 + `, since, limit, offset) if err != nil { return nil, err } defer rows.Close() - var items []CollectionItem - for rows.Next() { - var item CollectionItem - if err := rows.Scan(&item.URI, &item.AuthorDID, &item.CollectionURI, &item.AnnotationURI, &item.Position, &item.CreatedAt, &item.IndexedAt); err != nil { - return nil, err - } - items = append(items, item) - } - return items, nil + return scanCollectionItems(rows) } func (db *DB) GetShelvedCollectionItems(limit, offset int) ([]CollectionItem, error) { olderThan := time.Now().AddDate(0, 0, -1) since := time.Now().AddDate(0, 0, -14) - rows, err := db.Query(db.Rebind(` - SELECT - c.uri, c.author_did, c.collection_uri, c.annotation_uri, + rows, err := db.Query(` + SELECT + c.uri, c.author_did, c.collection_uri, c.annotation_uri, c.position, c.created_at, c.indexed_at FROM collection_items c - LEFT JOIN ( - SELECT subject_uri, COUNT(*) as cnt FROM likes GROUP BY subject_uri - ) l ON l.subject_uri = c.annotation_uri - LEFT JOIN ( - SELECT root_uri, COUNT(*) as cnt FROM replies GROUP BY root_uri - ) r ON r.root_uri = c.annotation_uri - WHERE c.created_at < ? AND c.created_at > ? AND (COALESCE(l.cnt, 0) + COALESCE(r.cnt, 0)) = 0 + WHERE c.created_at < $1 AND c.created_at > $2 + AND NOT EXISTS (SELECT 1 FROM likes WHERE subject_uri = c.annotation_uri) + AND NOT EXISTS (SELECT 1 FROM replies WHERE root_uri = c.annotation_uri) ORDER BY RANDOM() - LIMIT ? OFFSET ? - `), olderThan, since, limit, offset) + LIMIT $3 OFFSET $4 + `, olderThan, since, limit, offset) if err != nil { return nil, err } defer rows.Close() - var items []CollectionItem - for rows.Next() { - var item CollectionItem - if err := rows.Scan(&item.URI, &item.AuthorDID, &item.CollectionURI, &item.AnnotationURI, &item.Position, &item.CreatedAt, &item.IndexedAt); err != nil { - return nil, err - } - items = append(items, item) - } - return items, nil + return scanCollectionItems(rows) } func (db *DB) GetCollectionItemsByAuthor(authorDID string) ([]CollectionItem, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, collection_uri, annotation_uri, position, created_at, indexed_at FROM collection_items - WHERE author_did = ? + WHERE author_did = $1 ORDER BY created_at DESC - `), authorDID) + `, authorDID) if err != nil { return nil, err } defer rows.Close() - var items []CollectionItem - for rows.Next() { - var item CollectionItem - if err := rows.Scan(&item.URI, &item.AuthorDID, &item.CollectionURI, &item.AnnotationURI, &item.Position, &item.CreatedAt, &item.IndexedAt); err != nil { - return nil, err - } - items = append(items, item) - } - return items, nil + return scanCollectionItems(rows) } func (db *DB) GetCollectionURIsForAnnotation(annotationURI string) ([]string, error) { - rows, err := db.Query(db.Rebind(` - SELECT collection_uri FROM collection_items WHERE annotation_uri = ? - `), annotationURI) + rows, err := db.Query(` + SELECT collection_uri FROM collection_items WHERE annotation_uri = $1 + `, annotationURI) if err != nil { return nil, err } @@ -235,19 +198,12 @@ func (db *DB) GetCollectionItemCounts(uris []string) (map[string]int, error) { return map[string]int{}, nil } - query := db.Rebind(` + rows, err := db.Query(` SELECT collection_uri, COUNT(*) FROM collection_items - WHERE collection_uri IN (` + buildPlaceholders(len(uris)) + `) + WHERE collection_uri = ANY($1) GROUP BY collection_uri - `) - - args := make([]interface{}, len(uris)) - for i, uri := range uris { - args[i] = uri - } - - rows, err := db.Query(query, args...) + `, pqStringArray(uris)) if err != nil { return nil, err } @@ -270,18 +226,11 @@ func (db *DB) GetCollectionsByURIs(uris []string) ([]Collection, error) { return []Collection{}, nil } - query := db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, name, description, icon, created_at, indexed_at FROM collections - WHERE uri IN (` + buildPlaceholders(len(uris)) + `) - `) - - args := make([]interface{}, len(uris)) - for i, uri := range uris { - args[i] = uri - } - - rows, err := db.Query(query, args...) + WHERE uri = ANY($1) + `, pqStringArray(uris)) if err != nil { return nil, err } @@ -297,3 +246,18 @@ func (db *DB) GetCollectionsByURIs(uris []string) ([]Collection, error) { } return collections, nil } + +func scanCollectionItems(rows interface { + Next() bool + Scan(...interface{}) error +}) ([]CollectionItem, error) { + var items []CollectionItem + for rows.Next() { + var item CollectionItem + if err := rows.Scan(&item.URI, &item.AuthorDID, &item.CollectionURI, &item.AnnotationURI, &item.Position, &item.CreatedAt, &item.IndexedAt); err != nil { + return nil, err + } + items = append(items, item) + } + return items, nil +} diff --git a/backend/internal/db/queries_highlights.go b/backend/internal/db/queries_highlights.go index 6cdcf8a..bdd755a 100644 --- a/backend/internal/db/queries_highlights.go +++ b/backend/internal/db/queries_highlights.go @@ -5,29 +5,29 @@ import ( ) func (db *DB) CreateHighlight(h *Highlight) error { - _, err := db.Exec(db.Rebind(` + _, err := db.Exec(` INSERT INTO highlights (uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) ON CONFLICT(uri) DO UPDATE SET - target_source = excluded.target_source, - target_hash = excluded.target_hash, - target_title = excluded.target_title, - selector_json = excluded.selector_json, - color = excluded.color, - tags_json = excluded.tags_json, - indexed_at = excluded.indexed_at, - cid = excluded.cid - `), h.URI, h.AuthorDID, h.TargetSource, h.TargetHash, h.TargetTitle, h.SelectorJSON, h.Color, h.TagsJSON, h.CreatedAt, h.IndexedAt, h.CID) + target_source = EXCLUDED.target_source, + target_hash = EXCLUDED.target_hash, + target_title = EXCLUDED.target_title, + selector_json = EXCLUDED.selector_json, + color = EXCLUDED.color, + tags_json = EXCLUDED.tags_json, + indexed_at = EXCLUDED.indexed_at, + cid = EXCLUDED.cid + `, h.URI, h.AuthorDID, h.TargetSource, h.TargetHash, h.TargetTitle, h.SelectorJSON, h.Color, h.TagsJSON, h.CreatedAt, h.IndexedAt, h.CID) return err } func (db *DB) GetHighlightByURI(uri string) (*Highlight, error) { var h Highlight - err := db.QueryRow(db.Rebind(` + err := db.QueryRow(` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights - WHERE uri = ? - `), uri).Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID) + WHERE uri = $1 + `, uri).Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID) if err != nil { return nil, err } @@ -35,424 +35,286 @@ func (db *DB) GetHighlightByURI(uri string) (*Highlight, error) { } func (db *DB) GetRecentHighlights(limit, offset int) ([]Highlight, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), limit, offset) + LIMIT $1 OFFSET $2 + `, limit, offset) if err != nil { return nil, err } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) GetPopularHighlights(limit, offset int) ([]Highlight, error) { since := time.Now().AddDate(0, 0, -14) - rows, err := db.Query(db.Rebind(` - SELECT - h.uri, h.author_did, h.target_source, h.target_hash, h.target_title, + rows, err := db.Query(` + SELECT + h.uri, h.author_did, h.target_source, h.target_hash, h.target_title, h.selector_json, h.color, h.tags_json, h.created_at, h.indexed_at, h.cid FROM highlights h - LEFT JOIN ( - SELECT subject_uri, COUNT(*) as cnt FROM likes GROUP BY subject_uri - ) l ON l.subject_uri = h.uri - LEFT JOIN ( - SELECT root_uri, COUNT(*) as cnt FROM replies GROUP BY root_uri - ) r ON r.root_uri = h.uri - WHERE h.created_at > ? AND (COALESCE(l.cnt, 0) + COALESCE(r.cnt, 0)) > 0 - ORDER BY (COALESCE(l.cnt, 0) + COALESCE(r.cnt, 0)) DESC, h.created_at DESC - LIMIT ? OFFSET ? - `), since, limit, offset) + LEFT JOIN LATERAL ( + SELECT COUNT(*) as cnt FROM likes WHERE subject_uri = h.uri + ) l ON true + LEFT JOIN LATERAL ( + SELECT COUNT(*) as cnt FROM replies WHERE root_uri = h.uri + ) r ON true + WHERE h.created_at > $1 AND (l.cnt + r.cnt) > 0 + ORDER BY (l.cnt + r.cnt) DESC, h.created_at DESC + LIMIT $2 OFFSET $3 + `, since, limit, offset) if err != nil { return nil, err } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) GetShelvedHighlights(limit, offset int) ([]Highlight, error) { olderThan := time.Now().AddDate(0, 0, -1) since := time.Now().AddDate(0, 0, -14) - rows, err := db.Query(db.Rebind(` - SELECT - h.uri, h.author_did, h.target_source, h.target_hash, h.target_title, + rows, err := db.Query(` + SELECT + h.uri, h.author_did, h.target_source, h.target_hash, h.target_title, h.selector_json, h.color, h.tags_json, h.created_at, h.indexed_at, h.cid FROM highlights h - LEFT JOIN ( - SELECT subject_uri, COUNT(*) as cnt FROM likes GROUP BY subject_uri - ) l ON l.subject_uri = h.uri - LEFT JOIN ( - SELECT root_uri, COUNT(*) as cnt FROM replies GROUP BY root_uri - ) r ON r.root_uri = h.uri - WHERE h.created_at < ? AND h.created_at > ? AND (COALESCE(l.cnt, 0) + COALESCE(r.cnt, 0)) = 0 + WHERE h.created_at < $1 AND h.created_at > $2 + AND NOT EXISTS (SELECT 1 FROM likes WHERE subject_uri = h.uri) + AND NOT EXISTS (SELECT 1 FROM replies WHERE root_uri = h.uri) ORDER BY RANDOM() - LIMIT ? OFFSET ? - `), olderThan, since, limit, offset) + LIMIT $3 OFFSET $4 + `, olderThan, since, limit, offset) if err != nil { return nil, err } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) GetMarginHighlights(limit, offset int) ([]Highlight, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights WHERE uri NOT LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), limit, offset) + LIMIT $1 OFFSET $2 + `, limit, offset) if err != nil { return nil, err } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) GetSembleHighlights(limit, offset int) ([]Highlight, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights WHERE uri LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), limit, offset) + LIMIT $1 OFFSET $2 + `, limit, offset) if err != nil { return nil, err } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) GetHighlightsByTag(tag string, limit, offset int) ([]Highlight, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights - WHERE tags_json LIKE ? + WHERE tags_json::jsonb ? $1 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), pattern, limit, offset) + LIMIT $2 OFFSET $3 + `, tag, limit, offset) if err != nil { return nil, err } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) GetMarginHighlightsByTag(tag string, limit, offset int) ([]Highlight, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights - WHERE tags_json LIKE ? AND uri NOT LIKE '%network.cosmik%' + WHERE tags_json::jsonb ? $1 AND uri NOT LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), pattern, limit, offset) + LIMIT $2 OFFSET $3 + `, tag, limit, offset) if err != nil { return nil, err } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) GetSembleHighlightsByTag(tag string, limit, offset int) ([]Highlight, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights - WHERE tags_json LIKE ? AND uri LIKE '%network.cosmik%' + WHERE tags_json::jsonb ? $1 AND uri LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), pattern, limit, offset) + LIMIT $2 OFFSET $3 + `, tag, limit, offset) if err != nil { return nil, err } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) GetHighlightsByTagAndAuthor(tag, authorDID string, limit, offset int) ([]Highlight, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights - WHERE author_did = ? AND tags_json LIKE ? + WHERE author_did = $1 AND tags_json::jsonb ? $2 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, pattern, limit, offset) + LIMIT $3 OFFSET $4 + `, authorDID, tag, limit, offset) if err != nil { return nil, err } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) GetMarginHighlightsByTagAndAuthor(tag, authorDID string, limit, offset int) ([]Highlight, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights - WHERE author_did = ? AND tags_json LIKE ? AND uri NOT LIKE '%network.cosmik%' + WHERE author_did = $1 AND tags_json::jsonb ? $2 AND uri NOT LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, pattern, limit, offset) + LIMIT $3 OFFSET $4 + `, authorDID, tag, limit, offset) if err != nil { return nil, err } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) GetSembleHighlightsByTagAndAuthor(tag, authorDID string, limit, offset int) ([]Highlight, error) { - pattern := "%\"" + tag + "\"%" - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights - WHERE author_did = ? AND tags_json LIKE ? AND uri LIKE '%network.cosmik%' + WHERE author_did = $1 AND tags_json::jsonb ? $2 AND uri LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, pattern, limit, offset) + LIMIT $3 OFFSET $4 + `, authorDID, tag, limit, offset) if err != nil { return nil, err } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) GetHighlightsByTargetHash(targetHash string, limit, offset int) ([]Highlight, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights - WHERE target_hash = ? + WHERE target_hash = $1 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), targetHash, limit, offset) + LIMIT $2 OFFSET $3 + `, targetHash, limit, offset) if err != nil { return nil, err } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) GetHighlightsByAuthor(authorDID string, limit, offset int) ([]Highlight, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights - WHERE author_did = ? + WHERE author_did = $1 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, limit, offset) + LIMIT $2 OFFSET $3 + `, authorDID, limit, offset) if err != nil { return nil, err } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) GetMarginHighlightsByAuthor(authorDID string, limit, offset int) ([]Highlight, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights - WHERE author_did = ? AND uri NOT LIKE '%network.cosmik%' + WHERE author_did = $1 AND uri NOT LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, limit, offset) + LIMIT $2 OFFSET $3 + `, authorDID, limit, offset) if err != nil { return nil, err } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) GetSembleHighlightsByAuthor(authorDID string, limit, offset int) ([]Highlight, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights - WHERE author_did = ? AND uri LIKE '%network.cosmik%' + WHERE author_did = $1 AND uri LIKE '%network.cosmik%' ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, limit, offset) + LIMIT $2 OFFSET $3 + `, authorDID, limit, offset) if err != nil { return nil, err } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) GetHighlightsByAuthorAndTargetHash(authorDID, targetHash string, limit, offset int) ([]Highlight, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights - WHERE author_did = ? AND target_hash = ? + WHERE author_did = $1 AND target_hash = $2 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), authorDID, targetHash, limit, offset) + LIMIT $3 OFFSET $4 + `, authorDID, targetHash, limit, offset) if err != nil { return nil, err } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) DeleteHighlight(uri string) error { - _, err := db.Exec(db.Rebind(`DELETE FROM highlights WHERE uri = ?`), uri) + _, err := db.Exec(`DELETE FROM highlights WHERE uri = $1`, uri) return err } func (db *DB) UpdateHighlight(uri, color, tagsJSON, cid string) error { - _, err := db.Exec(db.Rebind(` - UPDATE highlights - SET color = ?, tags_json = ?, cid = ?, indexed_at = ? - WHERE uri = ? - `), color, tagsJSON, cid, time.Now(), uri) + _, err := db.Exec(` + UPDATE highlights + SET color = $1, tags_json = $2, cid = $3, indexed_at = $4 + WHERE uri = $5 + `, color, tagsJSON, cid, time.Now(), uri) return err } @@ -461,38 +323,23 @@ func (db *DB) GetHighlightsByURIs(uris []string) ([]Highlight, error) { return []Highlight{}, nil } - query := db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights - WHERE uri IN (` + buildPlaceholders(len(uris)) + `) - `) - - args := make([]interface{}, len(uris)) - for i, uri := range uris { - args[i] = uri - } - - rows, err := db.Query(query, args...) + WHERE uri = ANY($1) + `, pqStringArray(uris)) if err != nil { return nil, err } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) GetHighlightURIs(authorDID string) ([]string, error) { - rows, err := db.Query(db.Rebind(` - SELECT uri FROM highlights WHERE author_did = ? - `), authorDID) + rows, err := db.Query(` + SELECT uri FROM highlights WHERE author_did = $1 + `, authorDID) if err != nil { return nil, err } @@ -508,3 +355,18 @@ func (db *DB) GetHighlightURIs(authorDID string) ([]string, error) { } return uris, nil } + +func scanHighlights(rows interface { + Next() bool + Scan(...interface{}) error +}) ([]Highlight, error) { + var highlights []Highlight + for rows.Next() { + var h Highlight + if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { + return nil, err + } + highlights = append(highlights, h) + } + return highlights, nil +} diff --git a/backend/internal/db/queries_history.go b/backend/internal/db/queries_history.go index 254a0d2..55eba4e 100644 --- a/backend/internal/db/queries_history.go +++ b/backend/internal/db/queries_history.go @@ -7,20 +7,20 @@ import ( ) func (db *DB) SaveEditHistory(uri, recordType, previousContent string, previousCID *string) error { - _, err := db.Exec(db.Rebind(` + _, err := db.Exec(` INSERT INTO edit_history (uri, record_type, previous_content, previous_cid, edited_at) - VALUES (?, ?, ?, ?, ?) - `), uri, recordType, previousContent, previousCID, time.Now()) + VALUES ($1, $2, $3, $4, $5) + `, uri, recordType, previousContent, previousCID, time.Now()) return err } func (db *DB) GetEditHistory(uri string) ([]EditHistory, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT id, uri, record_type, previous_content, previous_cid, edited_at FROM edit_history - WHERE uri = ? + WHERE uri = $1 ORDER BY edited_at DESC - `), uri) + `, uri) if err != nil { return nil, err } @@ -29,28 +29,9 @@ func (db *DB) GetEditHistory(uri string) ([]EditHistory, error) { var history []EditHistory for rows.Next() { var h EditHistory - var editedAt interface{} - if err := rows.Scan(&h.ID, &h.URI, &h.RecordType, &h.PreviousContent, &h.PreviousCID, &editedAt); err != nil { + if err := rows.Scan(&h.ID, &h.URI, &h.RecordType, &h.PreviousContent, &h.PreviousCID, &h.EditedAt); err != nil { return nil, err } - - switch v := editedAt.(type) { - case time.Time: - h.EditedAt = v - case []byte: - parsed, err := parseTime(string(v)) - if err != nil { - return nil, err - } - h.EditedAt = parsed - case string: - parsed, err := parseTime(v) - if err != nil { - return nil, err - } - h.EditedAt = parsed - } - history = append(history, h) } return history, nil @@ -61,33 +42,21 @@ func (db *DB) GetLatestEditTimes(uris []string) (map[string]time.Time, error) { return nil, nil } - query := ` - SELECT uri, MAX(edited_at) as edited_at - FROM edit_history - WHERE uri IN (` - args := make([]interface{}, len(uris)) placeholders := make([]string, len(uris)) - + args := make([]interface{}, len(uris)) for i, uri := range uris { placeholders[i] = fmt.Sprintf("$%d", i+1) args[i] = uri } - query += strings.Join(placeholders, ",") + ") GROUP BY uri" - - if db.driver == "sqlite3" { - query = strings.ReplaceAll(query, "$", "?") - placeholders = make([]string, len(uris)) - for i := range uris { - placeholders[i] = "?" - } - query = ` + query := ` SELECT uri, MAX(edited_at) as edited_at FROM edit_history - WHERE uri IN (` + strings.Join(placeholders, ",") + ") GROUP BY uri" - } + WHERE uri IN (` + strings.Join(placeholders, ",") + `) + GROUP BY uri + ` - rows, err := db.Query(db.Rebind(query), args...) + rows, err := db.Query(query, args...) if err != nil { return nil, err } @@ -96,49 +65,12 @@ func (db *DB) GetLatestEditTimes(uris []string) (map[string]time.Time, error) { result := make(map[string]time.Time) for rows.Next() { var uri string - var editedAt interface{} + var editedAt time.Time if err := rows.Scan(&uri, &editedAt); err != nil { continue } - - var finalTime time.Time - switch v := editedAt.(type) { - case time.Time: - finalTime = v - case []byte: - parsed, err := parseTime(string(v)) - if err != nil { - continue - } - finalTime = parsed - case string: - parsed, err := parseTime(v) - if err != nil { - continue - } - finalTime = parsed - default: - continue - } - - result[uri] = finalTime + result[uri] = editedAt } return result, nil } - -func parseTime(s string) (time.Time, error) { - formats := []string{ - time.RFC3339, - time.RFC3339Nano, - "2006-01-02 15:04:05.999999999-07:00", - "2006-01-02 15:04:05", - } - - for _, f := range formats { - if t, err := time.Parse(f, s); err == nil { - return t, nil - } - } - return time.Time{}, fmt.Errorf("could not parse time: %s", s) -} diff --git a/backend/internal/db/queries_keys.go b/backend/internal/db/queries_keys.go index c0c8990..325ce1a 100644 --- a/backend/internal/db/queries_keys.go +++ b/backend/internal/db/queries_keys.go @@ -5,25 +5,25 @@ import ( ) func (db *DB) CreateAPIKey(key *APIKey) error { - _, err := db.Exec(db.Rebind(` + _, err := db.Exec(` INSERT INTO api_keys (id, owner_did, name, key_hash, created_at, uri, cid) - VALUES (?, ?, ?, ?, ?, ?, ?) + VALUES ($1, $2, $3, $4, $5, $6, $7) ON CONFLICT (id) DO UPDATE SET name = EXCLUDED.name, key_hash = EXCLUDED.key_hash, uri = EXCLUDED.uri, cid = EXCLUDED.cid - `), key.ID, key.OwnerDID, key.Name, key.KeyHash, key.CreatedAt, key.URI, key.CID) + `, key.ID, key.OwnerDID, key.Name, key.KeyHash, key.CreatedAt, key.URI, key.CID) return err } func (db *DB) GetAPIKeysByOwner(ownerDID string) ([]APIKey, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT id, owner_did, name, key_hash, created_at, last_used_at FROM api_keys - WHERE owner_did = ? + WHERE owner_did = $1 ORDER BY created_at DESC - `), ownerDID) + `, ownerDID) if err != nil { return nil, err } @@ -42,11 +42,11 @@ func (db *DB) GetAPIKeysByOwner(ownerDID string) ([]APIKey, error) { func (db *DB) GetAPIKeyByHash(keyHash string) (*APIKey, error) { var k APIKey - err := db.QueryRow(db.Rebind(` + err := db.QueryRow(` SELECT id, owner_did, name, key_hash, created_at, last_used_at FROM api_keys - WHERE key_hash = ? - `), keyHash).Scan(&k.ID, &k.OwnerDID, &k.Name, &k.KeyHash, &k.CreatedAt, &k.LastUsedAt) + WHERE key_hash = $1 + `, keyHash).Scan(&k.ID, &k.OwnerDID, &k.Name, &k.KeyHash, &k.CreatedAt, &k.LastUsedAt) if err != nil { return nil, err } @@ -54,6 +54,6 @@ func (db *DB) GetAPIKeyByHash(keyHash string) (*APIKey, error) { } func (db *DB) UpdateAPIKeyLastUsed(id string) error { - _, err := db.Exec(db.Rebind(`UPDATE api_keys SET last_used_at = ? WHERE id = ?`), time.Now(), id) + _, err := db.Exec(`UPDATE api_keys SET last_used_at = $1 WHERE id = $2`, time.Now(), id) return err } diff --git a/backend/internal/db/queries_likes.go b/backend/internal/db/queries_likes.go index d38be4b..33b8b6a 100644 --- a/backend/internal/db/queries_likes.go +++ b/backend/internal/db/queries_likes.go @@ -1,26 +1,28 @@ package db +import "fmt" + func (db *DB) CreateLike(l *Like) error { - _, err := db.Exec(db.Rebind(` + _, err := db.Exec(` INSERT INTO likes (uri, author_did, subject_uri, created_at, indexed_at) - VALUES (?, ?, ?, ?, ?) + VALUES ($1, $2, $3, $4, $5) ON CONFLICT(uri) DO NOTHING - `), l.URI, l.AuthorDID, l.SubjectURI, l.CreatedAt, l.IndexedAt) + `, l.URI, l.AuthorDID, l.SubjectURI, l.CreatedAt, l.IndexedAt) return err } func (db *DB) DeleteLike(uri string) error { - _, err := db.Exec(db.Rebind(`DELETE FROM likes WHERE uri = ?`), uri) + _, err := db.Exec(`DELETE FROM likes WHERE uri = $1`, uri) return err } func (db *DB) GetLikesByAuthor(authorDID string) ([]Like, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, subject_uri, created_at, indexed_at FROM likes - WHERE author_did = ? + WHERE author_did = $1 ORDER BY created_at DESC - `), authorDID) + `, authorDID) if err != nil { return nil, err } @@ -39,17 +41,17 @@ func (db *DB) GetLikesByAuthor(authorDID string) ([]Like, error) { func (db *DB) GetLikeCount(subjectURI string) (int, error) { var count int - err := db.QueryRow(db.Rebind(`SELECT COUNT(*) FROM likes WHERE subject_uri = ?`), subjectURI).Scan(&count) + err := db.QueryRow(`SELECT COUNT(*) FROM likes WHERE subject_uri = $1`, subjectURI).Scan(&count) return count, err } func (db *DB) GetLikeByUserAndSubject(userDID, subjectURI string) (*Like, error) { var like Like - err := db.QueryRow(db.Rebind(` + err := db.QueryRow(` SELECT uri, author_did, subject_uri, created_at, indexed_at FROM likes - WHERE author_did = ? AND subject_uri = ? - `), userDID, subjectURI).Scan(&like.URI, &like.AuthorDID, &like.SubjectURI, &like.CreatedAt, &like.IndexedAt) + WHERE author_did = $1 AND subject_uri = $2 + `, userDID, subjectURI).Scan(&like.URI, &like.AuthorDID, &like.SubjectURI, &like.CreatedAt, &like.IndexedAt) if err != nil { return nil, err } @@ -61,12 +63,12 @@ func (db *DB) GetLikeCounts(subjectURIs []string) (map[string]int, error) { return map[string]int{}, nil } - query := db.Rebind(` - SELECT subject_uri, COUNT(*) - FROM likes - WHERE subject_uri IN (` + buildPlaceholders(len(subjectURIs)) + `) + query := ` + SELECT subject_uri, COUNT(*) + FROM likes + WHERE subject_uri IN (` + buildPlaceholders(len(subjectURIs), 1) + `) GROUP BY subject_uri - `) + ` args := make([]interface{}, len(subjectURIs)) for i, uri := range subjectURIs { @@ -97,11 +99,11 @@ func (db *DB) GetViewerLikes(viewerDID string, subjectURIs []string) (map[string return map[string]bool{}, nil } - query := db.Rebind(` - SELECT subject_uri - FROM likes - WHERE author_did = ? AND subject_uri IN (` + buildPlaceholders(len(subjectURIs)) + `) - `) + query := fmt.Sprintf(` + SELECT subject_uri + FROM likes + WHERE author_did = $1 AND subject_uri IN (%s) + `, buildPlaceholders(len(subjectURIs), 2)) args := make([]interface{}, len(subjectURIs)+1) args[0] = viewerDID diff --git a/backend/internal/db/queries_moderation.go b/backend/internal/db/queries_moderation.go index ebb911c..b058394 100644 --- a/backend/internal/db/queries_moderation.go +++ b/backend/internal/db/queries_moderation.go @@ -1,21 +1,26 @@ package db -import "time" +import ( + "fmt" + "strings" + "time" +) func (db *DB) CreateBlock(actorDID, subjectDID string) error { - query := `INSERT INTO blocks (actor_did, subject_did, created_at) VALUES (?, ?, ?) - ON CONFLICT(actor_did, subject_did) DO NOTHING` - _, err := db.Exec(db.Rebind(query), actorDID, subjectDID, time.Now()) + _, err := db.Exec(` + INSERT INTO blocks (actor_did, subject_did, created_at) VALUES ($1, $2, $3) + ON CONFLICT(actor_did, subject_did) DO NOTHING + `, actorDID, subjectDID, time.Now()) return err } func (db *DB) DeleteBlock(actorDID, subjectDID string) error { - _, err := db.Exec(db.Rebind(`DELETE FROM blocks WHERE actor_did = ? AND subject_did = ?`), actorDID, subjectDID) + _, err := db.Exec(`DELETE FROM blocks WHERE actor_did = $1 AND subject_did = $2`, actorDID, subjectDID) return err } func (db *DB) GetBlocks(actorDID string) ([]Block, error) { - rows, err := db.Query(db.Rebind(`SELECT id, actor_did, subject_did, created_at FROM blocks WHERE actor_did = ? ORDER BY created_at DESC`), actorDID) + rows, err := db.Query(`SELECT id, actor_did, subject_did, created_at FROM blocks WHERE actor_did = $1 ORDER BY created_at DESC`, actorDID) if err != nil { return nil, err } @@ -33,19 +38,19 @@ func (db *DB) GetBlocks(actorDID string) ([]Block, error) { } func (db *DB) IsBlocked(actorDID, subjectDID string) (bool, error) { - var count int - err := db.QueryRow(db.Rebind(`SELECT COUNT(*) FROM blocks WHERE actor_did = ? AND subject_did = ?`), actorDID, subjectDID).Scan(&count) - return count > 0, err + var exists bool + err := db.QueryRow(`SELECT EXISTS(SELECT 1 FROM blocks WHERE actor_did = $1 AND subject_did = $2)`, actorDID, subjectDID).Scan(&exists) + return exists, err } func (db *DB) IsBlockedEither(did1, did2 string) (bool, error) { - var count int - err := db.QueryRow(db.Rebind(`SELECT COUNT(*) FROM blocks WHERE (actor_did = ? AND subject_did = ?) OR (actor_did = ? AND subject_did = ?)`), did1, did2, did2, did1).Scan(&count) - return count > 0, err + var exists bool + err := db.QueryRow(`SELECT EXISTS(SELECT 1 FROM blocks WHERE (actor_did = $1 AND subject_did = $2) OR (actor_did = $2 AND subject_did = $1))`, did1, did2).Scan(&exists) + return exists, err } func (db *DB) GetBlockedDIDs(actorDID string) ([]string, error) { - rows, err := db.Query(db.Rebind(`SELECT subject_did FROM blocks WHERE actor_did = ?`), actorDID) + rows, err := db.Query(`SELECT subject_did FROM blocks WHERE actor_did = $1`, actorDID) if err != nil { return nil, err } @@ -63,7 +68,7 @@ func (db *DB) GetBlockedDIDs(actorDID string) ([]string, error) { } func (db *DB) GetBlockedByDIDs(actorDID string) ([]string, error) { - rows, err := db.Query(db.Rebind(`SELECT actor_did FROM blocks WHERE subject_did = ?`), actorDID) + rows, err := db.Query(`SELECT actor_did FROM blocks WHERE subject_did = $1`, actorDID) if err != nil { return nil, err } @@ -81,19 +86,20 @@ func (db *DB) GetBlockedByDIDs(actorDID string) ([]string, error) { } func (db *DB) CreateMute(actorDID, subjectDID string) error { - query := `INSERT INTO mutes (actor_did, subject_did, created_at) VALUES (?, ?, ?) - ON CONFLICT(actor_did, subject_did) DO NOTHING` - _, err := db.Exec(db.Rebind(query), actorDID, subjectDID, time.Now()) + _, err := db.Exec(` + INSERT INTO mutes (actor_did, subject_did, created_at) VALUES ($1, $2, $3) + ON CONFLICT(actor_did, subject_did) DO NOTHING + `, actorDID, subjectDID, time.Now()) return err } func (db *DB) DeleteMute(actorDID, subjectDID string) error { - _, err := db.Exec(db.Rebind(`DELETE FROM mutes WHERE actor_did = ? AND subject_did = ?`), actorDID, subjectDID) + _, err := db.Exec(`DELETE FROM mutes WHERE actor_did = $1 AND subject_did = $2`, actorDID, subjectDID) return err } func (db *DB) GetMutes(actorDID string) ([]Mute, error) { - rows, err := db.Query(db.Rebind(`SELECT id, actor_did, subject_did, created_at FROM mutes WHERE actor_did = ? ORDER BY created_at DESC`), actorDID) + rows, err := db.Query(`SELECT id, actor_did, subject_did, created_at FROM mutes WHERE actor_did = $1 ORDER BY created_at DESC`, actorDID) if err != nil { return nil, err } @@ -111,13 +117,13 @@ func (db *DB) GetMutes(actorDID string) ([]Mute, error) { } func (db *DB) IsMuted(actorDID, subjectDID string) (bool, error) { - var count int - err := db.QueryRow(db.Rebind(`SELECT COUNT(*) FROM mutes WHERE actor_did = ? AND subject_did = ?`), actorDID, subjectDID).Scan(&count) - return count > 0, err + var exists bool + err := db.QueryRow(`SELECT EXISTS(SELECT 1 FROM mutes WHERE actor_did = $1 AND subject_did = $2)`, actorDID, subjectDID).Scan(&exists) + return exists, err } func (db *DB) GetMutedDIDs(actorDID string) ([]string, error) { - rows, err := db.Query(db.Rebind(`SELECT subject_did FROM mutes WHERE actor_did = ?`), actorDID) + rows, err := db.Query(`SELECT subject_did FROM mutes WHERE actor_did = $1`, actorDID) if err != nil { return nil, err } @@ -187,32 +193,31 @@ func (db *DB) GetViewerRelationship(viewerDID, subjectDID string) (blocked bool, } func (db *DB) CreateReport(reporterDID, subjectDID string, subjectURI *string, reasonType string, reasonText *string) (int, error) { - query := `INSERT INTO moderation_reports (reporter_did, subject_did, subject_uri, reason_type, reason_text, status, created_at) - VALUES (?, ?, ?, ?, ?, 'pending', ?)` - - result, err := db.Exec(db.Rebind(query), reporterDID, subjectDID, subjectURI, reasonType, reasonText, time.Now()) - if err != nil { - return 0, err - } - - id, err := result.LastInsertId() - return int(id), err + var id int + err := db.QueryRow(` + INSERT INTO moderation_reports (reporter_did, subject_did, subject_uri, reason_type, reason_text, status, created_at) + VALUES ($1, $2, $3, $4, $5, 'pending', $6) + RETURNING id + `, reporterDID, subjectDID, subjectURI, reasonType, reasonText, time.Now()).Scan(&id) + return id, err } func (db *DB) GetReports(status string, limit, offset int) ([]ModerationReport, error) { query := `SELECT id, reporter_did, subject_did, subject_uri, reason_type, reason_text, status, created_at, resolved_at, resolved_by FROM moderation_reports` args := []interface{}{} + paramIdx := 1 if status != "" { - query += ` WHERE status = ?` + query += ` WHERE status = $1` args = append(args, status) + paramIdx = 2 } - query += ` ORDER BY created_at DESC LIMIT ? OFFSET ?` + query += ` ORDER BY created_at DESC LIMIT $` + itoa(paramIdx) + ` OFFSET $` + itoa(paramIdx+1) args = append(args, limit, offset) - rows, err := db.Query(db.Rebind(query), args...) + rows, err := db.Query(query, args...) if err != nil { return nil, err } @@ -231,7 +236,7 @@ func (db *DB) GetReports(status string, limit, offset int) ([]ModerationReport, func (db *DB) GetReport(id int) (*ModerationReport, error) { var r ModerationReport - err := db.QueryRow(db.Rebind(`SELECT id, reporter_did, subject_did, subject_uri, reason_type, reason_text, status, created_at, resolved_at, resolved_by FROM moderation_reports WHERE id = ?`), id).Scan( + err := db.QueryRow(`SELECT id, reporter_did, subject_did, subject_uri, reason_type, reason_text, status, created_at, resolved_at, resolved_by FROM moderation_reports WHERE id = $1`, id).Scan( &r.ID, &r.ReporterDID, &r.SubjectDID, &r.SubjectURI, &r.ReasonType, &r.ReasonText, &r.Status, &r.CreatedAt, &r.ResolvedAt, &r.ResolvedBy, ) if err != nil { @@ -241,18 +246,17 @@ func (db *DB) GetReport(id int) (*ModerationReport, error) { } func (db *DB) ResolveReport(id int, resolvedBy string, status string) error { - _, err := db.Exec(db.Rebind(`UPDATE moderation_reports SET status = ?, resolved_at = ?, resolved_by = ? WHERE id = ?`), status, time.Now(), resolvedBy, id) + _, err := db.Exec(`UPDATE moderation_reports SET status = $1, resolved_at = $2, resolved_by = $3 WHERE id = $4`, status, time.Now(), resolvedBy, id) return err } func (db *DB) CreateModerationAction(reportID int, actorDID, action string, comment *string) error { - query := `INSERT INTO moderation_actions (report_id, actor_did, action, comment, created_at) VALUES (?, ?, ?, ?, ?)` - _, err := db.Exec(db.Rebind(query), reportID, actorDID, action, comment, time.Now()) + _, err := db.Exec(`INSERT INTO moderation_actions (report_id, actor_did, action, comment, created_at) VALUES ($1, $2, $3, $4, $5)`, reportID, actorDID, action, comment, time.Now()) return err } func (db *DB) GetReportActions(reportID int) ([]ModerationAction, error) { - rows, err := db.Query(db.Rebind(`SELECT id, report_id, actor_did, action, comment, created_at FROM moderation_actions WHERE report_id = ? ORDER BY created_at DESC`), reportID) + rows, err := db.Query(`SELECT id, report_id, actor_did, action, comment, created_at FROM moderation_actions WHERE report_id = $1 ORDER BY created_at DESC`, reportID) if err != nil { return nil, err } @@ -273,22 +277,21 @@ func (db *DB) GetReportCount(status string) (int, error) { query := `SELECT COUNT(*) FROM moderation_reports` args := []interface{}{} if status != "" { - query += ` WHERE status = ?` + query += ` WHERE status = $1` args = append(args, status) } var count int - err := db.QueryRow(db.Rebind(query), args...).Scan(&count) + err := db.QueryRow(query, args...).Scan(&count) return count, err } func (db *DB) CreateContentLabel(src, uri, val, createdBy string) error { - query := `INSERT INTO content_labels (src, uri, val, neg, created_by, created_at) VALUES (?, ?, ?, 0, ?, ?)` - _, err := db.Exec(db.Rebind(query), src, uri, val, createdBy, time.Now()) + _, err := db.Exec(`INSERT INTO content_labels (src, uri, val, neg, created_by, created_at) VALUES ($1, $2, $3, 0, $4, $5)`, src, uri, val, createdBy, time.Now()) return err } func (db *DB) SyncSelfLabels(authorDID, uri string, labels []string) error { - _, err := db.Exec(db.Rebind(`DELETE FROM content_labels WHERE src = ? AND uri = ? AND created_by = ?`), authorDID, uri, authorDID) + _, err := db.Exec(`DELETE FROM content_labels WHERE src = $1 AND uri = $2 AND created_by = $3`, authorDID, uri, authorDID) if err != nil { return err } @@ -301,12 +304,12 @@ func (db *DB) SyncSelfLabels(authorDID, uri string, labels []string) error { } func (db *DB) NegateContentLabel(id int) error { - _, err := db.Exec(db.Rebind(`UPDATE content_labels SET neg = 1 WHERE id = ?`), id) + _, err := db.Exec(`UPDATE content_labels SET neg = 1 WHERE id = $1`, id) return err } func (db *DB) DeleteContentLabel(id int) error { - _, err := db.Exec(db.Rebind(`DELETE FROM content_labels WHERE id = ?`), id) + _, err := db.Exec(`DELETE FROM content_labels WHERE id = $1`, id) return err } @@ -316,28 +319,18 @@ func (db *DB) GetContentLabelsForURIs(uris []string, labelerDIDs []string) (map[ return result, nil } - placeholders := make([]string, len(uris)) - args := make([]interface{}, len(uris)) - for i, uri := range uris { - placeholders[i] = "?" - args[i] = uri - } - query := `SELECT id, src, uri, val, neg, created_by, created_at FROM content_labels - WHERE uri IN (` + joinStrings(placeholders, ",") + `) AND neg = 0` + WHERE uri = ANY($1) AND neg = 0` + args := []interface{}{pqStringArray(uris)} if len(labelerDIDs) > 0 { - srcPlaceholders := make([]string, len(labelerDIDs)) - for i, did := range labelerDIDs { - srcPlaceholders[i] = "?" - args = append(args, did) - } - query += ` AND src IN (` + joinStrings(srcPlaceholders, ",") + `)` + query += ` AND src = ANY($2)` + args = append(args, pqStringArray(labelerDIDs)) } query += ` ORDER BY created_at DESC` - rows, err := db.Query(db.Rebind(query), args...) + rows, err := db.Query(query, args...) if err != nil { return result, err } @@ -359,28 +352,18 @@ func (db *DB) GetContentLabelsForDIDs(dids []string, labelerDIDs []string) (map[ return result, nil } - placeholders := make([]string, len(dids)) - args := make([]interface{}, len(dids)) - for i, did := range dids { - placeholders[i] = "?" - args[i] = did - } - query := `SELECT id, src, uri, val, neg, created_by, created_at FROM content_labels - WHERE uri IN (` + joinStrings(placeholders, ",") + `) AND neg = 0` + WHERE uri = ANY($1) AND neg = 0` + args := []interface{}{pqStringArray(dids)} if len(labelerDIDs) > 0 { - srcPlaceholders := make([]string, len(labelerDIDs)) - for i, did := range labelerDIDs { - srcPlaceholders[i] = "?" - args = append(args, did) - } - query += ` AND src IN (` + joinStrings(srcPlaceholders, ",") + `)` + query += ` AND src = ANY($2)` + args = append(args, pqStringArray(labelerDIDs)) } query += ` ORDER BY created_at DESC` - rows, err := db.Query(db.Rebind(query), args...) + rows, err := db.Query(query, args...) if err != nil { return result, err } @@ -397,7 +380,7 @@ func (db *DB) GetContentLabelsForDIDs(dids []string, labelerDIDs []string) (map[ } func (db *DB) GetAllContentLabels(limit, offset int) ([]ContentLabel, error) { - rows, err := db.Query(db.Rebind(`SELECT id, src, uri, val, neg, created_by, created_at FROM content_labels ORDER BY created_at DESC LIMIT ? OFFSET ?`), limit, offset) + rows, err := db.Query(`SELECT id, src, uri, val, neg, created_by, created_at FROM content_labels ORDER BY created_at DESC LIMIT $1 OFFSET $2`, limit, offset) if err != nil { return nil, err } @@ -414,13 +397,6 @@ func (db *DB) GetAllContentLabels(limit, offset int) ([]ContentLabel, error) { return labels, nil } -func joinStrings(strs []string, sep string) string { - result := "" - for i, s := range strs { - if i > 0 { - result += sep - } - result += s - } - return result +func itoa(i int) string { + return strings.Repeat("", 0) + fmt.Sprintf("%d", i) } diff --git a/backend/internal/db/queries_notifications.go b/backend/internal/db/queries_notifications.go index 1e72f42..c5cc4b9 100644 --- a/backend/internal/db/queries_notifications.go +++ b/backend/internal/db/queries_notifications.go @@ -5,21 +5,21 @@ import ( ) func (db *DB) CreateNotification(n *Notification) error { - _, err := db.Exec(db.Rebind(` + _, err := db.Exec(` INSERT INTO notifications (recipient_did, actor_did, type, subject_uri, created_at) - VALUES (?, ?, ?, ?, ?) - `), n.RecipientDID, n.ActorDID, n.Type, n.SubjectURI, n.CreatedAt) + VALUES ($1, $2, $3, $4, $5) + `, n.RecipientDID, n.ActorDID, n.Type, n.SubjectURI, n.CreatedAt) return err } func (db *DB) GetNotifications(recipientDID string, limit, offset int) ([]Notification, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT id, recipient_did, actor_did, type, subject_uri, created_at, read_at FROM notifications - WHERE recipient_did = ? + WHERE recipient_did = $1 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `), recipientDID, limit, offset) + LIMIT $2 OFFSET $3 + `, recipientDID, limit, offset) if err != nil { return nil, err } @@ -38,15 +38,15 @@ func (db *DB) GetNotifications(recipientDID string, limit, offset int) ([]Notifi func (db *DB) GetUnreadNotificationCount(recipientDID string) (int, error) { var count int - err := db.QueryRow(db.Rebind(` - SELECT COUNT(*) FROM notifications WHERE recipient_did = ? AND read_at IS NULL - `), recipientDID).Scan(&count) + err := db.QueryRow(` + SELECT COUNT(*) FROM notifications WHERE recipient_did = $1 AND read_at IS NULL + `, recipientDID).Scan(&count) return count, err } func (db *DB) MarkNotificationsRead(recipientDID string) error { - _, err := db.Exec(db.Rebind(` - UPDATE notifications SET read_at = ? WHERE recipient_did = ? AND read_at IS NULL - `), time.Now(), recipientDID) + _, err := db.Exec(` + UPDATE notifications SET read_at = $1 WHERE recipient_did = $2 AND read_at IS NULL + `, time.Now(), recipientDID) return err } diff --git a/backend/internal/db/queries_recommendations.go b/backend/internal/db/queries_recommendations.go index 32b7a69..b5b4829 100644 --- a/backend/internal/db/queries_recommendations.go +++ b/backend/internal/db/queries_recommendations.go @@ -55,16 +55,11 @@ type UserProfile struct { } func (db *DB) MigrateRecommendations() error { - dateType := "TIMESTAMP" - if db.driver == "sqlite3" { - dateType = "DATETIME" - } - _, err := db.Exec(` CREATE TABLE IF NOT EXISTS document_embeddings ( document_uri TEXT PRIMARY KEY, embedding TEXT NOT NULL, - updated_at ` + dateType + ` NOT NULL + updated_at TIMESTAMP NOT NULL )`) if err != nil { return fmt.Errorf("create document_embeddings table: %w", err) @@ -76,7 +71,7 @@ func (db *DB) MigrateRecommendations() error { author_did TEXT NOT NULL, document_uri TEXT, embedding TEXT NOT NULL, - updated_at ` + dateType + ` NOT NULL + updated_at TIMESTAMP NOT NULL )`) if err != nil { return fmt.Errorf("create annotation_embeddings table: %w", err) @@ -90,7 +85,7 @@ func (db *DB) MigrateRecommendations() error { embedding TEXT NOT NULL, tag_affinities TEXT DEFAULT '{}', annotation_count INTEGER NOT NULL DEFAULT 0, - updated_at ` + dateType + ` NOT NULL + updated_at TIMESTAMP NOT NULL )`) if err != nil { return fmt.Errorf("create user_profiles table: %w", err) @@ -154,7 +149,7 @@ func (db *DB) DeleteDocument(uri string) error { func (db *DB) GetDocumentByCanonicalURL(canonicalURL string) (*Document, error) { var d Document err := db.QueryRow( - `SELECT uri, author_did, site, path, title, description, text_content, tags_json, canonical_url, published_at, indexed_at + `SELECT uri, author_did, site, path, title, description, text_content, tags_json, canonical_url, published_at, indexed_at FROM documents WHERE canonical_url = $1`, canonicalURL, ).Scan(&d.URI, &d.AuthorDID, &d.Site, &d.Path, &d.Title, &d.Description, &d.TextContent, &d.TagsJSON, &d.CanonicalURL, &d.PublishedAt, &d.IndexedAt) @@ -167,7 +162,7 @@ func (db *DB) GetDocumentByCanonicalURL(canonicalURL string) (*Document, error) func (db *DB) GetDocumentByURI(uri string) (*Document, error) { var d Document err := db.QueryRow( - `SELECT uri, author_did, site, path, title, description, text_content, tags_json, canonical_url, published_at, indexed_at + `SELECT uri, author_did, site, path, title, description, text_content, tags_json, canonical_url, published_at, indexed_at FROM documents WHERE uri = $1`, uri, ).Scan(&d.URI, &d.AuthorDID, &d.Site, &d.Path, &d.Title, &d.Description, &d.TextContent, &d.TagsJSON, &d.CanonicalURL, &d.PublishedAt, &d.IndexedAt) @@ -178,14 +173,14 @@ func (db *DB) GetDocumentByURI(uri string) (*Document, error) { } func (db *DB) GetDocumentsWithoutEmbeddings(limit int) ([]Document, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT d.uri, d.author_did, d.site, d.path, d.title, d.description, d.text_content, d.tags_json, d.canonical_url, d.published_at, d.indexed_at FROM documents d LEFT JOIN document_embeddings de ON d.uri = de.document_uri WHERE de.document_uri IS NULL ORDER BY d.indexed_at DESC - LIMIT ? - `), limit) + LIMIT $1 + `, limit) if err != nil { return nil, err } @@ -194,14 +189,14 @@ func (db *DB) GetDocumentsWithoutEmbeddings(limit int) ([]Document, error) { } func (db *DB) GetAnnotationsWithoutEmbeddings(limit int) ([]Annotation, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT a.uri, a.author_did, a.motivation, a.body_value, a.body_format, a.body_uri, a.target_source, a.target_hash, a.target_title, a.selector_json, a.tags_json, a.created_at, a.indexed_at, a.cid FROM annotations a LEFT JOIN annotation_embeddings ae ON a.uri = ae.annotation_uri WHERE ae.annotation_uri IS NULL AND a.motivation IN ('commenting', 'highlighting') ORDER BY a.created_at DESC - LIMIT ? - `), limit) + LIMIT $1 + `, limit) if err != nil { return nil, err } @@ -219,14 +214,14 @@ type HighlightForEmbedding struct { } func (db *DB) GetHighlightsWithoutEmbeddings(limit int) ([]HighlightForEmbedding, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT h.uri, h.author_did, h.target_source, h.target_title, h.selector_json, h.tags_json FROM highlights h LEFT JOIN annotation_embeddings ae ON h.uri = ae.annotation_uri WHERE ae.annotation_uri IS NULL ORDER BY h.created_at DESC - LIMIT ? - `), limit) + LIMIT $1 + `, limit) if err != nil { return nil, err } @@ -276,12 +271,12 @@ func scanDocuments(rows interface { } func (db *DB) GetRecentDocuments(limit, offset int) ([]Document, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, site, path, title, description, text_content, tags_json, canonical_url, published_at, indexed_at FROM documents ORDER BY published_at DESC - LIMIT ? OFFSET ? - `), limit, offset) + LIMIT $1 OFFSET $2 + `, limit, offset) if err != nil { return nil, err } @@ -290,14 +285,14 @@ func (db *DB) GetRecentDocuments(limit, offset int) ([]Document, error) { } func (db *DB) GetPopularDocuments(limit, offset int) ([]Document, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT d.uri, d.author_did, d.site, d.path, d.title, d.description, d.text_content, d.tags_json, d.canonical_url, d.published_at, d.indexed_at FROM documents d LEFT JOIN annotations a ON a.target_source = d.canonical_url GROUP BY d.uri ORDER BY COUNT(a.uri) DESC, d.published_at DESC - LIMIT ? OFFSET ? - `), limit, offset) + LIMIT $1 OFFSET $2 + `, limit, offset) if err != nil { return nil, err } @@ -386,7 +381,7 @@ func (db *DB) GetAnnotationEmbeddingsByAuthor(authorDID string) ([]AnnotationEmb func (db *DB) GetRecentAnnotationEmbeddingsByAuthor(authorDID string, limit int) ([]AnnotationEmbedding, error) { rows, err := db.Query( - db.Rebind(`SELECT annotation_uri, author_did, document_uri, embedding, updated_at FROM annotation_embeddings WHERE author_did = ? ORDER BY updated_at DESC LIMIT ?`), + `SELECT annotation_uri, author_did, document_uri, embedding, updated_at FROM annotation_embeddings WHERE author_did = $1 ORDER BY updated_at DESC LIMIT $2`, authorDID, limit, ) if err != nil { @@ -422,11 +417,8 @@ type CandidateDocument struct { } func (db *DB) GetCandidateDocuments(userDID string, limit int) ([]CandidateDocument, error) { - // Note: We use NOT LIKE instead of !~* for cross-database compatibility and performance. - // The engagement count sub-select is also constrained to recent elements if possible, but - // for now we just optimize the regex and exact grouping. - rows, err := db.Query(db.Rebind(` - SELECT + rows, err := db.Query(` + SELECT d.uri, d.author_did, d.site, d.path, d.title, d.description, d.tags_json, d.canonical_url, d.published_at, de.embedding, COALESCE(eng.cnt, 0) AS engagement @@ -439,7 +431,7 @@ func (db *DB) GetCandidateDocuments(userDID string, limit int) ([]CandidateDocum GROUP BY document_uri ) eng ON eng.document_uri = d.uri LEFT JOIN publications p ON d.site = p.uri OR d.site = p.url - WHERE d.author_did != ? + WHERE d.author_did != $1 AND (p.show_in_discover IS NULL OR p.show_in_discover = true) AND LENGTH(d.title) > 15 AND (LENGTH(COALESCE(d.description, '')) >= 30 OR LENGTH(COALESCE(d.text_content, '')) >= 100) @@ -453,11 +445,11 @@ func (db *DB) GetCandidateDocuments(userDID string, limit int) ([]CandidateDocum AND LOWER(d.title) NOT LIKE '%placeholder%' AND d.uri NOT IN ( SELECT DISTINCT document_uri FROM annotation_embeddings - WHERE author_did = ? AND document_uri IS NOT NULL + WHERE author_did = $2 AND document_uri IS NOT NULL ) ORDER BY d.published_at DESC - LIMIT ? - `), userDID, userDID, limit) + LIMIT $3 + `, userDID, userDID, limit) if err != nil { return nil, fmt.Errorf("candidate query: %w", err) } diff --git a/backend/internal/db/queries_replies.go b/backend/internal/db/queries_replies.go index d528bce..7f154f9 100644 --- a/backend/internal/db/queries_replies.go +++ b/backend/internal/db/queries_replies.go @@ -1,48 +1,40 @@ package db func (db *DB) CreateReply(r *Reply) error { - _, err := db.Exec(db.Rebind(` + _, err := db.Exec(` INSERT INTO replies (uri, author_did, parent_uri, root_uri, text, format, created_at, indexed_at, cid) - VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) ON CONFLICT(uri) DO UPDATE SET - text = excluded.text, - format = excluded.format, - indexed_at = excluded.indexed_at, - cid = excluded.cid - `), r.URI, r.AuthorDID, r.ParentURI, r.RootURI, r.Text, r.Format, r.CreatedAt, r.IndexedAt, r.CID) + text = EXCLUDED.text, + format = EXCLUDED.format, + indexed_at = EXCLUDED.indexed_at, + cid = EXCLUDED.cid + `, r.URI, r.AuthorDID, r.ParentURI, r.RootURI, r.Text, r.Format, r.CreatedAt, r.IndexedAt, r.CID) return err } func (db *DB) GetRepliesByRoot(rootURI string) ([]Reply, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, parent_uri, root_uri, text, format, created_at, indexed_at, cid FROM replies - WHERE root_uri = ? + WHERE root_uri = $1 ORDER BY created_at ASC - `), rootURI) + `, rootURI) if err != nil { return nil, err } defer rows.Close() - var replies []Reply - for rows.Next() { - var r Reply - if err := rows.Scan(&r.URI, &r.AuthorDID, &r.ParentURI, &r.RootURI, &r.Text, &r.Format, &r.CreatedAt, &r.IndexedAt, &r.CID); err != nil { - return nil, err - } - replies = append(replies, r) - } - return replies, nil + return scanReplies(rows) } func (db *DB) GetReplyByURI(uri string) (*Reply, error) { var r Reply - err := db.QueryRow(db.Rebind(` + err := db.QueryRow(` SELECT uri, author_did, parent_uri, root_uri, text, format, created_at, indexed_at, cid FROM replies - WHERE uri = ? - `), uri).Scan(&r.URI, &r.AuthorDID, &r.ParentURI, &r.RootURI, &r.Text, &r.Format, &r.CreatedAt, &r.IndexedAt, &r.CID) + WHERE uri = $1 + `, uri).Scan(&r.URI, &r.AuthorDID, &r.ParentURI, &r.RootURI, &r.Text, &r.Format, &r.CreatedAt, &r.IndexedAt, &r.CID) if err != nil { return nil, err } @@ -50,59 +42,43 @@ func (db *DB) GetReplyByURI(uri string) (*Reply, error) { } func (db *DB) DeleteReply(uri string) error { - _, err := db.Exec(db.Rebind(`DELETE FROM replies WHERE uri = ?`), uri) + _, err := db.Exec(`DELETE FROM replies WHERE uri = $1`, uri) return err } func (db *DB) GetRepliesByAuthor(authorDID string) ([]Reply, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, parent_uri, root_uri, text, format, created_at, indexed_at, cid FROM replies - WHERE author_did = ? + WHERE author_did = $1 ORDER BY created_at DESC - `), authorDID) + `, authorDID) if err != nil { return nil, err } defer rows.Close() - var replies []Reply - for rows.Next() { - var r Reply - if err := rows.Scan(&r.URI, &r.AuthorDID, &r.ParentURI, &r.RootURI, &r.Text, &r.Format, &r.CreatedAt, &r.IndexedAt, &r.CID); err != nil { - return nil, err - } - replies = append(replies, r) - } - return replies, nil + return scanReplies(rows) } func (db *DB) GetOrphanedRepliesByAuthor(authorDID string) ([]Reply, error) { - rows, err := db.Query(db.Rebind(` + rows, err := db.Query(` SELECT r.uri, r.author_did, r.parent_uri, r.root_uri, r.text, r.format, r.created_at, r.indexed_at, r.cid FROM replies r LEFT JOIN annotations a ON r.root_uri = a.uri - WHERE r.author_did = ? AND a.uri IS NULL - `), authorDID) + WHERE r.author_did = $1 AND a.uri IS NULL + `, authorDID) if err != nil { return nil, err } defer rows.Close() - var replies []Reply - for rows.Next() { - var r Reply - if err := rows.Scan(&r.URI, &r.AuthorDID, &r.ParentURI, &r.RootURI, &r.Text, &r.Format, &r.CreatedAt, &r.IndexedAt, &r.CID); err != nil { - return nil, err - } - replies = append(replies, r) - } - return replies, nil + return scanReplies(rows) } func (db *DB) GetReplyCount(rootURI string) (int, error) { var count int - err := db.QueryRow(db.Rebind(`SELECT COUNT(*) FROM replies WHERE root_uri = ?`), rootURI).Scan(&count) + err := db.QueryRow(`SELECT COUNT(*) FROM replies WHERE root_uri = $1`, rootURI).Scan(&count) return count, err } @@ -111,19 +87,14 @@ func (db *DB) GetReplyCounts(rootURIs []string) (map[string]int, error) { return map[string]int{}, nil } - query := db.Rebind(` - SELECT root_uri, COUNT(*) - FROM replies - WHERE root_uri IN (` + buildPlaceholders(len(rootURIs)) + `) + query := ` + SELECT root_uri, COUNT(*) + FROM replies + WHERE root_uri = ANY($1) GROUP BY root_uri - `) - - args := make([]interface{}, len(rootURIs)) - for i, uri := range rootURIs { - args[i] = uri - } + ` - rows, err := db.Query(query, args...) + rows, err := db.Query(query, pqStringArray(rootURIs)) if err != nil { return nil, err } @@ -147,23 +118,23 @@ func (db *DB) GetRepliesByURIs(uris []string) ([]Reply, error) { return []Reply{}, nil } - query := db.Rebind(` + rows, err := db.Query(` SELECT uri, author_did, parent_uri, root_uri, text, format, created_at, indexed_at, cid FROM replies - WHERE uri IN (` + buildPlaceholders(len(uris)) + `) - `) - - args := make([]interface{}, len(uris)) - for i, uri := range uris { - args[i] = uri - } - - rows, err := db.Query(query, args...) + WHERE uri = ANY($1) + `, pqStringArray(uris)) if err != nil { return nil, err } defer rows.Close() + return scanReplies(rows) +} + +func scanReplies(rows interface { + Next() bool + Scan(...interface{}) error +}) ([]Reply, error) { var replies []Reply for rows.Next() { var r Reply diff --git a/backend/internal/db/queries_search.go b/backend/internal/db/queries_search.go index fa74ff1..0c89060 100644 --- a/backend/internal/db/queries_search.go +++ b/backend/internal/db/queries_search.go @@ -1,29 +1,38 @@ package db +import "strings" + +func escapeLike(s string) string { + s = strings.ReplaceAll(s, "\\", "\\\\") + s = strings.ReplaceAll(s, "%", "\\%") + s = strings.ReplaceAll(s, "_", "\\_") + return s +} + func (db *DB) SearchAnnotations(query string, authorDID string, limit, offset int) ([]Annotation, error) { - pattern := "%" + query + "%" + pattern := "%" + escapeLike(query) + "%" var baseQuery string var args []interface{} if authorDID != "" { - baseQuery = db.Rebind(` + baseQuery = ` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations - WHERE author_did = ? - AND (body_value LIKE ? OR target_source LIKE ? OR target_title LIKE ? OR tags_json LIKE ? OR selector_json LIKE ?) + WHERE author_did = $1 + AND (body_value ILIKE $2 OR target_source ILIKE $3 OR target_title ILIKE $4 OR tags_json ILIKE $5 OR selector_json ILIKE $6) ORDER BY created_at DESC - LIMIT ? OFFSET ? - `) + LIMIT $7 OFFSET $8 + ` args = []interface{}{authorDID, pattern, pattern, pattern, pattern, pattern, limit, offset} } else { - baseQuery = db.Rebind(` + baseQuery = ` SELECT uri, author_did, motivation, body_value, body_format, body_uri, target_source, target_hash, target_title, selector_json, tags_json, created_at, indexed_at, cid FROM annotations - WHERE body_value LIKE ? OR target_source LIKE ? OR target_title LIKE ? OR tags_json LIKE ? OR selector_json LIKE ? + WHERE body_value ILIKE $1 OR target_source ILIKE $2 OR target_title ILIKE $3 OR tags_json ILIKE $4 OR selector_json ILIKE $5 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `) + LIMIT $6 OFFSET $7 + ` args = []interface{}{pattern, pattern, pattern, pattern, pattern, limit, offset} } @@ -37,29 +46,29 @@ func (db *DB) SearchAnnotations(query string, authorDID string, limit, offset in } func (db *DB) SearchHighlights(query string, authorDID string, limit, offset int) ([]Highlight, error) { - pattern := "%" + query + "%" + pattern := "%" + escapeLike(query) + "%" var baseQuery string var args []interface{} if authorDID != "" { - baseQuery = db.Rebind(` + baseQuery = ` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights - WHERE author_did = ? - AND (target_source LIKE ? OR target_title LIKE ? OR selector_json LIKE ? OR tags_json LIKE ?) + WHERE author_did = $1 + AND (target_source ILIKE $2 OR target_title ILIKE $3 OR selector_json ILIKE $4 OR tags_json ILIKE $5) ORDER BY created_at DESC - LIMIT ? OFFSET ? - `) + LIMIT $6 OFFSET $7 + ` args = []interface{}{authorDID, pattern, pattern, pattern, pattern, limit, offset} } else { - baseQuery = db.Rebind(` + baseQuery = ` SELECT uri, author_did, target_source, target_hash, target_title, selector_json, color, tags_json, created_at, indexed_at, cid FROM highlights - WHERE target_source LIKE ? OR target_title LIKE ? OR selector_json LIKE ? OR tags_json LIKE ? + WHERE target_source ILIKE $1 OR target_title ILIKE $2 OR selector_json ILIKE $3 OR tags_json ILIKE $4 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `) + LIMIT $5 OFFSET $6 + ` args = []interface{}{pattern, pattern, pattern, pattern, limit, offset} } @@ -69,41 +78,33 @@ func (db *DB) SearchHighlights(query string, authorDID string, limit, offset int } defer rows.Close() - var highlights []Highlight - for rows.Next() { - var h Highlight - if err := rows.Scan(&h.URI, &h.AuthorDID, &h.TargetSource, &h.TargetHash, &h.TargetTitle, &h.SelectorJSON, &h.Color, &h.TagsJSON, &h.CreatedAt, &h.IndexedAt, &h.CID); err != nil { - return nil, err - } - highlights = append(highlights, h) - } - return highlights, nil + return scanHighlights(rows) } func (db *DB) SearchBookmarks(query string, authorDID string, limit, offset int) ([]Bookmark, error) { - pattern := "%" + query + "%" + pattern := "%" + escapeLike(query) + "%" var baseQuery string var args []interface{} if authorDID != "" { - baseQuery = db.Rebind(` + baseQuery = ` SELECT uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid FROM bookmarks - WHERE author_did = ? - AND (source LIKE ? OR title LIKE ? OR description LIKE ? OR tags_json LIKE ?) + WHERE author_did = $1 + AND (source ILIKE $2 OR title ILIKE $3 OR description ILIKE $4 OR tags_json ILIKE $5) ORDER BY created_at DESC - LIMIT ? OFFSET ? - `) + LIMIT $6 OFFSET $7 + ` args = []interface{}{authorDID, pattern, pattern, pattern, pattern, limit, offset} } else { - baseQuery = db.Rebind(` + baseQuery = ` SELECT uri, author_did, source, source_hash, title, description, tags_json, created_at, indexed_at, cid FROM bookmarks - WHERE source LIKE ? OR title LIKE ? OR description LIKE ? OR tags_json LIKE ? + WHERE source ILIKE $1 OR title ILIKE $2 OR description ILIKE $3 OR tags_json ILIKE $4 ORDER BY created_at DESC - LIMIT ? OFFSET ? - `) + LIMIT $5 OFFSET $6 + ` args = []interface{}{pattern, pattern, pattern, pattern, limit, offset} } @@ -113,13 +114,5 @@ func (db *DB) SearchBookmarks(query string, authorDID string, limit, offset int) } defer rows.Close() - var bookmarks []Bookmark - for rows.Next() { - var b Bookmark - if err := rows.Scan(&b.URI, &b.AuthorDID, &b.Source, &b.SourceHash, &b.Title, &b.Description, &b.TagsJSON, &b.CreatedAt, &b.IndexedAt, &b.CID); err != nil { - return nil, err - } - bookmarks = append(bookmarks, b) - } - return bookmarks, nil + return scanBookmarks(rows) } diff --git a/backend/internal/db/queries_sessions.go b/backend/internal/db/queries_sessions.go index d3d9a57..f5cd8f1 100644 --- a/backend/internal/db/queries_sessions.go +++ b/backend/internal/db/queries_sessions.go @@ -5,33 +5,33 @@ import ( ) func (db *DB) SaveSession(id, did, handle, accessToken, refreshToken, dpopKey string, expiresAt time.Time) error { - _, err := db.Exec(db.Rebind(` + _, err := db.Exec(` INSERT INTO sessions (id, did, handle, access_token, refresh_token, dpop_key, created_at, expires_at) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) + VALUES ($1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT(id) DO UPDATE SET - access_token = excluded.access_token, - refresh_token = excluded.refresh_token, - dpop_key = excluded.dpop_key, - expires_at = excluded.expires_at - `), id, did, handle, accessToken, refreshToken, dpopKey, time.Now(), expiresAt) + access_token = EXCLUDED.access_token, + refresh_token = EXCLUDED.refresh_token, + dpop_key = EXCLUDED.dpop_key, + expires_at = EXCLUDED.expires_at + `, id, did, handle, accessToken, refreshToken, dpopKey, time.Now(), expiresAt) return err } func (db *DB) GetSession(id string) (did, handle, accessToken, refreshToken, dpopKey string, err error) { - err = db.QueryRow(db.Rebind(` + err = db.QueryRow(` SELECT did, handle, access_token, refresh_token, COALESCE(dpop_key, '') FROM sessions - WHERE id = ? AND expires_at > ? - `), id, time.Now()).Scan(&did, &handle, &accessToken, &refreshToken, &dpopKey) + WHERE id = $1 AND expires_at > $2 + `, id, time.Now()).Scan(&did, &handle, &accessToken, &refreshToken, &dpopKey) return } func (db *DB) DeleteSession(id string) error { - _, err := db.Exec(db.Rebind(`DELETE FROM sessions WHERE id = ?`), id) + _, err := db.Exec(`DELETE FROM sessions WHERE id = $1`, id) return err } func (db *DB) DeleteExpiredSessions() error { - _, err := db.Exec(db.Rebind(`DELETE FROM sessions WHERE expires_at <= ?`), time.Now()) + _, err := db.Exec(`DELETE FROM sessions WHERE expires_at <= $1`, time.Now()) return err } diff --git a/backend/internal/db/tags.go b/backend/internal/db/tags.go index 225b4fe..8152a0e 100644 --- a/backend/internal/db/tags.go +++ b/backend/internal/db/tags.go @@ -1,69 +1,30 @@ package db -import "database/sql" - -type TrendingTag struct { - Tag string `json:"tag"` - Count int `json:"count"` -} - func (db *DB) GetTrendingTags(limit int) ([]TrendingTag, error) { - var query string - if db.driver == "postgres" { - query = ` - SELECT tag, COUNT(*) as count FROM ( - SELECT value as tag, author_did - FROM annotations, json_array_elements_text(tags_json::json) as value - WHERE tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' - AND created_at > NOW() - INTERVAL '14 days' - UNION ALL - SELECT value as tag, author_did - FROM highlights, json_array_elements_text(tags_json::json) as value - WHERE tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' - AND created_at > NOW() - INTERVAL '14 days' - UNION ALL - SELECT value as tag, author_did - FROM bookmarks, json_array_elements_text(tags_json::json) as value - WHERE tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' - AND created_at > NOW() - INTERVAL '14 days' - ) combined - GROUP BY tag - HAVING COUNT(DISTINCT author_did) >= 3 - ORDER BY count DESC - LIMIT $1 - ` - } else { - query = ` - SELECT tag, COUNT(*) as count FROM ( - SELECT json_each.value as tag, author_did - FROM annotations, json_each(annotations.tags_json) - WHERE tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' - AND created_at > datetime('now', '-14 days') - UNION ALL - SELECT json_each.value as tag, author_did - FROM highlights, json_each(highlights.tags_json) - WHERE tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' - AND created_at > datetime('now', '-14 days') - UNION ALL - SELECT json_each.value as tag, author_did - FROM bookmarks, json_each(bookmarks.tags_json) - WHERE tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' - AND created_at > datetime('now', '-14 days') - ) combined - GROUP BY tag - HAVING COUNT(DISTINCT author_did) >= 3 - ORDER BY count DESC - LIMIT ? - ` - } + query := ` + SELECT tag, COUNT(*) as count FROM ( + SELECT value as tag, author_did + FROM annotations, json_array_elements_text(tags_json::json) as value + WHERE tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' + AND created_at > NOW() - INTERVAL '14 days' + UNION ALL + SELECT value as tag, author_did + FROM highlights, json_array_elements_text(tags_json::json) as value + WHERE tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' + AND created_at > NOW() - INTERVAL '14 days' + UNION ALL + SELECT value as tag, author_did + FROM bookmarks, json_array_elements_text(tags_json::json) as value + WHERE tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' + AND created_at > NOW() - INTERVAL '14 days' + ) combined + GROUP BY tag + HAVING COUNT(DISTINCT author_did) >= 3 + ORDER BY count DESC + LIMIT $1 + ` - var rows *sql.Rows - var err error - if db.driver == "postgres" { - rows, err = db.Query(query, limit) - } else { - rows, err = db.Query(db.Rebind(query), limit) - } + rows, err := db.Query(query, limit) if err != nil { return nil, err } @@ -90,60 +51,29 @@ func (db *DB) GetTrendingTags(limit int) ([]TrendingTag, error) { } func (db *DB) GetUserTags(did string, limit int) ([]TrendingTag, error) { - var query string - if db.driver == "postgres" { - query = ` - SELECT tag, SUM(cnt) as count FROM ( - SELECT value as tag, COUNT(*) as cnt - FROM annotations, json_array_elements_text(tags_json::json) as value - WHERE author_did = $1 AND tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' - GROUP BY tag - UNION ALL - SELECT value as tag, COUNT(*) as cnt - FROM highlights, json_array_elements_text(tags_json::json) as value - WHERE author_did = $1 AND tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' - GROUP BY tag - UNION ALL - SELECT value as tag, COUNT(*) as cnt - FROM bookmarks, json_array_elements_text(tags_json::json) as value - WHERE author_did = $1 AND tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' - GROUP BY tag - ) combined + query := ` + SELECT tag, SUM(cnt) as count FROM ( + SELECT value as tag, COUNT(*) as cnt + FROM annotations, json_array_elements_text(tags_json::json) as value + WHERE author_did = $1 AND tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' GROUP BY tag - ORDER BY count DESC - LIMIT $2 - ` - } else { - query = ` - SELECT tag, SUM(cnt) as count FROM ( - SELECT json_each.value as tag, COUNT(*) as cnt - FROM annotations, json_each(annotations.tags_json) - WHERE author_did = ? AND tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' - GROUP BY tag - UNION ALL - SELECT json_each.value as tag, COUNT(*) as cnt - FROM highlights, json_each(highlights.tags_json) - WHERE author_did = ? AND tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' - GROUP BY tag - UNION ALL - SELECT json_each.value as tag, COUNT(*) as cnt - FROM bookmarks, json_each(bookmarks.tags_json) - WHERE author_did = ? AND tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' - GROUP BY tag - ) combined + UNION ALL + SELECT value as tag, COUNT(*) as cnt + FROM highlights, json_array_elements_text(tags_json::json) as value + WHERE author_did = $1 AND tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' GROUP BY tag - ORDER BY count DESC - LIMIT ? - ` - } + UNION ALL + SELECT value as tag, COUNT(*) as cnt + FROM bookmarks, json_array_elements_text(tags_json::json) as value + WHERE author_did = $1 AND tags_json IS NOT NULL AND tags_json != '' AND tags_json != '[]' + GROUP BY tag + ) combined + GROUP BY tag + ORDER BY count DESC + LIMIT $2 + ` - var rows *sql.Rows - var err error - if db.driver == "postgres" { - rows, err = db.Query(query, did, limit) - } else { - rows, err = db.Query(db.Rebind(query), did, did, did, limit) - } + rows, err := db.Query(query, did, limit) if err != nil { return nil, err } @@ -168,3 +98,8 @@ func (db *DB) GetUserTags(did string, limit int) ([]TrendingTag, error) { return tags, nil } + +type TrendingTag struct { + Tag string `json:"tag"` + Count int `json:"count"` +} diff --git a/backend/internal/firehose/ingester.go b/backend/internal/firehose/ingester.go index 752a7ba..b2e716c 100644 --- a/backend/internal/firehose/ingester.go +++ b/backend/internal/firehose/ingester.go @@ -57,15 +57,26 @@ type Ingester struct { currentRelayIdx int onAnnotation AnnotationCallback onDocument DocumentCallback + workerPool chan func() } type RecordHandler func(event *FirehoseEvent) func NewIngester(database *db.DB, syncService *internal_sync.Service) *Ingester { + pool := make(chan func(), 256) + for range 10 { + go func() { + for fn := range pool { + fn() + } + }() + } + i := &Ingester{ - db: database, - sync: syncService, - handlers: make(map[string]RecordHandler), + db: database, + sync: syncService, + handlers: make(map[string]RecordHandler), + workerPool: pool, } i.RegisterHandler(CollectionAnnotation, i.handleAnnotation) @@ -237,7 +248,11 @@ func (i *Ingester) handleCommit(event JetstreamEvent) { i.dispatchToHandler(firehoseEvent) - go i.triggerLazySync(event.Did) + did := event.Did + select { + case i.workerPool <- func() { i.triggerLazySync(did) }: + default: + } } case "delete": i.handleDelete(commit.Collection, uri) @@ -266,7 +281,9 @@ func (i *Ingester) triggerLazySync(did string) { return } - _, err = i.sync.PerformSync(context.Background(), did, func(ctx context.Context, _ string) (*xrpc.Client, error) { + syncCtx, syncCancel := context.WithTimeout(context.Background(), 15*time.Second) + defer syncCancel() + _, err = i.sync.PerformSync(syncCtx, did, func(ctx context.Context, _ string) (*xrpc.Client, error) { return &xrpc.Client{ PDS: pds, }, nil @@ -434,7 +451,11 @@ func (i *Ingester) handleAnnotation(event *FirehoseEvent) { } else { logger.Info("Indexed annotation from %s on %s", event.Repo, targetSource) if i.onAnnotation != nil { - go i.onAnnotation(uri, event.Repo, targetSource, bodyValuePtr, selectorJSONPtr, targetTitlePtr, tagsJSONPtr) + cb := i.onAnnotation + select { + case i.workerPool <- func() { cb(uri, event.Repo, targetSource, bodyValuePtr, selectorJSONPtr, targetTitlePtr, tagsJSONPtr) }: + default: + } } } } diff --git a/backend/internal/recommendations/service.go b/backend/internal/recommendations/service.go index f69f547..a428864 100644 --- a/backend/internal/recommendations/service.go +++ b/backend/internal/recommendations/service.go @@ -374,6 +374,8 @@ func (s *Service) BackfillDocumentEmbeddings(batchSize int) error { if len(docs) < batchSize { break } + + time.Sleep(2 * time.Second) } if total > 0 { @@ -429,6 +431,8 @@ func (s *Service) BackfillAnnotationEmbeddings(batchSize int) (int, error) { if len(anns) < batchSize { break } + + time.Sleep(2 * time.Second) } if total > 0 { @@ -484,6 +488,8 @@ func (s *Service) BackfillHighlightEmbeddings(batchSize int) (int, error) { if len(highlights) < batchSize { break } + + time.Sleep(2 * time.Second) } if total > 0 { diff --git a/backend/internal/verification/verify.go b/backend/internal/verification/verify.go index 8b99b47..822a78c 100644 --- a/backend/internal/verification/verify.go +++ b/backend/internal/verification/verify.go @@ -22,6 +22,8 @@ var client = &http.Client{ }, } +var verifySem = make(chan struct{}, 3) + var linkTagPattern = regexp.MustCompile(`]+rel=["']site\.standard\.document["'][^>]+href=["']([^"']+)["'][^>]*/?>|]+href=["']([^"']+)["'][^>]+rel=["']site\.standard\.document["'][^>]*/?>`) func VerifyPublication(pubURL, expectedURI string) error { @@ -84,7 +86,7 @@ func VerifyDocument(docURL, expectedURI string) error { return fmt.Errorf("document URL returned %d", resp.StatusCode) } - body, err := io.ReadAll(io.LimitReader(resp.Body, 256*1024)) + body, err := io.ReadAll(io.LimitReader(resp.Body, 16*1024)) if err != nil { return fmt.Errorf("failed to read document: %w", err) } @@ -107,6 +109,9 @@ func VerifyDocument(docURL, expectedURI string) error { func VerifyPublicationAsync(pubURL, uri string, onVerified func(string)) { go func() { + verifySem <- struct{}{} + defer func() { <-verifySem }() + if err := VerifyPublication(pubURL, uri); err != nil { return } @@ -119,6 +124,9 @@ func VerifyPublicationAsync(pubURL, uri string, onVerified func(string)) { func VerifyDocumentAsync(docURL, uri string, onVerified func(string)) { go func() { + verifySem <- struct{}{} + defer func() { <-verifySem }() + if err := VerifyDocument(docURL, uri); err != nil { return } diff --git a/extension/src/utils/overlay.ts b/extension/src/utils/overlay.ts index a5c9601..82aadcc 100644 --- a/extension/src/utils/overlay.ts +++ b/extension/src/utils/overlay.ts @@ -765,9 +765,9 @@ export async function initContentScript(ctx: { onInvalidated: (cb: () => void) = const marginLeft = i === 0 ? '0' : '-8px'; if (avatar) { - return ``; + return ``; } else { - return `
${handle[0]?.toUpperCase() || 'U'}
`; + return `
${escapeHtml(handle[0]?.toUpperCase() || 'U')}
`; } }) .join(''); @@ -916,9 +916,9 @@ export async function initContentScript(ctx: { onInvalidated: (cb: () => void) = const isOwned = currentUserDid && author.did === currentUserDid; const createdAt = item.createdAt ? formatRelativeTime(item.createdAt) : ''; - let avatarHtml = `
${handle[0]?.toUpperCase() || 'U'}
`; + let avatarHtml = `
${escapeHtml(handle[0]?.toUpperCase() || 'U')}
`; if (avatar) { - avatarHtml = ``; + avatarHtml = ``; } let bodyHtml = ''; @@ -928,25 +928,26 @@ export async function initContentScript(ctx: { onInvalidated: (cb: () => void) = bodyHtml = `
${escapeHtml(text)}
`; } + const safeId = escapeHtml(id || ''); const addNoteBtn = isHighlight && isOwned - ? `` + ? `` : ''; return ` -
+
${avatarHtml}
- @${handle} - ${createdAt ? `${createdAt}` : ''} + @${escapeHtml(handle)} + ${createdAt ? `${escapeHtml(createdAt)}` : ''}
${bodyHtml}
${addNoteBtn} - ${!isHighlight ? `` : ''} - + ${!isHighlight ? `` : ''} +
`; diff --git a/web/astro.config.mjs b/web/astro.config.mjs index 2374a3b..97e3a1a 100644 --- a/web/astro.config.mjs +++ b/web/astro.config.mjs @@ -6,18 +6,20 @@ import node from "@astrojs/node"; const API_PORT = process.env.API_PORT || 8081; -const isDev = process.env.NODE_ENV === "development"; - // https://astro.build/config export default defineConfig({ + output: "server", adapter: node({ mode: "standalone" }), integrations: [react(), tailwind()], + prefetch: { + prefetchAll: false, + defaultStrategy: "hover", + }, security: { - checkOrigin: false, + checkOrigin: true, }, vite: { ssr: { - noExternal: isDev ? /^(?!react|react-dom|react-router-dom|cookie)/ : true, external: ["@resvg/resvg-js"], }, build: { diff --git a/web/bun.lock b/web/bun.lock index d828f08..3b0c38d 100644 --- a/web/bun.lock +++ b/web/bun.lock @@ -1,17 +1,16 @@ { "lockfileVersion": 1, - "configVersion": 0, "workspaces": { "": { "name": "web", "dependencies": { - "@astrojs/node": "^9.5.2", - "@astrojs/react": "^4.4.2", + "@astrojs/node": "^10.0.3", + "@astrojs/react": "^5.0.1", "@astrojs/tailwind": "^6.0.2", "@nanostores/react": "^1.0.0", "@resvg/resvg-js": "^2.6.2", "@tailwindcss/vite": "^4.1.18", - "astro": "^5.17.1", + "astro": "^6.0.8", "autoprefixer": "^10.4.24", "clsx": "^2.1.1", "date-fns": "^4.1.0", @@ -21,7 +20,6 @@ "postcss": "^8.5.6", "react": "^19.2.4", "react-dom": "^19.2.4", - "react-router-dom": "^7.13.0", "satori": "^0.19.2", "tailwind-merge": "^3.4.0", "tailwindcss": "^3.4.19", @@ -50,17 +48,17 @@ "packages": { "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], - "@astrojs/compiler": ["@astrojs/compiler@2.13.0", "", {}, "sha512-mqVORhUJViA28fwHYaWmsXSzLO9osbdZ5ImUfxBarqsYdMlPbqAqGJCxsNzvppp1BEzc1mJNjOVvQqeDN8Vspw=="], + "@astrojs/compiler": ["@astrojs/compiler@3.0.1", "", {}, "sha512-z97oYbdebO5aoWzuJ/8q5hLK232+17KcLZ7cJ8BCWk6+qNzVxn/gftC0KzMBUTD8WAaBkPpNSQK6PXLnNrZ0CA=="], - "@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.7.5", "", {}, "sha512-vreGnYSSKhAjFJCWAwe/CNhONvoc5lokxtRoZims+0wa3KbHBdPHSSthJsKxPd8d/aic6lWKpRTYGY/hsgK6EA=="], + "@astrojs/internal-helpers": ["@astrojs/internal-helpers@0.8.0", "", { "dependencies": { "picomatch": "^4.0.3" } }, "sha512-J56GrhEiV+4dmrGLPNOl2pZjpHXAndWVyiVDYGDuw6MWKpBSEMLdFxHzeM/6sqaknw9M+HFfHZAcvi3OfT3D/w=="], - "@astrojs/markdown-remark": ["@astrojs/markdown-remark@6.3.10", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.5", "@astrojs/prism": "3.3.0", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.1", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^3.19.0", "smol-toml": "^1.5.2", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.0.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-kk4HeYR6AcnzC4QV8iSlOfh+N8TZ3MEStxPyenyCtemqn8IpEATBFMTJcfrNW32dgpt6MY3oCkMM/Tv3/I4G3A=="], + "@astrojs/markdown-remark": ["@astrojs/markdown-remark@7.0.1", "", { "dependencies": { "@astrojs/internal-helpers": "0.8.0", "@astrojs/prism": "4.0.1", "github-slugger": "^2.0.0", "hast-util-from-html": "^2.0.3", "hast-util-to-text": "^4.0.2", "js-yaml": "^4.1.1", "mdast-util-definitions": "^6.0.0", "rehype-raw": "^7.0.0", "rehype-stringify": "^10.0.1", "remark-gfm": "^4.0.1", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "remark-smartypants": "^3.0.2", "shiki": "^4.0.0", "smol-toml": "^1.6.0", "unified": "^11.0.5", "unist-util-remove-position": "^5.0.0", "unist-util-visit": "^5.1.0", "unist-util-visit-parents": "^6.0.2", "vfile": "^6.0.3" } }, "sha512-zAfLJmn07u9SlDNNHTpjv0RT4F8D4k54NR7ReRas8CO4OeGoqSvOuKwqCFg2/cqN3wHwdWlK/7Yv/lMXlhVIaw=="], - "@astrojs/node": ["@astrojs/node@9.5.2", "", { "dependencies": { "@astrojs/internal-helpers": "0.7.5", "send": "^1.2.1", "server-destroy": "^1.0.1" }, "peerDependencies": { "astro": "^5.14.3" } }, "sha512-85/x+FRwbNGDip1TzSGMiak31/6LvBhA8auqd9lLoHaM5XElk+uIfIr3KjJqucDojE0PtiLk1lMSwD9gd3YlGg=="], + "@astrojs/node": ["@astrojs/node@10.0.3", "", { "dependencies": { "@astrojs/internal-helpers": "0.8.0", "send": "^1.2.1", "server-destroy": "^1.0.1" }, "peerDependencies": { "astro": "^6.0.0" } }, "sha512-yWDPaXTOw34h9qNpxDBz1Xj5HudnyuWW2E8ZSegW6o8n+mKI3Yq/iLAUQfxA3h8wfaIRY/PCh3T2jLAys2SXeQ=="], - "@astrojs/prism": ["@astrojs/prism@3.3.0", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ=="], + "@astrojs/prism": ["@astrojs/prism@4.0.1", "", { "dependencies": { "prismjs": "^1.30.0" } }, "sha512-nksZQVjlferuWzhPsBpQ1JE5XuKAf1id1/9Hj4a9KG4+ofrlzxUUwX4YGQF/SuDiuiGKEnzopGOt38F3AnVWsQ=="], - "@astrojs/react": ["@astrojs/react@4.4.2", "", { "dependencies": { "@vitejs/plugin-react": "^4.7.0", "ultrahtml": "^1.6.0", "vite": "^6.4.1" }, "peerDependencies": { "@types/react": "^17.0.50 || ^18.0.21 || ^19.0.0", "@types/react-dom": "^17.0.17 || ^18.0.6 || ^19.0.0", "react": "^17.0.2 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0" } }, "sha512-1tl95bpGfuaDMDn8O3x/5Dxii1HPvzjvpL2YTuqOOrQehs60I2DKiDgh1jrKc7G8lv+LQT5H15V6QONQ+9waeQ=="], + "@astrojs/react": ["@astrojs/react@5.0.1", "", { "dependencies": { "@astrojs/internal-helpers": "0.8.0", "@vitejs/plugin-react": "^5.1.4", "devalue": "^5.6.3", "ultrahtml": "^1.6.0", "vite": "^7.3.1" }, "peerDependencies": { "@types/react": "^17.0.50 || ^18.0.21 || ^19.0.0", "@types/react-dom": "^17.0.17 || ^18.0.6 || ^19.0.0", "react": "^17.0.2 || ^18.0.0 || ^19.0.0", "react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0" } }, "sha512-gJgQfDUxyePk+UIzwCEtAq04SGbziwRNwOMYvkxLHEtZScSMvRnvQhDWAEMCjLwwEomoT92Tfm34xpD7XAAzOg=="], "@astrojs/tailwind": ["@astrojs/tailwind@6.0.2", "", { "dependencies": { "autoprefixer": "^10.4.21", "postcss": "^8.5.3", "postcss-load-config": "^4.0.2" }, "peerDependencies": { "astro": "^3.0.0 || ^4.0.0 || ^5.0.0", "tailwindcss": "^3.0.24" } }, "sha512-j3mhLNeugZq6A8dMNXVarUa8K6X9AW+QHU9u3lKNrPLMHhOQ0S7VeWhHwEeJFpEK1BTKEUY1U78VQv2gN6hNGg=="], @@ -106,59 +104,63 @@ "@capsizecss/unpack": ["@capsizecss/unpack@4.0.0", "", { "dependencies": { "fontkitten": "^1.0.0" } }, "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA=="], + "@clack/core": ["@clack/core@1.1.0", "", { "dependencies": { "sisteransi": "^1.0.5" } }, "sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA=="], + + "@clack/prompts": ["@clack/prompts@1.1.0", "", { "dependencies": { "@clack/core": "1.1.0", "sisteransi": "^1.0.5" } }, "sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g=="], + "@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q=="], - "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + "@esbuild/android-arm": ["@esbuild/android-arm@0.27.4", "", { "os": "android", "cpu": "arm" }, "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ=="], - "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.4", "", { "os": "android", "cpu": "arm64" }, "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw=="], - "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + "@esbuild/android-x64": ["@esbuild/android-x64@0.27.4", "", { "os": "android", "cpu": "x64" }, "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw=="], - "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ=="], - "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw=="], - "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw=="], - "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ=="], - "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.4", "", { "os": "linux", "cpu": "arm" }, "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg=="], - "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA=="], - "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.4", "", { "os": "linux", "cpu": "ia32" }, "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA=="], - "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.4", "", { "os": "linux", "cpu": "none" }, "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA=="], - "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.4", "", { "os": "linux", "cpu": "none" }, "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw=="], - "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA=="], - "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.4", "", { "os": "linux", "cpu": "none" }, "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw=="], - "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA=="], - "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.4", "", { "os": "linux", "cpu": "x64" }, "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA=="], - "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.4", "", { "os": "none", "cpu": "arm64" }, "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q=="], - "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.4", "", { "os": "none", "cpu": "x64" }, "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg=="], - "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.4", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow=="], - "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ=="], - "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.4", "", { "os": "none", "cpu": "arm64" }, "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg=="], - "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.4", "", { "os": "sunos", "cpu": "x64" }, "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g=="], - "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg=="], - "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw=="], - "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.4", "", { "os": "win32", "cpu": "x64" }, "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg=="], "@eslint-community/eslint-utils": ["@eslint-community/eslint-utils@4.9.1", "", { "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ=="], @@ -286,7 +288,7 @@ "@resvg/resvg-js-win32-x64-msvc": ["@resvg/resvg-js-win32-x64-msvc@2.6.2", "", { "os": "win32", "cpu": "x64" }, "sha512-ZXtYhtUr5SSaBrUDq7DiyjOFJqBVL/dOBN7N/qmi/pO0IgiWW/f/ue3nbvu9joWE5aAKDoIzy/CxsY0suwGosQ=="], - "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.27", "", {}, "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA=="], + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-rc.3", "", {}, "sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q=="], "@rollup/pluginutils": ["@rollup/pluginutils@5.3.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q=="], @@ -340,17 +342,19 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.57.1", "", { "os": "win32", "cpu": "x64" }, "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA=="], - "@shikijs/core": ["@shikijs/core@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-iAlTtSDDbJiRpvgL5ugKEATDtHdUVkqgHDm/gbD2ZS9c88mx7G1zSYjjOxp5Qa0eaW0MAQosFRmJSk354PRoQA=="], + "@shikijs/core": ["@shikijs/core@4.0.2", "", { "dependencies": { "@shikijs/primitive": "4.0.2", "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw=="], + + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag=="], - "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.4" } }, "sha512-jdKhfgW9CRtj3Tor0L7+yPwdG3CgP7W+ZEqSsojrMzCjD1e0IxIbwUMDDpYlVBlC08TACg4puwFGkZfLS+56Tw=="], + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg=="], - "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-DyXsOG0vGtNtl7ygvabHd7Mt5EY8gCNqR9Y7Lpbbd/PbJvgWrqaKzH1JW6H6qFkuUa8aCxoiYVv8/YfFljiQxA=="], + "@shikijs/langs": ["@shikijs/langs@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2" } }, "sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg=="], - "@shikijs/langs": ["@shikijs/langs@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0" } }, "sha512-x/42TfhWmp6H00T6uwVrdTJGKgNdFbrEdhaDwSR5fd5zhQ1Q46bHq9EO61SCEWJR0HY7z2HNDMaBZp8JRmKiIA=="], + "@shikijs/primitive": ["@shikijs/primitive@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw=="], - "@shikijs/themes": ["@shikijs/themes@3.22.0", "", { "dependencies": { "@shikijs/types": "3.22.0" } }, "sha512-o+tlOKqsr6FE4+mYJG08tfCFDS+3CG20HbldXeVoyP+cYSUxDhrFf3GPjE60U55iOkkjbpY2uC3It/eeja35/g=="], + "@shikijs/themes": ["@shikijs/themes@4.0.2", "", { "dependencies": { "@shikijs/types": "4.0.2" } }, "sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA=="], - "@shikijs/types": ["@shikijs/types@3.22.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-491iAekgKDBFE67z70Ok5a8KBMsQ2IJwOWw3us/7ffQkIBCyOQfm/aNwVMBUriP02QshIfgHCBSIYAl3u2eWjg=="], + "@shikijs/types": ["@shikijs/types@4.0.2", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg=="], "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], @@ -440,7 +444,7 @@ "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], - "@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@5.2.0", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw=="], "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], @@ -448,12 +452,6 @@ "ajv": ["ajv@6.12.6", "", { "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" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], - "ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="], - - "ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], - - "ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], - "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], @@ -480,7 +478,7 @@ "arraybuffer.prototype.slice": ["arraybuffer.prototype.slice@1.0.4", "", { "dependencies": { "array-buffer-byte-length": "^1.0.1", "call-bind": "^1.0.8", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "is-array-buffer": "^3.0.4" } }, "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ=="], - "astro": ["astro@5.17.1", "", { "dependencies": { "@astrojs/compiler": "^2.13.0", "@astrojs/internal-helpers": "0.7.5", "@astrojs/markdown-remark": "6.3.10", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^4.0.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "acorn": "^8.15.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.3.1", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.1.1", "cssesc": "^3.0.0", "debug": "^4.4.3", "deterministic-object-hash": "^2.0.2", "devalue": "^5.6.2", "diff": "^8.0.3", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.7.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.4.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "import-meta-resolve": "^4.2.0", "js-yaml": "^4.1.1", "magic-string": "^0.30.21", "magicast": "^0.5.1", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.1", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.3", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.3", "shiki": "^3.21.0", "smol-toml": "^1.6.0", "svgo": "^4.0.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", "unifont": "~0.7.3", "unist-util-visit": "^5.0.0", "unstorage": "^1.17.4", "vfile": "^6.0.3", "vite": "^6.4.1", "vitefu": "^1.1.1", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.3", "zod": "^3.25.76", "zod-to-json-schema": "^3.25.1", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "astro.js" } }, "sha512-oD3tlxTaVWGq/Wfbqk6gxzVRz98xa/rYlpe+gU2jXJMSD01k6sEDL01ZlT8mVSYB/rMgnvIOfiQQ3BbLdN237A=="], + "astro": ["astro@6.0.8", "", { "dependencies": { "@astrojs/compiler": "^3.0.0", "@astrojs/internal-helpers": "0.8.0", "@astrojs/markdown-remark": "7.0.1", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^4.0.0", "@clack/prompts": "^1.0.1", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", "clsx": "^2.1.1", "common-ancestor-path": "^2.0.0", "cookie": "^1.1.1", "devalue": "^5.6.3", "diff": "^8.0.3", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^2.0.0", "esbuild": "^0.27.3", "flattie": "^1.1.1", "fontace": "~0.4.1", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.2.0", "js-yaml": "^4.1.1", "magic-string": "^0.30.21", "magicast": "^0.5.2", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "obug": "^2.1.1", "p-limit": "^7.3.0", "p-queue": "^9.1.0", "package-manager-detector": "^1.6.0", "piccolore": "^0.1.3", "picomatch": "^4.0.3", "rehype": "^13.0.2", "semver": "^7.7.4", "shiki": "^4.0.0", "smol-toml": "^1.6.0", "svgo": "^4.0.0", "tinyclip": "^0.1.6", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tsconfck": "^3.1.6", "ultrahtml": "^1.6.0", "unifont": "~0.7.4", "unist-util-visit": "^5.1.0", "unstorage": "^1.17.4", "vfile": "^6.0.3", "vite": "^7.3.1", "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", "zod": "^4.3.6" }, "optionalDependencies": { "sharp": "^0.34.0" }, "bin": { "astro": "bin/astro.mjs" } }, "sha512-DCPeb8GKOoFWh+8whB7Qi/kKWD/6NcQ9nd1QVNzJFxgHkea3WYrNroQRq4whmBdjhkYPTLS/1gmUAl2iA2Es2g=="], "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], @@ -494,8 +492,6 @@ "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], - "base-64": ["base-64@1.0.0", "", {}, "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg=="], - "base64-js": ["base64-js@0.0.8", "", {}, "sha512-3XSA2cR/h/73EzlXXdU6YNycmYI7+kicTxks4eJg2g39biHR84slg2+des+p7iHYhbRg/udIS4TD53WabcOUkw=="], "baseline-browser-mapping": ["baseline-browser-mapping@2.9.19", "", { "bin": { "baseline-browser-mapping": "dist/cli.js" } }, "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg=="], @@ -504,8 +500,6 @@ "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], - "boxen": ["boxen@8.0.1", "", { "dependencies": { "ansi-align": "^3.0.1", "camelcase": "^8.0.0", "chalk": "^5.3.0", "cli-boxes": "^3.0.0", "string-width": "^7.2.0", "type-fest": "^4.21.0", "widest-line": "^5.0.0", "wrap-ansi": "^9.0.0" } }, "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw=="], - "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], @@ -518,8 +512,6 @@ "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], - "camelcase": ["camelcase@8.0.0", "", {}, "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA=="], - "camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="], "camelize": ["camelize@1.0.1", "", {}, "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ=="], @@ -528,8 +520,6 @@ "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], - "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], - "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], @@ -540,8 +530,6 @@ "ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="], - "cli-boxes": ["cli-boxes@3.0.0", "", {}, "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g=="], - "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], @@ -550,7 +538,7 @@ "commander": ["commander@11.1.0", "", {}, "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ=="], - "common-ancestor-path": ["common-ancestor-path@1.0.1", "", {}, "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w=="], + "common-ancestor-path": ["common-ancestor-path@2.0.0", "", {}, "sha512-dnN3ibLeoRf2HNC+OlCiNc5d2zxbLJXOtiZUudNFSXZrNSydxcCsSpRzXwfu7BBWCIfHPw+xTayeBvJCP/D8Ng=="], "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], @@ -614,9 +602,7 @@ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], - "deterministic-object-hash": ["deterministic-object-hash@2.0.2", "", { "dependencies": { "base-64": "^1.0.0" } }, "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ=="], - - "devalue": ["devalue@5.6.2", "", {}, "sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg=="], + "devalue": ["devalue@5.6.4", "", {}, "sha512-Gp6rDldRsFh/7XuouDbxMH3Mx8GMCcgzIb1pDTvNyn8pZGQ22u+Wa+lGV9dQCltFQ7uVw0MhRyb8XDskNFOReA=="], "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], @@ -646,8 +632,6 @@ "emoji-picker-react": ["emoji-picker-react@4.18.0", "", { "dependencies": { "flairup": "1.0.0" }, "peerDependencies": { "react": ">=16" } }, "sha512-vLTrLfApXAIciguGE57pXPWs9lPLBspbEpPMiUq03TIli2dHZBiB+aZ0R9/Wat0xmTfcd4AuEzQgSYxEZ8C88Q=="], - "emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], - "emoji-regex-xs": ["emoji-regex-xs@2.0.1", "", {}, "sha512-1QFuh8l7LqUcKe24LsPUNzjrzJQ7pgRwp1QMcZ5MX6mFplk2zQ08NVCM84++1cveaUUYtcCYHmeFEuNg16sU4g=="], "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], @@ -664,7 +648,7 @@ "es-iterator-helpers": ["es-iterator-helpers@1.2.2", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.4", "define-properties": "^1.2.1", "es-abstract": "^1.24.1", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.3.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", "safe-array-concat": "^1.1.3" } }, "sha512-BrUQ0cPTB/IwXj23HtwHjS9n7O4h9FX94b4xc5zlTHxeLgTAdzYUDyy6KdExAl9lbN5rtfe44xpjpmj9grxs5w=="], - "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + "es-module-lexer": ["es-module-lexer@2.0.0", "", {}, "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw=="], "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], @@ -674,7 +658,7 @@ "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], - "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + "esbuild": ["esbuild@0.27.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.4", "@esbuild/android-arm": "0.27.4", "@esbuild/android-arm64": "0.27.4", "@esbuild/android-x64": "0.27.4", "@esbuild/darwin-arm64": "0.27.4", "@esbuild/darwin-x64": "0.27.4", "@esbuild/freebsd-arm64": "0.27.4", "@esbuild/freebsd-x64": "0.27.4", "@esbuild/linux-arm": "0.27.4", "@esbuild/linux-arm64": "0.27.4", "@esbuild/linux-ia32": "0.27.4", "@esbuild/linux-loong64": "0.27.4", "@esbuild/linux-mips64el": "0.27.4", "@esbuild/linux-ppc64": "0.27.4", "@esbuild/linux-riscv64": "0.27.4", "@esbuild/linux-s390x": "0.27.4", "@esbuild/linux-x64": "0.27.4", "@esbuild/netbsd-arm64": "0.27.4", "@esbuild/netbsd-x64": "0.27.4", "@esbuild/openbsd-arm64": "0.27.4", "@esbuild/openbsd-x64": "0.27.4", "@esbuild/openharmony-arm64": "0.27.4", "@esbuild/sunos-x64": "0.27.4", "@esbuild/win32-arm64": "0.27.4", "@esbuild/win32-ia32": "0.27.4", "@esbuild/win32-x64": "0.27.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], @@ -706,7 +690,7 @@ "estraverse": ["estraverse@5.3.0", "", {}, "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA=="], - "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], @@ -768,8 +752,6 @@ "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], - "get-east-asian-width": ["get-east-asian-width@1.4.0", "", {}, "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q=="], - "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], @@ -838,8 +820,6 @@ "ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], - "import-meta-resolve": ["import-meta-resolve@4.2.0", "", {}, "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg=="], - "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], @@ -872,8 +852,6 @@ "is-finalizationregistry": ["is-finalizationregistry@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg=="], - "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], - "is-generator-function": ["is-generator-function@1.1.2", "", { "dependencies": { "call-bound": "^1.0.4", "generator-function": "^2.0.0", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" } }, "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA=="], "is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="], @@ -936,8 +914,6 @@ "keyv": ["keyv@4.5.4", "", { "dependencies": { "json-buffer": "3.0.1" } }, "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw=="], - "kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], - "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], "lightningcss": ["lightningcss@1.30.2", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.30.2", "lightningcss-darwin-arm64": "1.30.2", "lightningcss-darwin-x64": "1.30.2", "lightningcss-freebsd-x64": "1.30.2", "lightningcss-linux-arm-gnueabihf": "1.30.2", "lightningcss-linux-arm64-gnu": "1.30.2", "lightningcss-linux-arm64-musl": "1.30.2", "lightningcss-linux-x64-gnu": "1.30.2", "lightningcss-linux-x64-musl": "1.30.2", "lightningcss-win32-arm64-msvc": "1.30.2", "lightningcss-win32-x64-msvc": "1.30.2" } }, "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ=="], @@ -1124,6 +1100,8 @@ "object.values": ["object.values@1.2.1", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-object-atoms": "^1.0.0" } }, "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA=="], + "obug": ["obug@2.1.1", "", {}, "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ=="], + "ofetch": ["ofetch@1.5.1", "", { "dependencies": { "destr": "^2.0.5", "node-fetch-native": "^1.6.7", "ufo": "^1.6.1" } }, "sha512-2W4oUZlVaqAPAil6FUg/difl6YhqhUR7x2eZY4bQCko22UXg3hptq9KLQdqFClV+Wu85UX7hNtdGTngi/1BxcA=="], "ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], @@ -1138,13 +1116,13 @@ "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], - "p-limit": ["p-limit@6.2.0", "", { "dependencies": { "yocto-queue": "^1.1.1" } }, "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA=="], + "p-limit": ["p-limit@7.3.0", "", { "dependencies": { "yocto-queue": "^1.2.1" } }, "sha512-7cIXg/Z0M5WZRblrsOla88S4wAK+zOQQWeBYfV3qJuJXMr+LnbYjaadrFaS0JILfEDPVqHyKnZ1Z/1d6J9VVUw=="], "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], - "p-queue": ["p-queue@8.1.1", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^6.1.2" } }, "sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ=="], + "p-queue": ["p-queue@9.1.0", "", { "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^7.0.0" } }, "sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw=="], - "p-timeout": ["p-timeout@6.1.4", "", {}, "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg=="], + "p-timeout": ["p-timeout@7.0.1", "", {}, "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg=="], "package-manager-detector": ["package-manager-detector@1.6.0", "", {}, "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA=="], @@ -1196,8 +1174,6 @@ "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], - "prompts": ["prompts@2.4.2", "", { "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" } }, "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q=="], - "prop-types": ["prop-types@15.8.1", "", { "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", "react-is": "^16.13.1" } }, "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg=="], "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], @@ -1218,11 +1194,7 @@ "react-is": ["react-is@16.13.1", "", {}, "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="], - "react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="], - - "react-router": ["react-router@7.13.0", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-PZgus8ETambRT17BUm/LL8lX3Of+oiLaPuVTRH3l1eLvSPpKO3AvhAEb5N7ihAFZQrYDqkvvWfFh9p0z9VsjLw=="], - - "react-router-dom": ["react-router-dom@7.13.0", "", { "dependencies": { "react-router": "7.13.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" } }, "sha512-5CO/l5Yahi2SKC6rGZ+HDEjpjkGaG/ncEP7eWFTvFxbHP8yeeI0PxTDjimtpXYlR3b3i9/WIL4VJttPrESIf2g=="], + "react-refresh": ["react-refresh@0.18.0", "", {}, "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw=="], "read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="], @@ -1290,8 +1262,6 @@ "server-destroy": ["server-destroy@1.0.1", "", {}, "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ=="], - "set-cookie-parser": ["set-cookie-parser@2.7.2", "", {}, "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw=="], - "set-function-length": ["set-function-length@1.2.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2" } }, "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg=="], "set-function-name": ["set-function-name@2.0.2", "", { "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", "functions-have-names": "^1.2.3", "has-property-descriptors": "^1.0.2" } }, "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ=="], @@ -1306,7 +1276,7 @@ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "shiki": ["shiki@3.22.0", "", { "dependencies": { "@shikijs/core": "3.22.0", "@shikijs/engine-javascript": "3.22.0", "@shikijs/engine-oniguruma": "3.22.0", "@shikijs/langs": "3.22.0", "@shikijs/themes": "3.22.0", "@shikijs/types": "3.22.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-LBnhsoYEe0Eou4e1VgJACes+O6S6QC0w71fCSp5Oya79inkwkm15gQ1UF6VtQ8j/taMDh79hAB49WUk8ALQW3g=="], + "shiki": ["shiki@4.0.2", "", { "dependencies": { "@shikijs/core": "4.0.2", "@shikijs/engine-javascript": "4.0.2", "@shikijs/engine-oniguruma": "4.0.2", "@shikijs/langs": "4.0.2", "@shikijs/themes": "4.0.2", "@shikijs/types": "4.0.2", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ=="], "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], @@ -1328,8 +1298,6 @@ "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], - "string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], - "string.prototype.codepointat": ["string.prototype.codepointat@0.2.1", "", {}, "sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg=="], "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="], @@ -1344,8 +1312,6 @@ "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], - "strip-ansi": ["strip-ansi@7.1.2", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA=="], - "sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="], "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], @@ -1366,6 +1332,8 @@ "tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="], + "tinyclip": ["tinyclip@0.1.12", "", {}, "sha512-Ae3OVUqifDw0wBriIBS7yVaW44Dp6eSHQcyq4Igc7eN2TJH/2YsicswaW+J/OuMvhpDPOKEgpAZCjkb4hpoyeA=="], + "tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="], "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], @@ -1388,8 +1356,6 @@ "type-check": ["type-check@0.4.0", "", { "dependencies": { "prelude-ls": "^1.2.1" } }, "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew=="], - "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], - "typed-array-buffer": ["typed-array-buffer@1.0.3", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "is-typed-array": "^1.1.14" } }, "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw=="], "typed-array-byte-length": ["typed-array-byte-length@1.0.3", "", { "dependencies": { "call-bind": "^1.0.8", "for-each": "^0.3.3", "gopd": "^1.2.0", "has-proto": "^1.2.0", "is-typed-array": "^1.1.14" } }, "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg=="], @@ -1416,7 +1382,7 @@ "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], - "unifont": ["unifont@0.7.3", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-b0GtQzKCyuSHGsfj5vyN8st7muZ6VCI4XD4vFlr7Uy1rlWVYxC3npnfk8MyreHxJYrz1ooLDqDzFe9XqQTlAhA=="], + "unifont": ["unifont@0.7.4", "", { "dependencies": { "css-tree": "^3.1.0", "ofetch": "^1.5.1", "ohash": "^2.0.11" } }, "sha512-oHeis4/xl42HUIeHuNZRGEvxj5AaIKR+bHPNegRq5LV1gdc3jundpONbjglKpihmJf+dswygdMJn3eftGIMemg=="], "unist-util-find-after": ["unist-util-find-after@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ=="], @@ -1450,9 +1416,9 @@ "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], - "vite": ["vite@6.4.1", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g=="], + "vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.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" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="], - "vitefu": ["vitefu@1.1.1", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ=="], + "vitefu": ["vitefu@1.1.2", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-zpKATdUbzbsycPFBN71nS2uzBUQiVnFoOrr2rvqv34S1lcAgMKKkjWleLGeiJlZ8lwCXvtWaRn7R3ZC16SYRuw=="], "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], @@ -1468,34 +1434,22 @@ "which-typed-array": ["which-typed-array@1.1.20", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg=="], - "widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="], - "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], - "wrap-ansi": ["wrap-ansi@9.0.2", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww=="], - "xxhash-wasm": ["xxhash-wasm@1.1.0", "", {}, "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA=="], "yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], "yaml": ["yaml@2.8.2", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A=="], - "yargs-parser": ["yargs-parser@21.1.1", "", {}, "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="], + "yargs-parser": ["yargs-parser@22.0.0", "", {}, "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw=="], "yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="], - "yocto-spinner": ["yocto-spinner@0.2.3", "", { "dependencies": { "yoctocolors": "^2.1.1" } }, "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ=="], - - "yoctocolors": ["yoctocolors@2.1.2", "", {}, "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug=="], - "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "zod-to-json-schema": ["zod-to-json-schema@3.25.1", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA=="], - - "zod-to-ts": ["zod-to-ts@1.2.0", "", { "peerDependencies": { "typescript": "^4.9.4 || ^5.0.2", "zod": "^3" } }, "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA=="], - "zod-validation-error": ["zod-validation-error@4.0.2", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ=="], "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], @@ -1504,8 +1458,6 @@ "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], - "@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], - "@tailwindcss/node/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], "@tailwindcss/node/tailwindcss": ["tailwindcss@4.1.18", "", {}, "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw=="], @@ -1530,11 +1482,11 @@ "@typescript-eslint/visitor-keys/eslint-visitor-keys": ["eslint-visitor-keys@4.2.1", "", {}, "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="], - "ansi-align/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], - "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], - "astro/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "astro/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "astro/zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], @@ -1568,16 +1520,10 @@ "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], - "ansi-align/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - - "ansi-align/string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], "unstorage/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="], - - "ansi-align/string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], } } diff --git a/web/package-lock.json b/web/package-lock.json deleted file mode 100644 index fa0efda..0000000 --- a/web/package-lock.json +++ /dev/null @@ -1,9674 +0,0 @@ -{ - "name": "web", - "version": "0.0.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "web", - "version": "0.0.1", - "dependencies": { - "@astrojs/node": "^9.5.2", - "@astrojs/react": "^4.4.2", - "@astrojs/tailwind": "^6.0.2", - "@nanostores/react": "^1.0.0", - "@resvg/resvg-js": "^2.6.2", - "@tailwindcss/vite": "^4.1.18", - "astro": "^5.17.1", - "autoprefixer": "^10.4.24", - "clsx": "^2.1.1", - "date-fns": "^4.1.0", - "emoji-picker-react": "^4.18.0", - "lucide-react": "^0.563.0", - "nanostores": "^1.1.0", - "postcss": "^8.5.6", - "react": "^19.2.4", - "react-dom": "^19.2.4", - "react-router-dom": "^7.13.0", - "satori": "^0.19.2", - "tailwind-merge": "^3.4.0", - "tailwindcss": "^3.4.19" - }, - "devDependencies": { - "@eslint/js": "^10.0.1", - "@types/node": "^25.2.3", - "@types/react": "^19.2.11", - "@types/react-dom": "^19.2.3", - "@typescript-eslint/eslint-plugin": "^8.54.0", - "@typescript-eslint/parser": "^8.54.0", - "eslint": "^10.0.0", - "eslint-config-prettier": "^10.1.8", - "eslint-plugin-prettier": "^5.5.5", - "eslint-plugin-react": "^7.37.5", - "eslint-plugin-react-hooks": "^7.0.1", - "eslint-plugin-react-refresh": "^0.5.0", - "globals": "^17.3.0", - "prettier": "^3.8.1", - "react-icons": "^5.5.0", - "typescript": "^5.9.3", - "typescript-eslint": "^8.54.0" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@astrojs/compiler": { - "version": "2.13.0", - "license": "MIT" - }, - "node_modules/@astrojs/internal-helpers": { - "version": "0.7.5", - "license": "MIT" - }, - "node_modules/@astrojs/markdown-remark": { - "version": "6.3.10", - "license": "MIT", - "dependencies": { - "@astrojs/internal-helpers": "0.7.5", - "@astrojs/prism": "3.3.0", - "github-slugger": "^2.0.0", - "hast-util-from-html": "^2.0.3", - "hast-util-to-text": "^4.0.2", - "import-meta-resolve": "^4.2.0", - "js-yaml": "^4.1.1", - "mdast-util-definitions": "^6.0.0", - "rehype-raw": "^7.0.0", - "rehype-stringify": "^10.0.1", - "remark-gfm": "^4.0.1", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.1.2", - "remark-smartypants": "^3.0.2", - "shiki": "^3.19.0", - "smol-toml": "^1.5.2", - "unified": "^11.0.5", - "unist-util-remove-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "unist-util-visit-parents": "^6.0.2", - "vfile": "^6.0.3" - } - }, - "node_modules/@astrojs/node": { - "version": "9.5.2", - "license": "MIT", - "dependencies": { - "@astrojs/internal-helpers": "0.7.5", - "send": "^1.2.1", - "server-destroy": "^1.0.1" - }, - "peerDependencies": { - "astro": "^5.14.3" - } - }, - "node_modules/@astrojs/prism": { - "version": "3.3.0", - "license": "MIT", - "dependencies": { - "prismjs": "^1.30.0" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0" - } - }, - "node_modules/@astrojs/react": { - "version": "4.4.2", - "license": "MIT", - "dependencies": { - "@vitejs/plugin-react": "^4.7.0", - "ultrahtml": "^1.6.0", - "vite": "^6.4.1" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0" - }, - "peerDependencies": { - "@types/react": "^17.0.50 || ^18.0.21 || ^19.0.0", - "@types/react-dom": "^17.0.17 || ^18.0.6 || ^19.0.0", - "react": "^17.0.2 || ^18.0.0 || ^19.0.0", - "react-dom": "^17.0.2 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/@astrojs/tailwind": { - "version": "6.0.2", - "license": "MIT", - "dependencies": { - "autoprefixer": "^10.4.21", - "postcss": "^8.5.3", - "postcss-load-config": "^4.0.2" - }, - "peerDependencies": { - "astro": "^3.0.0 || ^4.0.0 || ^5.0.0", - "tailwindcss": "^3.0.24" - } - }, - "node_modules/@astrojs/telemetry": { - "version": "3.3.0", - "license": "MIT", - "dependencies": { - "ci-info": "^4.2.0", - "debug": "^4.4.0", - "dlv": "^1.1.3", - "dset": "^3.1.4", - "is-docker": "^3.0.0", - "is-wsl": "^3.1.0", - "which-pm-runs": "^1.1.0" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.29.0", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.29.0", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.29.0", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", - "@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/core/node_modules/semver": { - "version": "6.3.1", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.29.1", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "@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.28.6", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.28.6", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.28.6", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.29.0", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.29.0" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.28.6", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.29.0", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.29.0", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@capsizecss/unpack": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "fontkitten": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.8.1", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", - "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", - "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", - "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", - "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.12", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", - "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", - "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", - "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", - "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", - "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", - "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", - "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", - "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", - "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", - "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", - "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", - "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", - "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", - "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", - "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", - "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", - "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", - "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", - "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", - "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", - "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.1", - "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", - "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", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.23.1", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^3.0.1", - "debug": "^4.3.1", - "minimatch": "^10.1.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.5.2", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.1.0" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/core": { - "version": "1.1.0", - "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", - "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.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.6.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^1.1.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "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", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@img/colour": { - "version": "1.0.0", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", - "cpu": [ - "ppc64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", - "cpu": [ - "riscv64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", - "cpu": [ - "ppc64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", - "cpu": [ - "riscv64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.7.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@isaacs/balanced-match": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@isaacs/brace-expansion": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "@isaacs/balanced-match": "^4.0.1" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@nanostores/react": { - "version": "1.0.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "engines": { - "node": "^20.0.0 || >=22.0.0" - }, - "peerDependencies": { - "nanostores": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^1.0.0", - "react": ">=18.0.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@oslojs/encoding": { - "version": "1.1.0", - "license": "MIT" - }, - "node_modules/@pkgr/core": { - "version": "0.2.9", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@resvg/resvg-js": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@resvg/resvg-js/-/resvg-js-2.6.2.tgz", - "integrity": "sha512-xBaJish5OeGmniDj9cW5PRa/PtmuVU3ziqrbr5xJj901ZDN4TosrVaNZpEiLZAxdfnhAe7uQ7QFWfjPe9d9K2Q==", - "license": "MPL-2.0", - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@resvg/resvg-js-android-arm-eabi": "2.6.2", - "@resvg/resvg-js-android-arm64": "2.6.2", - "@resvg/resvg-js-darwin-arm64": "2.6.2", - "@resvg/resvg-js-darwin-x64": "2.6.2", - "@resvg/resvg-js-linux-arm-gnueabihf": "2.6.2", - "@resvg/resvg-js-linux-arm64-gnu": "2.6.2", - "@resvg/resvg-js-linux-arm64-musl": "2.6.2", - "@resvg/resvg-js-linux-x64-gnu": "2.6.2", - "@resvg/resvg-js-linux-x64-musl": "2.6.2", - "@resvg/resvg-js-win32-arm64-msvc": "2.6.2", - "@resvg/resvg-js-win32-ia32-msvc": "2.6.2", - "@resvg/resvg-js-win32-x64-msvc": "2.6.2" - } - }, - "node_modules/@resvg/resvg-js-android-arm-eabi": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm-eabi/-/resvg-js-android-arm-eabi-2.6.2.tgz", - "integrity": "sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA==", - "cpu": [ - "arm" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@resvg/resvg-js-android-arm64": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@resvg/resvg-js-android-arm64/-/resvg-js-android-arm64-2.6.2.tgz", - "integrity": "sha512-VcOKezEhm2VqzXpcIJoITuvUS/fcjIw5NA/w3tjzWyzmvoCdd+QXIqy3FBGulWdClvp4g+IfUemigrkLThSjAQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@resvg/resvg-js-darwin-arm64": { - "version": "2.6.2", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@resvg/resvg-js-darwin-x64": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@resvg/resvg-js-darwin-x64/-/resvg-js-darwin-x64-2.6.2.tgz", - "integrity": "sha512-GInyZLjgWDfsVT6+SHxQVRwNzV0AuA1uqGsOAW+0th56J7Nh6bHHKXHBWzUrihxMetcFDmQMAX1tZ1fZDYSRsw==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@resvg/resvg-js-linux-arm-gnueabihf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm-gnueabihf/-/resvg-js-linux-arm-gnueabihf-2.6.2.tgz", - "integrity": "sha512-YIV3u/R9zJbpqTTNwTZM5/ocWetDKGsro0SWp70eGEM9eV2MerWyBRZnQIgzU3YBnSBQ1RcxRZvY/UxwESfZIw==", - "cpu": [ - "arm" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@resvg/resvg-js-linux-arm64-gnu": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-gnu/-/resvg-js-linux-arm64-gnu-2.6.2.tgz", - "integrity": "sha512-zc2BlJSim7YR4FZDQ8OUoJg5holYzdiYMeobb9pJuGDidGL9KZUv7SbiD4E8oZogtYY42UZEap7dqkkYuA91pg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@resvg/resvg-js-linux-arm64-musl": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-arm64-musl/-/resvg-js-linux-arm64-musl-2.6.2.tgz", - "integrity": "sha512-3h3dLPWNgSsD4lQBJPb4f+kvdOSJHa5PjTYVsWHxLUzH4IFTJUAnmuWpw4KqyQ3NA5QCyhw4TWgxk3jRkQxEKg==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@resvg/resvg-js-linux-x64-gnu": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-gnu/-/resvg-js-linux-x64-gnu-2.6.2.tgz", - "integrity": "sha512-IVUe+ckIerA7xMZ50duAZzwf1U7khQe2E0QpUxu5MBJNao5RqC0zwV/Zm965vw6D3gGFUl7j4m+oJjubBVoftw==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@resvg/resvg-js-linux-x64-musl": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@resvg/resvg-js-linux-x64-musl/-/resvg-js-linux-x64-musl-2.6.2.tgz", - "integrity": "sha512-UOf83vqTzoYQO9SZ0fPl2ZIFtNIz/Rr/y+7X8XRX1ZnBYsQ/tTb+cj9TE+KHOdmlTFBxhYzVkP2lRByCzqi4jQ==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@resvg/resvg-js-win32-arm64-msvc": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-arm64-msvc/-/resvg-js-win32-arm64-msvc-2.6.2.tgz", - "integrity": "sha512-7C/RSgCa+7vqZ7qAbItfiaAWhyRSoD4l4BQAbVDqRRsRgY+S+hgS3in0Rxr7IorKUpGE69X48q6/nOAuTJQxeQ==", - "cpu": [ - "arm64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@resvg/resvg-js-win32-ia32-msvc": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-ia32-msvc/-/resvg-js-win32-ia32-msvc-2.6.2.tgz", - "integrity": "sha512-har4aPAlvjnLcil40AC77YDIk6loMawuJwFINEM7n0pZviwMkMvjb2W5ZirsNOZY4aDbo5tLx0wNMREp5Brk+w==", - "cpu": [ - "ia32" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@resvg/resvg-js-win32-x64-msvc": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@resvg/resvg-js-win32-x64-msvc/-/resvg-js-win32-x64-msvc-2.6.2.tgz", - "integrity": "sha512-ZXtYhtUr5SSaBrUDq7DiyjOFJqBVL/dOBN7N/qmi/pO0IgiWW/f/ue3nbvu9joWE5aAKDoIzy/CxsY0suwGosQ==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.27", - "license": "MIT" - }, - "node_modules/@rollup/pluginutils": { - "version": "5.3.0", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils/node_modules/estree-walker": { - "version": "2.0.2", - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", - "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", - "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.57.1", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", - "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", - "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", - "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", - "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", - "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", - "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", - "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", - "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", - "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", - "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", - "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", - "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", - "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", - "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", - "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", - "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", - "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", - "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", - "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", - "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", - "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", - "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@shikijs/core": { - "version": "3.22.0", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.22.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4", - "hast-util-to-html": "^9.0.5" - } - }, - "node_modules/@shikijs/engine-javascript": { - "version": "3.22.0", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.22.0", - "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.4" - } - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "3.22.0", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.22.0", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@shikijs/langs": { - "version": "3.22.0", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.22.0" - } - }, - "node_modules/@shikijs/themes": { - "version": "3.22.0", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.22.0" - } - }, - "node_modules/@shikijs/types": { - "version": "3.22.0", - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "license": "MIT" - }, - "node_modules/@shuding/opentype.js": { - "version": "1.4.0-beta.0", - "license": "MIT", - "dependencies": { - "fflate": "^0.7.3", - "string.prototype.codepointat": "^0.2.1" - }, - "bin": { - "ot": "bin/ot" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/@tailwindcss/node": { - "version": "4.1.18", - "license": "MIT", - "dependencies": { - "@jridgewell/remapping": "^2.3.4", - "enhanced-resolve": "^5.18.3", - "jiti": "^2.6.1", - "lightningcss": "1.30.2", - "magic-string": "^0.30.21", - "source-map-js": "^1.2.1", - "tailwindcss": "4.1.18" - } - }, - "node_modules/@tailwindcss/node/node_modules/jiti": { - "version": "2.6.1", - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/@tailwindcss/node/node_modules/tailwindcss": { - "version": "4.1.18", - "license": "MIT" - }, - "node_modules/@tailwindcss/oxide": { - "version": "4.1.18", - "license": "MIT", - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.18", - "@tailwindcss/oxide-darwin-arm64": "4.1.18", - "@tailwindcss/oxide-darwin-x64": "4.1.18", - "@tailwindcss/oxide-freebsd-x64": "4.1.18", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.18", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.18", - "@tailwindcss/oxide-linux-x64-musl": "4.1.18", - "@tailwindcss/oxide-wasm32-wasi": "4.1.18", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.18" - } - }, - "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz", - "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.18", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz", - "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz", - "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz", - "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz", - "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz", - "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz", - "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz", - "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz", - "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==", - "bundleDependencies": [ - "@napi-rs/wasm-runtime", - "@emnapi/core", - "@emnapi/runtime", - "@tybys/wasm-util", - "@emnapi/wasi-threads", - "tslib" - ], - "cpu": [ - "wasm32" - ], - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1", - "@emnapi/wasi-threads": "^1.1.0", - "@napi-rs/wasm-runtime": "^1.1.0", - "@tybys/wasm-util": "^0.10.1", - "tslib": "^2.4.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz", - "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz", - "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@tailwindcss/vite": { - "version": "4.1.18", - "license": "MIT", - "dependencies": { - "@tailwindcss/node": "4.1.18", - "@tailwindcss/oxide": "4.1.18", - "tailwindcss": "4.1.18" - }, - "peerDependencies": { - "vite": "^5.2.0 || ^6 || ^7" - } - }, - "node_modules/@tailwindcss/vite/node_modules/tailwindcss": { - "version": "4.1.18", - "license": "MIT" - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/debug": { - "version": "4.1.12", - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/esrecurse": { - "version": "4.3.1", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "license": "MIT" - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "license": "MIT" - }, - "node_modules/@types/nlcst": { - "version": "2.0.3", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/node": { - "version": "25.2.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz", - "integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.16.0" - } - }, - "node_modules/@types/react": { - "version": "19.2.11", - "dev": true, - "license": "MIT", - "dependencies": { - "csstype": "^3.2.2" - } - }, - "node_modules/@types/react-dom": { - "version": "19.2.3", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^19.2.0" - } - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.54.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/type-utils": "8.54.0", - "@typescript-eslint/utils": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", - "ignore": "^7.0.5", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.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.54.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.54.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.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", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.54.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.54.0", - "@typescript-eslint/types": "^8.54.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.0.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.54.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.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.54.0", - "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.0.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.54.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/utils": "8.54.0", - "debug": "^4.4.3", - "ts-api-utils": "^2.4.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", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.54.0", - "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.54.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.54.0", - "@typescript-eslint/tsconfig-utils": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", - "debug": "^4.4.3", - "minimatch": "^9.0.5", - "semver": "^7.7.3", - "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.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.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch/node_modules/brace-expansion": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.3", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.54.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.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", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.54.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.54.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "license": "ISC" - }, - "node_modules/@vitejs/plugin-react": { - "version": "4.7.0", - "license": "MIT", - "dependencies": { - "@babel/core": "^7.28.0", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-beta.27", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.17.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "dev": 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/ansi-align": { - "version": "3.0.1", - "license": "ISC", - "dependencies": { - "string-width": "^4.1.0" - } - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "4.2.3", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/string-width/node_modules/emoji-regex": { - "version": "8.0.0", - "license": "MIT" - }, - "node_modules/ansi-align/node_modules/string-width/node_modules/strip-ansi": { - "version": "6.0.1", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/string-width/node_modules/strip-ansi/node_modules/ansi-regex": { - "version": "5.0.1", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-regex": { - "version": "6.2.2", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.3", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "license": "Python-2.0" - }, - "node_modules/aria-query": { - "version": "5.3.2", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "is-array-buffer": "^3.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-includes": { - "version": "3.1.9", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.0", - "es-object-atoms": "^1.1.1", - "get-intrinsic": "^1.3.0", - "is-string": "^1.1.1", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-iterate": { - "version": "2.0.1", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.3", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.1", - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "is-array-buffer": "^3.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/astro": { - "version": "5.17.1", - "license": "MIT", - "dependencies": { - "@astrojs/compiler": "^2.13.0", - "@astrojs/internal-helpers": "0.7.5", - "@astrojs/markdown-remark": "6.3.10", - "@astrojs/telemetry": "3.3.0", - "@capsizecss/unpack": "^4.0.0", - "@oslojs/encoding": "^1.1.0", - "@rollup/pluginutils": "^5.3.0", - "acorn": "^8.15.0", - "aria-query": "^5.3.2", - "axobject-query": "^4.1.0", - "boxen": "8.0.1", - "ci-info": "^4.3.1", - "clsx": "^2.1.1", - "common-ancestor-path": "^1.0.1", - "cookie": "^1.1.1", - "cssesc": "^3.0.0", - "debug": "^4.4.3", - "deterministic-object-hash": "^2.0.2", - "devalue": "^5.6.2", - "diff": "^8.0.3", - "dlv": "^1.1.3", - "dset": "^3.1.4", - "es-module-lexer": "^1.7.0", - "esbuild": "^0.25.0", - "estree-walker": "^3.0.3", - "flattie": "^1.1.1", - "fontace": "~0.4.0", - "github-slugger": "^2.0.0", - "html-escaper": "3.0.3", - "http-cache-semantics": "^4.2.0", - "import-meta-resolve": "^4.2.0", - "js-yaml": "^4.1.1", - "magic-string": "^0.30.21", - "magicast": "^0.5.1", - "mrmime": "^2.0.1", - "neotraverse": "^0.6.18", - "p-limit": "^6.2.0", - "p-queue": "^8.1.1", - "package-manager-detector": "^1.6.0", - "piccolore": "^0.1.3", - "picomatch": "^4.0.3", - "prompts": "^2.4.2", - "rehype": "^13.0.2", - "semver": "^7.7.3", - "shiki": "^3.21.0", - "smol-toml": "^1.6.0", - "svgo": "^4.0.0", - "tinyexec": "^1.0.2", - "tinyglobby": "^0.2.15", - "tsconfck": "^3.1.6", - "ultrahtml": "^1.6.0", - "unifont": "~0.7.3", - "unist-util-visit": "^5.0.0", - "unstorage": "^1.17.4", - "vfile": "^6.0.3", - "vite": "^6.4.1", - "vitefu": "^1.1.1", - "xxhash-wasm": "^1.1.0", - "yargs-parser": "^21.1.1", - "yocto-spinner": "^0.2.3", - "zod": "^3.25.76", - "zod-to-json-schema": "^3.25.1", - "zod-to-ts": "^1.2.0" - }, - "bin": { - "astro": "astro.js" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/astrodotbuild" - }, - "optionalDependencies": { - "sharp": "^0.34.0" - } - }, - "node_modules/astro/node_modules/semver": { - "version": "7.7.3", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/async-function": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.24", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.28.1", - "caniuse-lite": "^1.0.30001766", - "fraction.js": "^5.3.4", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "dev": true, - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axobject-query": { - "version": "4.1.0", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "dev": true, - "license": "MIT" - }, - "node_modules/base-64": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "0.0.8", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.9.19", - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "license": "ISC" - }, - "node_modules/boxen": { - "version": "8.0.1", - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^8.0.0", - "chalk": "^5.3.0", - "cli-boxes": "^3.0.0", - "string-width": "^7.2.0", - "type-fest": "^4.21.0", - "widest-line": "^5.0.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.28.1", - "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.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/camelcase": { - "version": "8.0.0", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/camelize": { - "version": "1.0.1", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001768", - "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/ccount": { - "version": "2.0.1", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chalk": { - "version": "5.6.2", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ci-info": { - "version": "4.4.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "license": "MIT" - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "11.1.0", - "license": "MIT", - "engines": { - "node": ">=16" - } - }, - "node_modules/common-ancestor-path": { - "version": "1.0.1", - "license": "ISC" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "license": "MIT" - }, - "node_modules/cookie": { - "version": "1.1.1", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/cookie-es": { - "version": "1.2.2", - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crossws": { - "version": "0.3.5", - "license": "MIT", - "dependencies": { - "uncrypto": "^0.1.3" - } - }, - "node_modules/css-background-parser": { - "version": "0.1.0", - "license": "MIT" - }, - "node_modules/css-box-shadow": { - "version": "1.0.0-3", - "license": "MIT" - }, - "node_modules/css-color-keywords": { - "version": "1.0.0", - "license": "ISC", - "engines": { - "node": ">=4" - } - }, - "node_modules/css-gradient-parser": { - "version": "0.0.17", - "license": "MIT", - "engines": { - "node": ">=16" - } - }, - "node_modules/css-select": { - "version": "5.2.2", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-to-react-native": { - "version": "3.2.0", - "license": "MIT", - "dependencies": { - "camelize": "^1.0.0", - "css-color-keywords": "^1.0.0", - "postcss-value-parser": "^4.0.2" - } - }, - "node_modules/css-tree": { - "version": "3.1.0", - "license": "MIT", - "dependencies": { - "mdn-data": "2.12.2", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/css-what": { - "version": "6.2.2", - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csso": { - "version": "5.0.5", - "license": "MIT", - "dependencies": { - "css-tree": "~2.2.0" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/css-tree": { - "version": "2.2.1", - "license": "MIT", - "dependencies": { - "mdn-data": "2.0.28", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0", - "npm": ">=7.0.0" - } - }, - "node_modules/csso/node_modules/css-tree/node_modules/mdn-data": { - "version": "2.0.28", - "license": "CC0-1.0" - }, - "node_modules/csstype": { - "version": "3.2.3", - "dev": true, - "license": "MIT" - }, - "node_modules/data-view-buffer": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/data-view-byte-length": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/inspect-js" - } - }, - "node_modules/data-view-byte-offset": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-data-view": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/date-fns": { - "version": "4.1.0", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" - } - }, - "node_modules/debug": { - "version": "4.4.3", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decode-named-character-reference": { - "version": "1.3.0", - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "dev": true, - "license": "MIT" - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/defu": { - "version": "6.1.4", - "license": "MIT" - }, - "node_modules/depd": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/destr": { - "version": "2.0.5", - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.1.2", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/deterministic-object-hash": { - "version": "2.0.2", - "license": "MIT", - "dependencies": { - "base-64": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/devalue": { - "version": "5.6.2", - "license": "MIT" - }, - "node_modules/devlop": { - "version": "1.1.0", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "license": "Apache-2.0" - }, - "node_modules/diff": { - "version": "8.0.3", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "license": "MIT" - }, - "node_modules/doctrine": { - "version": "2.1.0", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/dom-serializer/node_modules/entities": { - "version": "4.5.0", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.2.2", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dset": { - "version": "3.1.4", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.286", - "license": "ISC" - }, - "node_modules/emoji-picker-react": { - "version": "4.18.0", - "license": "MIT", - "dependencies": { - "flairup": "1.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "react": ">=16" - } - }, - "node_modules/emoji-regex": { - "version": "10.6.0", - "license": "MIT" - }, - "node_modules/emoji-regex-xs": { - "version": "2.0.1", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.19.0", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "6.0.1", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/es-abstract": { - "version": "1.24.1", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.2", - "arraybuffer.prototype.slice": "^1.0.4", - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "data-view-buffer": "^1.0.2", - "data-view-byte-length": "^1.0.2", - "data-view-byte-offset": "^1.0.1", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "es-set-tostringtag": "^2.1.0", - "es-to-primitive": "^1.3.0", - "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.3.0", - "get-proto": "^1.0.1", - "get-symbol-description": "^1.1.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "internal-slot": "^1.1.0", - "is-array-buffer": "^3.0.5", - "is-callable": "^1.2.7", - "is-data-view": "^1.0.2", - "is-negative-zero": "^2.0.3", - "is-regex": "^1.2.1", - "is-set": "^2.0.3", - "is-shared-array-buffer": "^1.0.4", - "is-string": "^1.1.1", - "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.1", - "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.4", - "object-keys": "^1.1.1", - "object.assign": "^4.1.7", - "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.4", - "safe-array-concat": "^1.1.3", - "safe-push-apply": "^1.0.0", - "safe-regex-test": "^1.1.0", - "set-proto": "^1.0.0", - "stop-iteration-iterator": "^1.1.0", - "string.prototype.trim": "^1.2.10", - "string.prototype.trimend": "^1.0.9", - "string.prototype.trimstart": "^1.0.8", - "typed-array-buffer": "^1.0.3", - "typed-array-byte-length": "^1.0.3", - "typed-array-byte-offset": "^1.0.4", - "typed-array-length": "^1.0.7", - "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.19" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-iterator-helpers": { - "version": "1.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.1", - "es-errors": "^1.3.0", - "es-set-tostringtag": "^2.1.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.3.0", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "iterator.prototype": "^1.1.5", - "safe-array-concat": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/esbuild": { - "version": "0.25.12", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.12", - "@esbuild/android-arm": "0.25.12", - "@esbuild/android-arm64": "0.25.12", - "@esbuild/android-x64": "0.25.12", - "@esbuild/darwin-arm64": "0.25.12", - "@esbuild/darwin-x64": "0.25.12", - "@esbuild/freebsd-arm64": "0.25.12", - "@esbuild/freebsd-x64": "0.25.12", - "@esbuild/linux-arm": "0.25.12", - "@esbuild/linux-arm64": "0.25.12", - "@esbuild/linux-ia32": "0.25.12", - "@esbuild/linux-loong64": "0.25.12", - "@esbuild/linux-mips64el": "0.25.12", - "@esbuild/linux-ppc64": "0.25.12", - "@esbuild/linux-riscv64": "0.25.12", - "@esbuild/linux-s390x": "0.25.12", - "@esbuild/linux-x64": "0.25.12", - "@esbuild/netbsd-arm64": "0.25.12", - "@esbuild/netbsd-x64": "0.25.12", - "@esbuild/openbsd-arm64": "0.25.12", - "@esbuild/openbsd-x64": "0.25.12", - "@esbuild/openharmony-arm64": "0.25.12", - "@esbuild/sunos-x64": "0.25.12", - "@esbuild/win32-arm64": "0.25.12", - "@esbuild/win32-ia32": "0.25.12", - "@esbuild/win32-x64": "0.25.12" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "10.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.2", - "@eslint/config-array": "^0.23.0", - "@eslint/config-helpers": "^0.5.2", - "@eslint/core": "^1.1.0", - "@eslint/plugin-kit": "^0.6.0", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "ajv": "^6.12.4", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^9.1.0", - "eslint-visitor-keys": "^5.0.0", - "espree": "^11.1.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.1.1", - "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-config-prettier": { - "version": "10.1.8", - "dev": true, - "license": "MIT", - "bin": { - "eslint-config-prettier": "bin/cli.js" - }, - "funding": { - "url": "https://opencollective.com/eslint-config-prettier" - }, - "peerDependencies": { - "eslint": ">=7.0.0" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "5.5.5", - "dev": true, - "license": "MIT", - "dependencies": { - "prettier-linter-helpers": "^1.0.1", - "synckit": "^0.11.12" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint-plugin-prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.37.5", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.3", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.2.1", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.9", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.1", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.12", - "string.prototype.repeat": "^1.0.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "7.0.1", - "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" - } - }, - "node_modules/eslint-plugin-react-refresh": { - "version": "0.5.0", - "dev": true, - "license": "MIT", - "peerDependencies": { - "eslint": ">=9" - } - }, - "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-scope": { - "version": "9.1.0", - "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.0", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ignore": { - "version": "5.3.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/espree": { - "version": "11.1.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^5.0.0" - }, - "engines": { - "node": "^20.19.0 || ^22.13.0 || >=24" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.7.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.4", - "license": "MIT" - }, - "node_modules/extend": { - "version": "3.0.2", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "dev": true, - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.20.1", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fflate": { - "version": "0.7.4", - "license": "MIT" - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "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/flairup": { - "version": "1.0.0", - "license": "MIT" - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "dev": true, - "license": "ISC" - }, - "node_modules/flattie": { - "version": "1.1.1", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/fontace": { - "version": "0.4.1", - "license": "MIT", - "dependencies": { - "fontkitten": "^1.0.2" - } - }, - "node_modules/fontkitten": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "tiny-inflate": "^1.0.3" - }, - "engines": { - "node": ">=20" - } - }, - "node_modules/for-each": { - "version": "0.3.5", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/fraction.js": { - "version": "5.3.4", - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.8", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/generator-function": { - "version": "2.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.4.0", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-symbol-description": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/github-slugger": { - "version": "2.0.0", - "license": "ISC" - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "17.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.2.1", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "license": "ISC" - }, - "node_modules/h3": { - "version": "1.15.5", - "license": "MIT", - "dependencies": { - "cookie-es": "^1.2.2", - "crossws": "^0.3.5", - "defu": "^6.1.4", - "destr": "^2.0.5", - "iron-webcrypto": "^1.2.1", - "node-mock-http": "^1.0.4", - "radix3": "^1.1.2", - "ufo": "^1.6.3", - "uncrypto": "^0.1.3" - } - }, - "node_modules/has-bigints": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.2.0", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hast-util-from-html": { - "version": "2.0.3", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "devlop": "^1.1.0", - "hast-util-from-parse5": "^8.0.0", - "parse5": "^7.0.0", - "vfile": "^6.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5": { - "version": "8.0.3", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "hastscript": "^9.0.0", - "property-information": "^7.0.0", - "vfile": "^6.0.0", - "vfile-location": "^5.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-is-element": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw": { - "version": "9.1.0", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "@ungap/structured-clone": "^1.0.0", - "hast-util-from-parse5": "^8.0.0", - "hast-util-to-parse5": "^8.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "parse5": "^7.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-html": { - "version": "9.0.5", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-whitespace": "^3.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "stringify-entities": "^4.0.0", - "zwitch": "^2.0.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-parse5": { - "version": "8.0.1", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-text": { - "version": "4.0.2", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "hast-util-is-element": "^3.0.0", - "unist-util-find-after": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "9.0.1", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hermes-estree": { - "version": "0.25.1", - "dev": true, - "license": "MIT" - }, - "node_modules/hermes-parser": { - "version": "0.25.1", - "dev": true, - "license": "MIT", - "dependencies": { - "hermes-estree": "0.25.1" - } - }, - "node_modules/hex-rgb": { - "version": "4.3.0", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/html-escaper": { - "version": "3.0.3", - "license": "MIT" - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "license": "BSD-2-Clause" - }, - "node_modules/http-errors": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "depd": "~2.0.0", - "inherits": "~2.0.4", - "setprototypeof": "~1.2.0", - "statuses": "~2.0.2", - "toidentifier": "~1.0.1" - }, - "engines": { - "node": ">= 0.8" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/ignore": { - "version": "7.0.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-meta-resolve": { - "version": "4.2.0", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "license": "ISC" - }, - "node_modules/internal-slot": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "hasown": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/iron-webcrypto": { - "version": "1.2.1", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/brc-dd" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.5", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-async-function": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "async-function": "^1.0.0", - "call-bound": "^1.0.3", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-boolean-object": { - "version": "1.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-view": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "is-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "3.0.0", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.4", - "generator-function": "^2.0.0", - "get-proto": "^1.0.1", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-map": { - "version": "2.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-regex": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.3", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-string": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-symbols": "^1.1.0", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "dev": true, - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-intrinsic": "^1.2.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-wsl": { - "version": "3.1.0", - "license": "MIT", - "dependencies": { - "is-inside-container": "^1.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "dev": true, - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "dev": true, - "license": "ISC" - }, - "node_modules/iterator.prototype": { - "version": "1.1.5", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "get-proto": "^1.0.0", - "has-symbols": "^1.1.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.1", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "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.30.2", - "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.30.2", - "lightningcss-darwin-arm64": "1.30.2", - "lightningcss-darwin-x64": "1.30.2", - "lightningcss-freebsd-x64": "1.30.2", - "lightningcss-linux-arm-gnueabihf": "1.30.2", - "lightningcss-linux-arm64-gnu": "1.30.2", - "lightningcss-linux-arm64-musl": "1.30.2", - "lightningcss-linux-x64-gnu": "1.30.2", - "lightningcss-linux-x64-musl": "1.30.2", - "lightningcss-win32-arm64-msvc": "1.30.2", - "lightningcss-win32-x64-msvc": "1.30.2" - } - }, - "node_modules/lightningcss-android-arm64": { - "version": "1.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz", - "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==", - "cpu": [ - "arm64" - ], - "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.30.2", - "cpu": [ - "arm64" - ], - "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.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz", - "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==", - "cpu": [ - "x64" - ], - "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.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz", - "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==", - "cpu": [ - "x64" - ], - "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.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz", - "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==", - "cpu": [ - "arm" - ], - "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.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz", - "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==", - "cpu": [ - "arm64" - ], - "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.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz", - "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==", - "cpu": [ - "arm64" - ], - "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.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz", - "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==", - "cpu": [ - "x64" - ], - "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.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz", - "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==", - "cpu": [ - "x64" - ], - "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.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz", - "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==", - "cpu": [ - "arm64" - ], - "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.30.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz", - "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==", - "cpu": [ - "x64" - ], - "license": "MPL-2.0", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/linebreak": { - "version": "1.1.0", - "license": "MIT", - "dependencies": { - "base64-js": "0.0.8", - "unicode-trie": "^2.0.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "6.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "11.2.5", - "license": "BlueOak-1.0.0", - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/lucide-react": { - "version": "0.563.0", - "license": "ISC", - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/magic-string": { - "version": "0.30.21", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" - } - }, - "node_modules/magicast": { - "version": "0.5.2", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", - "source-map-js": "^1.2.1" - } - }, - "node_modules/markdown-table": { - "version": "3.0.4", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mdast-util-definitions": { - "version": "6.0.0", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "5.0.0", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.2", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.1", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdn-data": { - "version": "2.12.2", - "license": "CC0-1.0" - }, - "node_modules/merge2": { - "version": "1.4.1", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromark": { - "version": "4.0.2", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.2", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/minimatch": { - "version": "10.1.2", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/brace-expansion": "^5.0.1" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/mrmime": { - "version": "2.0.1", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "license": "MIT" - }, - "node_modules/mz": { - "version": "2.7.0", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "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/nanostores": { - "version": "1.1.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "engines": { - "node": "^20.0.0 || >=22.0.0" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "dev": true, - "license": "MIT" - }, - "node_modules/neotraverse": { - "version": "0.6.18", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/nlcst-to-string": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/node-fetch-native": { - "version": "1.6.7", - "license": "MIT" - }, - "node_modules/node-mock-http": { - "version": "1.0.4", - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.27", - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.7", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0", - "has-symbols": "^1.1.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.9", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ofetch": { - "version": "1.5.1", - "license": "MIT", - "dependencies": { - "destr": "^2.0.5", - "node-fetch-native": "^1.6.7", - "ufo": "^1.6.1" - } - }, - "node_modules/ohash": { - "version": "2.0.11", - "license": "MIT" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/oniguruma-parser": { - "version": "0.12.1", - "license": "MIT" - }, - "node_modules/oniguruma-to-es": { - "version": "4.3.4", - "license": "MIT", - "dependencies": { - "oniguruma-parser": "^0.12.1", - "regex": "^6.0.1", - "regex-recursion": "^6.0.2" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "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/own-keys": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.6", - "object-keys": "^1.1.1", - "safe-push-apply": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/p-limit": { - "version": "6.2.0", - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "3.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate/node_modules/p-limit/node_modules/yocto-queue": { - "version": "0.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-queue": { - "version": "8.1.1", - "license": "MIT", - "dependencies": { - "eventemitter3": "^5.0.1", - "p-timeout": "^6.1.2" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-timeout": { - "version": "6.1.4", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-manager-detector": { - "version": "1.6.0", - "license": "MIT" - }, - "node_modules/pako": { - "version": "0.2.9", - "license": "MIT" - }, - "node_modules/parse-css-color": { - "version": "0.2.1", - "license": "MIT", - "dependencies": { - "color-name": "^1.1.4", - "hex-rgb": "^4.1.0" - } - }, - "node_modules/parse-latin": { - "version": "7.0.0", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "@types/unist": "^3.0.0", - "nlcst-to-string": "^4.0.0", - "unist-util-modify-children": "^4.0.0", - "unist-util-visit-children": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse5": { - "version": "7.3.0", - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "license": "MIT" - }, - "node_modules/piccolore": { - "version": "0.1.3", - "license": "ISC" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.3", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "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.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-import/node_modules/resolve": { - "version": "1.22.11", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/postcss-js": { - "version": "4.1.0", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "4.0.2", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "license": "MIT" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.8.1", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/prismjs": { - "version": "1.30.0", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "dev": true, - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/property-information": { - "version": "7.1.0", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/radix3": { - "version": "1.1.2", - "license": "MIT" - }, - "node_modules/range-parser": { - "version": "1.2.1", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/react": { - "version": "19.2.4", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.2.4", - "license": "MIT", - "dependencies": { - "scheduler": "^0.27.0" - }, - "peerDependencies": { - "react": "^19.2.4" - } - }, - "node_modules/react-icons": { - "version": "5.5.0", - "dev": true, - "license": "MIT", - "peerDependencies": { - "react": "*" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "dev": true, - "license": "MIT" - }, - "node_modules/react-refresh": { - "version": "0.17.0", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-router": { - "version": "7.13.0", - "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.13.0", - "license": "MIT", - "dependencies": { - "react-router": "7.13.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.10", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.9", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.1", - "which-builtin-type": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regex": { - "version": "6.1.0", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-recursion": { - "version": "6.0.2", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-utilities": { - "version": "2.3.0", - "license": "MIT" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.4", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-errors": "^1.3.0", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/rehype": { - "version": "13.0.2", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "rehype-parse": "^9.0.0", - "rehype-stringify": "^10.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-parse": { - "version": "9.0.1", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-from-html": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-raw": { - "version": "7.0.0", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-raw": "^9.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-stringify": { - "version": "10.0.1", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-to-html": "^9.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-smartypants": { - "version": "3.0.2", - "license": "MIT", - "dependencies": { - "retext": "^9.0.0", - "retext-smartypants": "^6.0.0", - "unified": "^11.0.4", - "unist-util-visit": "^5.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/resolve": { - "version": "2.0.0-next.5", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/retext": { - "version": "9.0.0", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "retext-latin": "^4.0.0", - "retext-stringify": "^4.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-latin": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "parse-latin": "^7.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-smartypants": { - "version": "6.2.0", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "nlcst-to-string": "^4.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-stringify": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "nlcst-to-string": "^4.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "4.57.1", - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.57.1", - "@rollup/rollup-android-arm64": "4.57.1", - "@rollup/rollup-darwin-arm64": "4.57.1", - "@rollup/rollup-darwin-x64": "4.57.1", - "@rollup/rollup-freebsd-arm64": "4.57.1", - "@rollup/rollup-freebsd-x64": "4.57.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", - "@rollup/rollup-linux-arm-musleabihf": "4.57.1", - "@rollup/rollup-linux-arm64-gnu": "4.57.1", - "@rollup/rollup-linux-arm64-musl": "4.57.1", - "@rollup/rollup-linux-loong64-gnu": "4.57.1", - "@rollup/rollup-linux-loong64-musl": "4.57.1", - "@rollup/rollup-linux-ppc64-gnu": "4.57.1", - "@rollup/rollup-linux-ppc64-musl": "4.57.1", - "@rollup/rollup-linux-riscv64-gnu": "4.57.1", - "@rollup/rollup-linux-riscv64-musl": "4.57.1", - "@rollup/rollup-linux-s390x-gnu": "4.57.1", - "@rollup/rollup-linux-x64-gnu": "4.57.1", - "@rollup/rollup-linux-x64-musl": "4.57.1", - "@rollup/rollup-openbsd-x64": "4.57.1", - "@rollup/rollup-openharmony-arm64": "4.57.1", - "@rollup/rollup-win32-arm64-msvc": "4.57.1", - "@rollup/rollup-win32-ia32-msvc": "4.57.1", - "@rollup/rollup-win32-x64-gnu": "4.57.1", - "@rollup/rollup-win32-x64-msvc": "4.57.1", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.3", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-push-apply": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/satori": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/satori/-/satori-0.19.2.tgz", - "integrity": "sha512-71plFHWcq6WJBM5sf/n0eHOmTBiKLUB/G8du7SmLTTLHKEKrV3TPHGKcEVIoyjnbhnjvu9HhLyF9MATB/zzL7g==", - "license": "MPL-2.0", - "dependencies": { - "@shuding/opentype.js": "1.4.0-beta.0", - "css-background-parser": "^0.1.0", - "css-box-shadow": "1.0.0-3", - "css-gradient-parser": "^0.0.17", - "css-to-react-native": "^3.0.0", - "emoji-regex-xs": "^2.0.1", - "escape-html": "^1.0.3", - "linebreak": "^1.1.0", - "parse-css-color": "^0.2.1", - "postcss-value-parser": "^4.2.0", - "yoga-layout": "^3.2.1" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/sax": { - "version": "1.4.4", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=11.0.0" - } - }, - "node_modules/scheduler": { - "version": "0.27.0", - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/send": { - "version": "1.2.1", - "license": "MIT", - "dependencies": { - "debug": "^4.4.3", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.1", - "mime-types": "^3.0.2", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.2" - }, - "engines": { - "node": ">= 18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/server-destroy": { - "version": "1.0.1", - "license": "ISC" - }, - "node_modules/set-cookie-parser": { - "version": "2.7.2", - "license": "MIT" - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-proto": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "license": "ISC" - }, - "node_modules/sharp": { - "version": "0.34.5", - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "@img/colour": "^1.0.0", - "detect-libc": "^2.1.2", - "semver": "^7.7.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" - } - }, - "node_modules/sharp/node_modules/semver": { - "version": "7.7.3", - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shiki": { - "version": "3.22.0", - "license": "MIT", - "dependencies": { - "@shikijs/core": "3.22.0", - "@shikijs/engine-javascript": "3.22.0", - "@shikijs/engine-oniguruma": "3.22.0", - "@shikijs/langs": "3.22.0", - "@shikijs/themes": "3.22.0", - "@shikijs/types": "3.22.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "license": "MIT" - }, - "node_modules/smol-toml": { - "version": "1.6.0", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 18" - }, - "funding": { - "url": "https://github.com/sponsors/cyyynthia" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/stop-iteration-iterator": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "internal-slot": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string-width": { - "version": "7.2.0", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string.prototype.codepointat": { - "version": "0.2.1", - "license": "MIT" - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.12", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.6", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "regexp.prototype.flags": "^1.5.3", - "set-function-name": "^2.0.2", - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.repeat": { - "version": "1.0.0", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.10", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.8", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.2", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/sucrase": { - "version": "3.35.1", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "tinyglobby": "^0.2.11", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svgo": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "commander": "^11.1.0", - "css-select": "^5.1.0", - "css-tree": "^3.0.1", - "css-what": "^6.1.0", - "csso": "^5.0.5", - "picocolors": "^1.1.1", - "sax": "^1.4.1" - }, - "bin": { - "svgo": "bin/svgo.js" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/svgo" - } - }, - "node_modules/synckit": { - "version": "0.11.12", - "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.9" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" - } - }, - "node_modules/tailwind-merge": { - "version": "3.4.0", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/dcastil" - } - }, - "node_modules/tailwindcss": { - "version": "3.4.19", - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.7", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tailwindcss/node_modules/chokidar": { - "version": "3.6.0", - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/tailwindcss/node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/tailwindcss/node_modules/jiti": { - "version": "1.21.7", - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/tailwindcss/node_modules/picomatch": { - "version": "2.3.1", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/tailwindcss/node_modules/readdirp": { - "version": "3.6.0", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/tailwindcss/node_modules/resolve": { - "version": "1.22.11", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.1", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tapable": { - "version": "2.3.0", - "license": "MIT", - "engines": { - "node": ">=6" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tiny-inflate": { - "version": "1.0.3", - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "1.0.2", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ts-api-utils": { - "version": "2.4.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "license": "Apache-2.0" - }, - "node_modules/tsconfck": { - "version": "3.1.6", - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "license": "0BSD", - "optional": true - }, - "node_modules/type-check": { - "version": "0.4.0", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "4.41.0", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "es-errors": "^1.3.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.14" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.4", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "for-each": "^0.3.3", - "gopd": "^1.2.0", - "has-proto": "^1.2.0", - "is-typed-array": "^1.1.15", - "reflect.getprototypeof": "^1.0.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.7", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.54.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.54.0", - "@typescript-eslint/parser": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/utils": "8.54.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", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/ufo": { - "version": "1.6.3", - "license": "MIT" - }, - "node_modules/ultrahtml": { - "version": "1.6.0", - "license": "MIT" - }, - "node_modules/unbox-primitive": { - "version": "1.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "has-bigints": "^1.0.2", - "has-symbols": "^1.1.0", - "which-boxed-primitive": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/uncrypto": { - "version": "0.1.3", - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", - "dev": true, - "license": "MIT" - }, - "node_modules/unicode-trie": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "pako": "^0.2.5", - "tiny-inflate": "^1.0.0" - } - }, - "node_modules/unified": { - "version": "11.0.5", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unifont": { - "version": "0.7.3", - "license": "MIT", - "dependencies": { - "css-tree": "^3.1.0", - "ofetch": "^1.5.1", - "ohash": "^2.0.11" - } - }, - "node_modules/unist-util-find-after": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.1", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-modify-children": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "array-iterate": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove-position": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.1.0", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-children": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.2", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unstorage": { - "version": "1.17.4", - "license": "MIT", - "dependencies": { - "anymatch": "^3.1.3", - "chokidar": "^5.0.0", - "destr": "^2.0.5", - "h3": "^1.15.5", - "lru-cache": "^11.2.0", - "node-fetch-native": "^1.6.7", - "ofetch": "^1.5.1", - "ufo": "^1.6.3" - }, - "peerDependencies": { - "@azure/app-configuration": "^1.8.0", - "@azure/cosmos": "^4.2.0", - "@azure/data-tables": "^13.3.0", - "@azure/identity": "^4.6.0", - "@azure/keyvault-secrets": "^4.9.0", - "@azure/storage-blob": "^12.26.0", - "@capacitor/preferences": "^6 || ^7 || ^8", - "@deno/kv": ">=0.9.0", - "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", - "@planetscale/database": "^1.19.0", - "@upstash/redis": "^1.34.3", - "@vercel/blob": ">=0.27.1", - "@vercel/functions": "^2.2.12 || ^3.0.0", - "@vercel/kv": "^1 || ^2 || ^3", - "aws4fetch": "^1.0.20", - "db0": ">=0.2.1", - "idb-keyval": "^6.2.1", - "ioredis": "^5.4.2", - "uploadthing": "^7.4.4" - }, - "peerDependenciesMeta": { - "@azure/app-configuration": { - "optional": true - }, - "@azure/cosmos": { - "optional": true - }, - "@azure/data-tables": { - "optional": true - }, - "@azure/identity": { - "optional": true - }, - "@azure/keyvault-secrets": { - "optional": true - }, - "@azure/storage-blob": { - "optional": true - }, - "@capacitor/preferences": { - "optional": true - }, - "@deno/kv": { - "optional": true - }, - "@netlify/blobs": { - "optional": true - }, - "@planetscale/database": { - "optional": true - }, - "@upstash/redis": { - "optional": true - }, - "@vercel/blob": { - "optional": true - }, - "@vercel/functions": { - "optional": true - }, - "@vercel/kv": { - "optional": true - }, - "aws4fetch": { - "optional": true - }, - "db0": { - "optional": true - }, - "idb-keyval": { - "optional": true - }, - "ioredis": { - "optional": true - }, - "uploadthing": { - "optional": true - } - } - }, - "node_modules/unstorage/node_modules/chokidar": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "readdirp": "^5.0.0" - }, - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/unstorage/node_modules/chokidar/node_modules/readdirp": { - "version": "5.0.0", - "license": "MIT", - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.2.3", - "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", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "license": "MIT" - }, - "node_modules/vfile": { - "version": "6.0.3", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location": { - "version": "5.0.3", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vite": { - "version": "6.4.1", - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "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/vitefu": { - "version": "1.1.1", - "license": "MIT", - "workspaces": [ - "tests/deps/*", - "tests/projects/*", - "tests/projects/workspace/packages/*" - ], - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/web-namespaces": { - "version": "2.0.1", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/which": { - "version": "2.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-bigint": "^1.1.0", - "is-boolean-object": "^1.2.1", - "is-number-object": "^1.1.1", - "is-string": "^1.1.1", - "is-symbol": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.2.1", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "function.prototype.name": "^1.1.6", - "has-tostringtag": "^1.0.2", - "is-async-function": "^2.0.0", - "is-date-object": "^1.1.0", - "is-finalizationregistry": "^1.1.0", - "is-generator-function": "^1.0.10", - "is-regex": "^1.2.1", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.1.0", - "which-collection": "^1.0.2", - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "is-map": "^2.0.3", - "is-set": "^2.0.3", - "is-weakmap": "^2.0.2", - "is-weakset": "^2.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-pm-runs": { - "version": "1.1.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.20", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/widest-line": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "9.0.2", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/xxhash-wasm": { - "version": "1.1.0", - "license": "MIT" - }, - "node_modules/yallist": { - "version": "3.1.1", - "license": "ISC" - }, - "node_modules/yaml": { - "version": "2.8.2", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "1.2.2", - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yocto-spinner": { - "version": "0.2.3", - "license": "MIT", - "dependencies": { - "yoctocolors": "^2.1.1" - }, - "engines": { - "node": ">=18.19" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors": { - "version": "2.1.2", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoga-layout": { - "version": "3.2.1", - "license": "MIT" - }, - "node_modules/zod": { - "version": "3.25.76", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-to-json-schema": { - "version": "3.25.1", - "license": "ISC", - "peerDependencies": { - "zod": "^3.25 || ^4" - } - }, - "node_modules/zod-to-ts": { - "version": "1.2.0", - "peerDependencies": { - "typescript": "^4.9.4 || ^5.0.2", - "zod": "^3" - } - }, - "node_modules/zod-validation-error": { - "version": "4.0.2", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "zod": "^3.25.0 || ^4.0.0" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/web/package.json b/web/package.json index ea405ab..b239edd 100644 --- a/web/package.json +++ b/web/package.json @@ -10,13 +10,13 @@ "lint": "eslint 'src/**/*.{ts,tsx,js,jsx}' --fix" }, "dependencies": { - "@astrojs/node": "^9.5.2", - "@astrojs/react": "^4.4.2", + "@astrojs/node": "^10.0.3", + "@astrojs/react": "^5.0.1", "@astrojs/tailwind": "^6.0.2", "@nanostores/react": "^1.0.0", "@resvg/resvg-js": "^2.6.2", "@tailwindcss/vite": "^4.1.18", - "astro": "^5.17.1", + "astro": "^6.0.8", "autoprefixer": "^10.4.24", "clsx": "^2.1.1", "date-fns": "^4.1.0", @@ -26,7 +26,6 @@ "postcss": "^8.5.6", "react": "^19.2.4", "react-dom": "^19.2.4", - "react-router-dom": "^7.13.0", "satori": "^0.19.2", "tailwind-merge": "^3.4.0", "tailwindcss": "^3.4.19" diff --git a/web/src/App.tsx b/web/src/App.tsx deleted file mode 100644 index 4ea4e1e..0000000 --- a/web/src/App.tsx +++ /dev/null @@ -1,257 +0,0 @@ -import React from "react"; -import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; -import { initAuth, $user } from "./store/auth"; -import { loadPreferences } from "./store/preferences"; -import { useStore } from "@nanostores/react"; - -import AppLayout from "./layouts/AppLayout"; -import Feed from "./views/core/Feed"; -import Login from "./views/auth/Login"; -import Notifications from "./views/core/Notifications"; -import Collections from "./views/collections/Collections"; -import Settings from "./views/core/Settings"; -import NewAnnotationPage from "./views/core/New"; -import MasonryFeed from "./components/feed/MasonryFeed"; -import { - ProfileWrapper, - SelfProfileWrapper, - CollectionDetailWrapper, - AnnotationDetailWrapper, - UserUrlWrapper, - UrlWrapper, -} from "./routes/wrappers"; -import About from "./views/About"; -import AdminModeration from "./views/core/AdminModeration"; -import Search from "./views/core/Search"; -import Discover from "./views/core/Discover"; - -function RootRoute() { - const user = useStore($user); - - if (user) { - return ; - } - - return ; -} - -export default function App() { - React.useEffect(() => { - initAuth(); - loadPreferences(); - }, []); - - return ( - - - } /> - } /> - } /> - Redirecting...
} /> - - - - - } - /> - } /> - - - - - } - /> - - - - - } - /> - - - - - } - /> - - - - } - /> - - - - } - /> - - - - - } - /> - - - - } - /> - - - - } - /> - - - - - } - /> - - - - } - /> - - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - } - /> - - - - - } - /> - - - - - } - /> - - - - } - /> - - } /> - - - ); -} diff --git a/web/src/components/common/Card.tsx b/web/src/components/common/Card.tsx index 0111eef..8a693d7 100644 --- a/web/src/components/common/Card.tsx +++ b/web/src/components/common/Card.tsx @@ -43,7 +43,7 @@ import type { ContentLabel, LabelVisibility, } from "../../types"; -import { Link } from "react-router-dom"; + import { Avatar } from "../ui"; import CollectionIcon from "./CollectionIcon"; import ProfileHoverCard from "./ProfileHoverCard"; @@ -318,9 +318,20 @@ export default function Card({ : null; const decodeHTMLEntities = (text: string) => { - const textarea = document.createElement("textarea"); - textarea.innerHTML = text; - return textarea.value; + const entities: Record = { + "&": "&", + "<": "<", + ">": ">", + """: '"', + "'": "'", + "'": "'", + "/": "/", + " ": " ", + }; + return text.replace( + /&(?:amp|lt|gt|quot|nbsp|#39|#x27|#x2F);/g, + (match) => entities[match] || match, + ); }; const displayTitle = decodeHTMLEntities( @@ -339,8 +350,8 @@ export default function Card({ {item.addedBy && item.addedBy.did !== item.author?.did ? ( <> - {item.addedBy.displayName || `@${item.addedBy.handle}`} - + added to @@ -370,30 +381,30 @@ export default function Card({ {index > 0 && index === item.context!.length - 1 && ( and )} - {col.name} - + )) ) : ( - {item.collection!.name} - + )} )}
- +
- +
- {item.author?.displayName || item.author?.handle} - + @{item.author?.handle} @@ -652,15 +663,15 @@ export default function Card({ {item.tags && item.tags.length > 0 && (
{item.tags.map((tag) => ( - e.stopPropagation()} > {tag} - + ))}
)} @@ -681,15 +692,15 @@ export default function Card({ {type === "annotation" && ( - {(item.replyCount || 0) > 0 && ( {item.replyCount} )} - + )} {user && ( diff --git a/web/src/components/common/ProfileHoverCard.tsx b/web/src/components/common/ProfileHoverCard.tsx index f075802..c127228 100644 --- a/web/src/components/common/ProfileHoverCard.tsx +++ b/web/src/components/common/ProfileHoverCard.tsx @@ -1,5 +1,4 @@ import React, { useState, useEffect, useRef } from "react"; -import { Link } from "react-router-dom"; import Avatar from "../ui/Avatar"; import RichText from "./RichText"; import { getProfile } from "../../api/client"; @@ -114,8 +113,8 @@ export default function ProfileHoverCard({
) : profile ? (
-
- + {profile.description && (

@@ -140,12 +139,12 @@ export default function ProfileHoverCard({

)} - View Profile - +
) : (

diff --git a/web/src/components/common/RichText.tsx b/web/src/components/common/RichText.tsx index f53ca2c..1f9468d 100644 --- a/web/src/components/common/RichText.tsx +++ b/web/src/components/common/RichText.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { Link } from "react-router-dom"; import ExternalLinkModal from "../modals/ExternalLinkModal"; import { useStore } from "@nanostores/react"; import { $preferences } from "../../store/preferences"; @@ -138,14 +137,14 @@ export default function RichText({ text, className }: RichTextProps) { } finalParts.push( - e.stopPropagation()} > @{handle} - , + , ); lastMentionIndex = startIndex + fullMatch.length; diff --git a/web/src/components/modals/SignUpModal.tsx b/web/src/components/modals/SignUpModal.tsx index 135bb48..4f91331 100644 --- a/web/src/components/modals/SignUpModal.tsx +++ b/web/src/components/modals/SignUpModal.tsx @@ -195,6 +195,9 @@ export default function SignUpModal({ onClose }: SignUpModalProps) { try { const result = await startSignup(serviceUrl); if (result.authorizationUrl) { + const url = new URL(result.authorizationUrl); + if (url.protocol !== "https:") + throw new Error("Invalid authorization URL"); window.location.href = result.authorizationUrl; } } catch (err) { diff --git a/web/src/components/navigation/MobileNav.tsx b/web/src/components/navigation/MobileNav.tsx index 6610589..c2a6e41 100644 --- a/web/src/components/navigation/MobileNav.tsx +++ b/web/src/components/navigation/MobileNav.tsx @@ -15,22 +15,43 @@ import { X, } from "lucide-react"; import React, { useEffect, useState } from "react"; -import { Link, useLocation } from "react-router-dom"; import { getUnreadNotificationCount } from "../../api/client"; import { $user, logout } from "../../store/auth"; +import type { UserProfile } from "../../types"; import { AppleIcon } from "../common/Icons"; -export default function MobileNav() { - const user = useStore($user); - const location = useLocation(); +interface MobileNavProps { + initialUser?: UserProfile | null; + currentPath?: string; +} + +export default function MobileNav({ + initialUser, + currentPath: initialPath, +}: MobileNavProps) { + const storeUser = useStore($user); + const user = storeUser || initialUser || null; + const [currentPath, setCurrentPath] = useState(initialPath || "/"); const [isMenuOpen, setIsMenuOpen] = useState(false); const [unreadCount, setUnreadCount] = useState(0); const isAuthenticated = !!user; + useEffect(() => { + if (initialUser && !storeUser) { + $user.set(initialUser); + } + }, [initialUser, storeUser]); + + useEffect(() => { + const handler = () => setCurrentPath(window.location.pathname); + document.addEventListener("astro:page-load", handler); + return () => document.removeEventListener("astro:page-load", handler); + }, []); + const isActive = (path: string) => { - if (path === "/") return location.pathname === "/"; - return location.pathname.startsWith(path); + if (path === "/") return currentPath === "/"; + return currentPath.startsWith(path); }; useEffect(() => { @@ -57,8 +78,8 @@ export default function MobileNav() {

{isAuthenticated && user ? ( <> - @@ -81,54 +102,54 @@ export default function MobileNav() { @{user.handle}
- +
- Annotations - + - Highlights - + - Bookmarks - + - Collections - + - Settings - +
@@ -158,30 +179,30 @@ export default function MobileNav() { ) : ( <> - Sign In - - + Collections - - + Settings - +
@@ -201,64 +222,80 @@ export default function MobileNav() {
)} -
); } const handleSuccess = () => { - navigate("/home"); + window.location.href = "/home"; }; return ( @@ -97,7 +102,7 @@ export default function NewAnnotationPage() { } selector={initialSelector} onSuccess={handleSuccess} - onCancel={() => navigate(-1)} + onCancel={() => window.history.back()} />
diff --git a/web/src/views/core/Notifications.tsx b/web/src/views/core/Notifications.tsx index 038c8c8..78bc749 100644 --- a/web/src/views/core/Notifications.tsx +++ b/web/src/views/core/Notifications.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from "react"; -import { Link } from "react-router-dom"; + import { getNotifications, markNotificationsRead } from "../../api/client"; import type { NotificationItem, AnnotationItem } from "../../types"; import { @@ -192,13 +192,13 @@ function SubjectPreview({ {parentUri && (

in reply to{" "} - e.stopPropagation()} > {parentIsReply ? "a reply" : "an annotation"} - +

)} @@ -208,12 +208,12 @@ function SubjectPreview({ if (!preview) return null; return ( - {preview} - + ); } @@ -300,24 +300,24 @@ export default function Notifications() {
- + - +
- {n.actor.displayName || `@${n.actor.handle}`} - {" "} + {" "} {n.type !== "follow" && n.subjectUri ? ( - {verb} - + ) : ( verb )} diff --git a/web/src/views/core/Search.tsx b/web/src/views/core/Search.tsx index f262bff..b0f5cb3 100644 --- a/web/src/views/core/Search.tsx +++ b/web/src/views/core/Search.tsx @@ -1,5 +1,4 @@ import React, { useState, useEffect, useCallback, useRef } from "react"; -import { useSearchParams } from "react-router-dom"; import { Search as SearchIcon, Loader2, @@ -18,9 +17,11 @@ import LayoutToggle from "../../components/ui/LayoutToggle"; import { $user } from "../../store/auth"; import { $feedLayout } from "../../store/feedLayout"; -export default function Search() { - const [searchParams, setSearchParams] = useSearchParams(); - const initialQuery = searchParams.get("q") || ""; +interface SearchProps { + initialQuery?: string; +} + +export default function Search({ initialQuery = "" }: SearchProps) { const user = useStore($user); const layout = useStore($feedLayout); @@ -85,7 +86,9 @@ export default function Search() { const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (query.trim()) { - setSearchParams({ q: query.trim() }); + const url = new URL(window.location.href); + url.searchParams.set("q", query.trim()); + window.history.replaceState({}, "", url.toString()); doSearch(query.trim()); } }; @@ -130,7 +133,7 @@ export default function Search() { {initialQuery && ( -
+
{filters.map((f) => { const isActive = diff --git a/web/src/views/core/Settings.tsx b/web/src/views/core/Settings.tsx index 08ea681..b4f78d9 100644 --- a/web/src/views/core/Settings.tsx +++ b/web/src/views/core/Settings.tsx @@ -59,7 +59,6 @@ import { Switch, } from "../../components/ui"; import { AppleIcon } from "../../components/common/Icons"; -import { Link } from "react-router-dom"; import { HighlightImporter } from "./HighlightImporter"; import IOSShortcutModal from "../../components/modals/IOSShortcutModal"; @@ -364,8 +363,8 @@ export default function Settings() { key={b.did} className="flex items-center justify-between p-3 bg-surface-50 dark:bg-surface-800 rounded-xl group hover:bg-surface-100 dark:hover:bg-surface-700 transition-all" > - )}
- +
- +