diff --git a/.golangci.yml b/.golangci.yml index d2118de..37ad74e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -133,6 +133,7 @@ linters: rules: - linters: - dupl + - exhaustruct - goconst - lll - mnd diff --git a/cmd/gateway/helpers.go b/cmd/gateway/helpers.go index f3b2f3f..a4b9df8 100644 --- a/cmd/gateway/helpers.go +++ b/cmd/gateway/helpers.go @@ -51,8 +51,8 @@ func withConfig(action func(c *cli.Context, cfg config.Config) error) cli.Action } } -func newMigrator(cfg config.PostgresConfig) (*migrator.Migrator, error) { //nolint:gocritic - dbConn, err := pgdb.ConnectToPg(cfg) +func newMigrator(ctx context.Context, cfg config.PostgresConfig) (*migrator.Migrator, error) { //nolint:gocritic + dbConn, err := pgdb.ConnectToPg(ctx, cfg) if err != nil { return nil, fmt.Errorf("connect to postgres: %w", err) } @@ -85,7 +85,7 @@ func withApp( logger.Info("start") - migratorInst, err := newMigrator(cfg.Storage) + migratorInst, err := newMigrator(ctx, cfg.Storage) if err != nil { return fmt.Errorf("create new migrator: %w", err) } @@ -94,7 +94,7 @@ func withApp( return fmt.Errorf("up all migrations: %w", err) } - dbConnWrite, err := pgdb.ConnectToPg(cfg.Storage) + dbConnWrite, err := pgdb.ConnectToPg(ctx, cfg.Storage) if err != nil { return fmt.Errorf("connect to db: %w", err) } diff --git a/cmd/gateway/main.go b/cmd/gateway/main.go index 2f7d34d..0e0212c 100644 --- a/cmd/gateway/main.go +++ b/cmd/gateway/main.go @@ -155,8 +155,8 @@ func cmdGenerateModels(_ *cli.Context, cfg config.Config) error { //nolint:gocri return nil } -func cmdMigrateUp(_ *cli.Context, cfg config.Config) error { //nolint:gocritic - migratorInst, err := newMigrator(cfg.Storage) +func cmdMigrateUp(c *cli.Context, cfg config.Config) error { //nolint:gocritic + migratorInst, err := newMigrator(c.Context, cfg.Storage) if err != nil { return fmt.Errorf("create new migrator: %w", err) } @@ -168,8 +168,8 @@ func cmdMigrateUp(_ *cli.Context, cfg config.Config) error { //nolint:gocritic return nil } -func cmdMigrateDownOne(_ *cli.Context, cfg config.Config) error { //nolint:gocritic - migratorInst, err := newMigrator(cfg.Storage) +func cmdMigrateDownOne(c *cli.Context, cfg config.Config) error { //nolint:gocritic + migratorInst, err := newMigrator(c.Context, cfg.Storage) if err != nil { return fmt.Errorf("create new migrator: %w", err) } @@ -181,8 +181,8 @@ func cmdMigrateDownOne(_ *cli.Context, cfg config.Config) error { //nolint:gocri return nil } -func cmdMigrateCreateNew(_ *cli.Context, cfg config.Config) error { //nolint:gocritic - migratorInst, err := newMigrator(cfg.Storage) +func cmdMigrateCreateNew(c *cli.Context, cfg config.Config) error { //nolint:gocritic + migratorInst, err := newMigrator(c.Context, cfg.Storage) if err != nil { return fmt.Errorf("create new migrator: %w", err) } diff --git a/frontend/src/components/QueryResultsView.svelte b/frontend/src/components/QueryResultsView.svelte index 1ddd313..7100203 100644 --- a/frontend/src/components/QueryResultsView.svelte +++ b/frontend/src/components/QueryResultsView.svelte @@ -17,7 +17,13 @@ status = "", error = "" } = $props(); - console.log(meta) + + const displayMeta = $derived(meta ?? { + rows_count: "", + columns_count: "", + vectors_count: "", + trace: {records: []} + }); const panelClass = "rounded-xl border border-zinc-700/90 bg-zinc-900/90 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl"; const chipClass = "rounded-md border border-zinc-700/80 bg-zinc-800/90"; @@ -44,10 +50,10 @@ - - - - {#each meta.trace.records as record} + + + + {#each displayMeta.trace.records as record} {/each} diff --git a/internal/app/service.go b/internal/app/service.go index b5899e4..ae7708c 100644 --- a/internal/app/service.go +++ b/internal/app/service.go @@ -46,8 +46,9 @@ import ( ) var ( - ErrNotFound = errors.New("not found") - ErrForbidden = errors.New("forbidden") + ErrNotFound = errors.New("not found") + ErrForbidden = errors.New("forbidden") + errInvalidArgument = errors.New("invalid argument") ) type storedQueryResultPayload struct { @@ -507,7 +508,7 @@ func (s *Service) ListAllBookmarks(ctx context.Context, uid config.UserID) ([]st func (s *Service) ListRecentQueries(ctx context.Context, uid config.UserID, limit int64) ([]structs.Query, error) { if limit <= 0 { - limit = 50 + return nil, fmt.Errorf("limit must be positive: %w", errInvalidArgument) } items, err := s.opts.storage.ListQueryResultsByUser(s.opts.storage.Conn(ctx), uid, limit) @@ -518,9 +519,11 @@ func (s *Service) ListRecentQueries(ctx context.Context, uid config.UserID, limi out := make([]structs.Query, 0, len(items)) for i := range items { item := &items[i] - var payload storedQueryResultPayload - if err := json.Unmarshal(item.Response, &payload); err != nil { - continue + if len(item.Response) != 0 { + var payload storedQueryResultPayload + if err := json.Unmarshal(item.Response, &payload); err != nil { + continue + } } out = append(out, structs.Query{ @@ -545,10 +548,10 @@ func (s *Service) ListAdminRequests( } if page <= 0 { - page = 1 + return nil, false, fmt.Errorf("page must be positive: %w", errInvalidArgument) } if pageSize <= 0 { - pageSize = 50 + return nil, false, fmt.Errorf("page size must be positive: %w", errInvalidArgument) } offset := (page - 1) * pageSize diff --git a/internal/app/service_bookmarks_test.go b/internal/app/service_bookmarks_test.go new file mode 100644 index 0000000..62e76a0 --- /dev/null +++ b/internal/app/service_bookmarks_test.go @@ -0,0 +1,218 @@ +// Database Gateway provides access to servers with ACL for safe and restricted database interactions. +// Copyright (C) 2024 Kirill Zhuravlev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package app //nolint:exhaustruct,testpackage + +import ( + "context" + "errors" + "sync" + "testing" + + "github.com/go-jet/jet/v2/qrm" + "github.com/kazhuravlev/database-gateway/internal/config" + "github.com/kazhuravlev/database-gateway/internal/storage" + "github.com/kazhuravlev/database-gateway/internal/storage/jetgen/model" + "github.com/kazhuravlev/database-gateway/internal/structs" + "github.com/kazhuravlev/database-gateway/internal/uuid6" + "github.com/stretchr/testify/require" +) + +var errBookmarkStorageFailed = errors.New("storage failed") + +type fakeBookmarkStorage struct { + mu sync.Mutex + bookmarkInserts []storage.InsertBookmarkReq + insertErr error +} + +func (*fakeBookmarkStorage) Conn(context.Context) qrm.DB { //nolint:ireturn + return nil +} + +func (*fakeBookmarkStorage) InsertQueryResults(qrm.DB, storage.InsertQueryResultsReq) error { + return nil +} + +func (*fakeBookmarkStorage) SetQueryResultsState(qrm.DB, storage.SetQueryResultsStateReq) error { + return nil +} + +func (*fakeBookmarkStorage) GetQueryResultsByID(qrm.DB, uuid6.UUID) (*model.QueryResults, error) { + return nil, storage.ErrNotFound +} + +func (*fakeBookmarkStorage) ListQueryResultsByUser( + qrm.DB, + config.UserID, + int64, +) ([]storage.QueryResult, error) { + return nil, nil +} + +func (*fakeBookmarkStorage) ListQueryResults(qrm.DB, int64, int64) ([]storage.QueryResult, error) { + return nil, nil +} + +func (s *fakeBookmarkStorage) InsertBookmark(_ qrm.DB, req storage.InsertBookmarkReq) error { //nolint:gocritic + s.mu.Lock() + defer s.mu.Unlock() + + s.bookmarkInserts = append(s.bookmarkInserts, req) + if s.insertErr != nil { + return s.insertErr + } + + return nil +} + +func (*fakeBookmarkStorage) DeleteBookmark(qrm.DB, config.UserID, uuid6.UUID) error { + return nil +} + +func (*fakeBookmarkStorage) ListBookmarks(qrm.DB, config.UserID, config.TargetID) ([]storage.Bookmark, error) { + return nil, nil +} + +func (*fakeBookmarkStorage) ListBookmarksByUser(qrm.DB, config.UserID) ([]storage.Bookmark, error) { + return nil, nil +} + +func (s *fakeBookmarkStorage) insertedBookmarks() []storage.InsertBookmarkReq { + s.mu.Lock() + defer s.mu.Unlock() + + return append([]storage.InsertBookmarkReq(nil), s.bookmarkInserts...) +} + +func TestServiceAddBookmarkTrimsTitleAndQuery(t *testing.T) { + t.Parallel() + + store := new(fakeBookmarkStorage) + svc := newBookmarkTestService(t, store, bookmarkAllowPolicy) + user := structs.User{ID: config.UserID("alice@example.com"), Username: "alice", Role: config.RoleUser} + + err := svc.AddBookmark(context.Background(), user, config.TargetID("pg-1"), " Important query ", "\n select 1 \t") + + require.NoError(t, err) + inserted := store.insertedBookmarks() + require.Len(t, inserted, 1) + require.NotEmpty(t, inserted[0].ID.S()) + require.Equal(t, user.ID, inserted[0].UserID) + require.Equal(t, config.TargetID("pg-1"), inserted[0].TargetID) + require.Equal(t, "Important query", inserted[0].Title) + require.Equal(t, "select 1", inserted[0].Query) + require.False(t, inserted[0].CreatedAt.IsZero()) +} + +func TestServiceAddBookmarkValidatesEmptyTitleAndQuery(t *testing.T) { + t.Parallel() + + user := structs.User{ID: config.UserID("alice@example.com"), Username: "alice", Role: config.RoleUser} + testCases := []struct { + name string + title string + query string + }{ + {name: "empty title", title: "", query: "select 1"}, + {name: "blank title", title: " \t", query: "select 1"}, + {name: "empty query", title: "Important query", query: ""}, + {name: "blank query", title: "Important query", query: " \n"}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + store := new(fakeBookmarkStorage) + svc := newBookmarkTestService(t, store, bookmarkAllowPolicy) + + err := svc.AddBookmark(context.Background(), user, config.TargetID("pg-1"), tc.title, tc.query) + + require.ErrorContains(t, err, "title and query are required") + require.Empty(t, store.insertedBookmarks()) + }) + } +} + +func TestServiceAddBookmarkValidatesTargetAccess(t *testing.T) { + t.Parallel() + + store := new(fakeBookmarkStorage) + svc := newBookmarkTestService(t, store, ` +package gateway + +default allow_target := false +default allow_query := false +`) + user := structs.User{ID: config.UserID("alice@example.com"), Username: "alice", Role: config.RoleUser} + + err := svc.AddBookmark(context.Background(), user, config.TargetID("pg-1"), "Important query", "select 1") + + require.ErrorIs(t, err, ErrNotFound) + require.ErrorContains(t, err, "validate target access") + require.Empty(t, store.insertedBookmarks()) +} + +func TestServiceAddBookmarkPropagatesStorageError(t *testing.T) { + t.Parallel() + + insertErr := errBookmarkStorageFailed + store := &fakeBookmarkStorage{insertErr: insertErr} + svc := newBookmarkTestService(t, store, bookmarkAllowPolicy) + user := structs.User{ID: config.UserID("alice@example.com"), Username: "alice", Role: config.RoleUser} + + err := svc.AddBookmark(context.Background(), user, config.TargetID("pg-1"), "Important query", "select 1") + + require.ErrorIs(t, err, insertErr) + require.ErrorContains(t, err, "insert bookmark") + require.Len(t, store.insertedBookmarks(), 1) +} + +const bookmarkAllowPolicy = ` +package gateway + +default allow_target := false +default allow_query := false + +allow_target if { + "role:user" in input.subjects + input.target == "pg-1" +} +` + +func newBookmarkTestService(t *testing.T, store *fakeBookmarkStorage, policy string) *Service { + t.Helper() + + return &Service{ + opts: Options{ + targets: []config.Target{ + { + ID: "pg-1", + Description: "main", + Tags: []string{"prod"}, + Type: "postgres", + DefaultSchema: "public", + Tables: []config.TargetTable{{Table: "public.clients", Fields: []string{"id"}}}, + }, + }, + authorizer: mustAuthorizer(t, policy), + storage: store, + }, + connsMu: new(sync.RWMutex), + conns: nil, + } +} diff --git a/internal/app/service_query_results_test.go b/internal/app/service_query_results_test.go index 33396ca..4ddae5c 100644 --- a/internal/app/service_query_results_test.go +++ b/internal/app/service_query_results_test.go @@ -14,16 +14,25 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package app //nolint:testpackage +package app //nolint:exhaustruct,testpackage import ( + "context" + "encoding/json" + "errors" "testing" + "time" "github.com/kazhuravlev/database-gateway/internal/config" + "github.com/kazhuravlev/database-gateway/internal/storage" + "github.com/kazhuravlev/database-gateway/internal/storage/jetgen/model" "github.com/kazhuravlev/database-gateway/internal/structs" + "github.com/kazhuravlev/database-gateway/internal/uuid6" "github.com/stretchr/testify/require" ) +var errQueryResultsStorageFailed = errors.New("storage failed") + func TestCanReadQueryResults(t *testing.T) { t.Parallel() @@ -73,3 +82,453 @@ func TestCanReadQueryResults(t *testing.T) { }) } } + +func TestServiceGetQueryResults(t *testing.T) { + t.Parallel() + + queryResultID := uuid6.New() + createdAt := time.Date(2026, 7, 2, 12, 30, 0, 0, time.UTC) + owner := structs.User{ + ID: config.UserID("alice@example.com"), + Username: "alice", + Role: config.RoleUser, + } + otherUser := structs.User{ + ID: config.UserID("bob@example.com"), + Username: "bob", + Role: config.RoleUser, + } + table := structs.QTable{ + Headers: []string{"id", "name"}, + Rows: [][]string{{"1", "Alice"}}, + } + meta := structs.QMeta{ + RowsCount: 1, + ColumnsCount: 2, + } + + completedPayload, err := json.Marshal(storedQueryResultPayload{ + Table: table, + Meta: meta, + }) + require.NoError(t, err) + failedPayload, err := json.Marshal(structs.QError{Error: "query failed"}) + require.NoError(t, err) + + baseResult := model.QueryResults{ + ID: queryResultID, + UserID: owner.ID, + CreatedAt: createdAt, + Query: "select 1", + Response: completedPayload, + TargetID: config.TargetID("pg-1"), + State: structs.QueryStateCompleted, + } + + testCases := []struct { + name string + user structs.User + result model.QueryResults + wantTable structs.QTable + wantMeta structs.QMeta + wantError structs.QError + errIs error + errText string + }{ + { + name: "completed payload unmarshalling", + user: owner, + result: baseResult, + wantTable: table, + wantMeta: meta, + }, + { + name: "failed payload unmarshalling", + user: owner, + result: model.QueryResults{ + ID: queryResultID, + UserID: owner.ID, + CreatedAt: createdAt, + Query: "select broken", + Response: failedPayload, + TargetID: config.TargetID("pg-1"), + State: structs.QueryStateFailed, + }, + wantError: structs.QError{Error: "query failed"}, + }, + { + name: "malformed json errors", + user: owner, + result: model.QueryResults{ + ID: queryResultID, + UserID: owner.ID, + CreatedAt: createdAt, + Query: "select 1", + Response: []byte("{"), + TargetID: config.TargetID("pg-1"), + State: structs.QueryStateCompleted, + }, + errText: "unmarshal query results", + }, + { + name: "owner access", + user: owner, + result: baseResult, + wantTable: table, + wantMeta: meta, + }, + { + name: "non-owner denial", + user: otherUser, + result: baseResult, + errIs: ErrNotFound, + errText: "user does not have access to this query result", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + store := &fakeQueryHistoryStorage{queryResult: &tc.result} + svc := &Service{opts: Options{storage: store}} + + got, err := svc.GetQueryResults(context.Background(), tc.user, queryResultID) + + require.Equal(t, []uuid6.UUID{queryResultID}, store.queryResultIDsSet()) + if tc.errIs != nil || tc.errText != "" { + require.Nil(t, got) + if tc.errIs != nil { + require.ErrorIs(t, err, tc.errIs) + } else { + require.Error(t, err) + } + if tc.errText != "" { + require.ErrorContains(t, err, tc.errText) + } + + return + } + + require.NoError(t, err) + require.Equal(t, queryResultID.S(), got.ID) + require.Equal(t, tc.result.UserID.S(), got.UserID) + require.Equal(t, tc.result.TargetID.S(), got.TargetID) + require.Equal(t, tc.result.CreatedAt, got.CreatedAt) + require.Equal(t, tc.result.Query, got.Query) + require.Equal(t, tc.result.State, got.State) + + if tc.wantTable.Headers != nil { + require.True(t, got.QTable.HasVal()) + require.Equal(t, tc.wantTable, got.QTable.Val()) + require.Equal(t, tc.wantMeta, got.Meta) + } else { + require.False(t, got.QTable.HasVal()) + } + if tc.wantError.Error != "" { + require.True(t, got.QError.HasVal()) + require.Equal(t, tc.wantError, got.QError.Val()) + } else { + require.False(t, got.QError.HasVal()) + } + }) + } +} + +func TestListRecentQueriesValidatesLimit(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + limit int64 + }{ + {name: "zero", limit: 0}, + {name: "negative", limit: -1}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + store := new(fakeQueryHistoryStorage) + svc := &Service{opts: Options{storage: store}} + + got, err := svc.ListRecentQueries(context.Background(), config.UserID("alice@example.com"), tc.limit) + + require.Nil(t, got) + require.ErrorIs(t, err, errInvalidArgument) + require.Empty(t, store.listByUserLimitsSet()) + }) + } +} + +func TestListRecentQueriesPassesLimit(t *testing.T) { + t.Parallel() + + store := new(fakeQueryHistoryStorage) + svc := &Service{opts: Options{storage: store}} + + got, err := svc.ListRecentQueries(context.Background(), config.UserID("alice@example.com"), 7) + + require.NoError(t, err) + require.Empty(t, got) + require.Equal(t, []int64{7}, store.listByUserLimitsSet()) +} + +func TestListRecentQueriesStorageError(t *testing.T) { + t.Parallel() + + errStorage := errQueryResultsStorageFailed + store := &fakeQueryHistoryStorage{listByUserErr: errStorage} + svc := &Service{opts: Options{storage: store}} + + got, err := svc.ListRecentQueries(context.Background(), config.UserID("alice@example.com"), 7) + + require.Nil(t, got) + require.ErrorIs(t, err, errStorage) + require.ErrorContains(t, err, "list query results by user") + require.Equal(t, []int64{7}, store.listByUserLimitsSet()) +} + +func TestListRecentQueriesSkipsMalformedPayloads(t *testing.T) { + t.Parallel() + + completedPayload, err := json.Marshal(storedQueryResultPayload{}) + require.NoError(t, err) + + validID := uuid6.New() + store := &fakeQueryHistoryStorage{ + listByUserItems: []storage.QueryResult{ + { + ID: uuid6.New(), + UserID: config.UserID("alice@example.com"), + TargetID: config.TargetID("pg-1"), + CreatedAt: time.Date(2026, 7, 2, 12, 30, 0, 0, time.UTC), + Query: "select broken", + State: structs.QueryStateCompleted, + Response: []byte("{"), + }, + { + ID: validID, + UserID: config.UserID("alice@example.com"), + TargetID: config.TargetID("pg-2"), + CreatedAt: time.Date(2026, 7, 2, 12, 31, 0, 0, time.UTC), + Query: "select 1", + State: structs.QueryStateCompleted, + Response: completedPayload, + }, + }, + } + svc := &Service{opts: Options{storage: store}} + + got, err := svc.ListRecentQueries(context.Background(), config.UserID("alice@example.com"), 7) + + require.NoError(t, err) + require.Equal(t, []structs.Query{ + { + ID: validID.S(), + TargetID: config.TargetID("pg-2"), + Query: "select 1", + State: structs.QueryStateCompleted, + CreatedAt: "2026-07-02 12:31:00", + }, + }, got) +} + +func TestListRecentQueriesMixedStates(t *testing.T) { + t.Parallel() + + completedPayload, err := json.Marshal(storedQueryResultPayload{}) + require.NoError(t, err) + failedPayload, err := json.Marshal(structs.QError{Error: "query failed"}) + require.NoError(t, err) + + completedID := uuid6.New() + failedID := uuid6.New() + pendingID := uuid6.New() + store := &fakeQueryHistoryStorage{ + listByUserItems: []storage.QueryResult{ + { + ID: completedID, + UserID: config.UserID("alice@example.com"), + TargetID: config.TargetID("pg-1"), + CreatedAt: time.Date(2026, 7, 2, 12, 30, 0, 0, time.UTC), + Query: "select 1", + State: structs.QueryStateCompleted, + Response: completedPayload, + }, + { + ID: failedID, + UserID: config.UserID("alice@example.com"), + TargetID: config.TargetID("pg-2"), + CreatedAt: time.Date(2026, 7, 2, 12, 31, 0, 0, time.UTC), + Query: "select broken", + State: structs.QueryStateFailed, + Response: failedPayload, + }, + { + ID: pendingID, + UserID: config.UserID("alice@example.com"), + TargetID: config.TargetID("pg-3"), + CreatedAt: time.Date(2026, 7, 2, 12, 32, 0, 0, time.UTC), + Query: "select pending", + State: structs.QueryStateNew, + Response: nil, + }, + }, + } + svc := &Service{opts: Options{storage: store}} + + got, err := svc.ListRecentQueries(context.Background(), config.UserID("alice@example.com"), 7) + + require.NoError(t, err) + require.Equal(t, []structs.Query{ + { + ID: completedID.S(), + TargetID: config.TargetID("pg-1"), + Query: "select 1", + State: structs.QueryStateCompleted, + CreatedAt: "2026-07-02 12:30:00", + }, + { + ID: failedID.S(), + TargetID: config.TargetID("pg-2"), + Query: "select broken", + State: structs.QueryStateFailed, + CreatedAt: "2026-07-02 12:31:00", + }, + { + ID: pendingID.S(), + TargetID: config.TargetID("pg-3"), + Query: "select pending", + State: structs.QueryStateNew, + CreatedAt: "2026-07-02 12:32:00", + }, + }, got) + require.Equal(t, []int64{7}, store.listByUserLimitsSet()) +} + +func TestListAdminRequestsValidatesPagination(t *testing.T) { + t.Parallel() + + admin := structs.User{ID: config.UserID("admin@example.com"), Username: "admin", Role: config.RoleAdmin} + testCases := []struct { + name string + page int64 + pageSize int64 + }{ + {name: "zero page", page: 0, pageSize: 50}, + {name: "negative page", page: -1, pageSize: 50}, + {name: "zero page size", page: 1, pageSize: 0}, + {name: "negative page size", page: 1, pageSize: -1}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + store := new(fakeQueryHistoryStorage) + svc := &Service{opts: Options{storage: store}} + + got, hasNext, err := svc.ListAdminRequests(context.Background(), admin, tc.page, tc.pageSize) + + require.Nil(t, got) + require.False(t, hasNext) + require.ErrorIs(t, err, errInvalidArgument) + require.Empty(t, store.listRequestsLimitsSet()) + require.Empty(t, store.listRequestsOffsetsSet()) + }) + } +} + +func TestListAdminRequestsDeniesNonAdmin(t *testing.T) { + t.Parallel() + + store := new(fakeQueryHistoryStorage) + svc := &Service{opts: Options{storage: store}} + user := structs.User{ID: config.UserID("alice@example.com"), Username: "alice", Role: config.RoleUser} + + got, hasNext, err := svc.ListAdminRequests(context.Background(), user, 1, 50) + + require.Nil(t, got) + require.False(t, hasNext) + require.ErrorIs(t, err, ErrForbidden) + require.Empty(t, store.listRequestsLimitsSet()) + require.Empty(t, store.listRequestsOffsetsSet()) +} + +func TestListAdminRequestsPassesPagination(t *testing.T) { + t.Parallel() + + store := new(fakeQueryHistoryStorage) + svc := &Service{opts: Options{storage: store}} + admin := structs.User{ID: config.UserID("admin@example.com"), Username: "admin", Role: config.RoleAdmin} + + got, hasNext, err := svc.ListAdminRequests(context.Background(), admin, 3, 25) + + require.NoError(t, err) + require.False(t, hasNext) + require.Empty(t, got) + require.Equal(t, []int64{26}, store.listRequestsLimitsSet()) + require.Equal(t, []int64{50}, store.listRequestsOffsetsSet()) +} + +func TestListAdminRequestsTrimsHasNextItem(t *testing.T) { + t.Parallel() + + firstID := uuid6.New() + secondID := uuid6.New() + store := &fakeQueryHistoryStorage{ + listRequestsItems: []storage.QueryResult{ + { + ID: firstID, + UserID: config.UserID("alice@example.com"), + TargetID: config.TargetID("pg-1"), + Query: "select 1", + CreatedAt: time.Date(2026, 7, 2, 12, 30, 0, 0, time.UTC), + }, + { + ID: secondID, + UserID: config.UserID("bob@example.com"), + TargetID: config.TargetID("pg-2"), + Query: "select 2", + CreatedAt: time.Date(2026, 7, 2, 12, 31, 0, 0, time.UTC), + }, + }, + } + svc := &Service{opts: Options{storage: store}} + admin := structs.User{ID: config.UserID("admin@example.com"), Username: "admin", Role: config.RoleAdmin} + + got, hasNext, err := svc.ListAdminRequests(context.Background(), admin, 1, 1) + + require.NoError(t, err) + require.True(t, hasNext) + require.Equal(t, []structs.AdminRequest{ + { + ID: firstID.S(), + UserID: config.UserID("alice@example.com"), + TargetID: config.TargetID("pg-1"), + Query: "select 1", + CreatedAt: "2026-07-02 12:30:00", + }, + }, got) + require.Equal(t, []int64{2}, store.listRequestsLimitsSet()) + require.Equal(t, []int64{0}, store.listRequestsOffsetsSet()) +} + +func TestListAdminRequestsPropagatesStorageError(t *testing.T) { + t.Parallel() + + errStorage := errQueryResultsStorageFailed + store := &fakeQueryHistoryStorage{listRequestsErr: errStorage} + svc := &Service{opts: Options{storage: store}} + admin := structs.User{ID: config.UserID("admin@example.com"), Username: "admin", Role: config.RoleAdmin} + + got, hasNext, err := svc.ListAdminRequests(context.Background(), admin, 2, 10) + + require.Nil(t, got) + require.False(t, hasNext) + require.ErrorIs(t, err, errStorage) + require.Equal(t, []int64{11}, store.listRequestsLimitsSet()) + require.Equal(t, []int64{10}, store.listRequestsOffsetsSet()) +} diff --git a/internal/app/service_targets_test.go b/internal/app/service_targets_test.go index 326dbca..b7a4672 100644 --- a/internal/app/service_targets_test.go +++ b/internal/app/service_targets_test.go @@ -34,9 +34,19 @@ import ( ) type fakeQueryHistoryStorage struct { - mu sync.Mutex - inserts []storage.InsertQueryResultsReq - states []storage.SetQueryResultsStateReq + mu sync.Mutex + inserts []storage.InsertQueryResultsReq + states []storage.SetQueryResultsStateReq + queryResult *model.QueryResults + queryResultErr error + queryResultIDs []uuid6.UUID + listByUserItems []storage.QueryResult + listByUserErr error + listByUserLimits []int64 + listRequestsItems []storage.QueryResult + listRequestsErr error + listRequestsLimits []int64 + listRequestsOffset []int64 } func (*fakeQueryHistoryStorage) Conn(context.Context) qrm.DB { //nolint:ireturn @@ -61,20 +71,51 @@ func (s *fakeQueryHistoryStorage) SetQueryResultsState(_ qrm.DB, req storage.Set return nil } -func (*fakeQueryHistoryStorage) GetQueryResultsByID(qrm.DB, uuid6.UUID) (*model.QueryResults, error) { - return nil, storage.ErrNotFound +func (s *fakeQueryHistoryStorage) GetQueryResultsByID(_ qrm.DB, queryID uuid6.UUID) (*model.QueryResults, error) { + s.mu.Lock() + defer s.mu.Unlock() + + s.queryResultIDs = append(s.queryResultIDs, queryID) + if s.queryResultErr != nil { + return nil, s.queryResultErr + } + if s.queryResult == nil { + return nil, storage.ErrNotFound + } + + res := *s.queryResult + res.Response = append([]byte(nil), s.queryResult.Response...) + + return &res, nil } -func (*fakeQueryHistoryStorage) ListQueryResultsByUser( - qrm.DB, - config.UserID, - int64, +func (s *fakeQueryHistoryStorage) ListQueryResultsByUser( + _ qrm.DB, + _ config.UserID, + limit int64, ) ([]storage.QueryResult, error) { - return []storage.QueryResult{}, nil + s.mu.Lock() + defer s.mu.Unlock() + + s.listByUserLimits = append(s.listByUserLimits, limit) + if s.listByUserErr != nil { + return nil, s.listByUserErr + } + + return append([]storage.QueryResult(nil), s.listByUserItems...), nil } -func (*fakeQueryHistoryStorage) ListQueryResults(qrm.DB, int64, int64) ([]storage.QueryResult, error) { - return []storage.QueryResult{}, nil +func (s *fakeQueryHistoryStorage) ListQueryResults(_ qrm.DB, limit, offset int64) ([]storage.QueryResult, error) { + s.mu.Lock() + defer s.mu.Unlock() + + s.listRequestsLimits = append(s.listRequestsLimits, limit) + s.listRequestsOffset = append(s.listRequestsOffset, offset) + if s.listRequestsErr != nil { + return nil, s.listRequestsErr + } + + return append([]storage.QueryResult(nil), s.listRequestsItems...), nil } func (*fakeQueryHistoryStorage) InsertBookmark(qrm.DB, storage.InsertBookmarkReq) error { @@ -107,6 +148,34 @@ func (s *fakeQueryHistoryStorage) statesSet() []storage.SetQueryResultsStateReq return append([]storage.SetQueryResultsStateReq(nil), s.states...) } +func (s *fakeQueryHistoryStorage) queryResultIDsSet() []uuid6.UUID { + s.mu.Lock() + defer s.mu.Unlock() + + return append([]uuid6.UUID(nil), s.queryResultIDs...) +} + +func (s *fakeQueryHistoryStorage) listByUserLimitsSet() []int64 { + s.mu.Lock() + defer s.mu.Unlock() + + return append([]int64(nil), s.listByUserLimits...) +} + +func (s *fakeQueryHistoryStorage) listRequestsLimitsSet() []int64 { + s.mu.Lock() + defer s.mu.Unlock() + + return append([]int64(nil), s.listRequestsLimits...) +} + +func (s *fakeQueryHistoryStorage) listRequestsOffsetsSet() []int64 { + s.mu.Lock() + defer s.mu.Unlock() + + return append([]int64(nil), s.listRequestsOffset...) +} + const targetPolicy = ` package gateway diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 2c7b426..fa1c64a 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package config_test +package config_test //nolint:exhaustruct import ( "testing" @@ -27,35 +27,40 @@ func TestConfigValidate(t *testing.T) { t.Parallel() testCases := []struct { - name string - prepare func(cfg *config.Config) - wantErr bool + name string + prepare func(cfg *config.Config) + wantErrContains string }{ { - name: "valid config", + name: "valid config with schema-qualified table", prepare: func(_ *config.Config) {}, - wantErr: false, }, { - name: "table without schema prefix", + name: "already-qualified table", + prepare: func(cfg *config.Config) { + cfg.Targets[0].Tables[0].Table = "custom.clients" + }, + }, + { + name: "schema-qualified table is required", prepare: func(cfg *config.Config) { cfg.Targets[0].Tables[0].Table = "clients" }, - wantErr: true, + wantErrContains: "use table notation with leading schema", }, { name: "missing policy path", prepare: func(cfg *config.Config) { cfg.Policy.Path = "" }, - wantErr: true, + wantErrContains: "policy.path is required", }, { - name: "invalid role mapping", + name: "unsupported role", prepare: func(cfg *config.Config) { cfg.Users.RoleMapping["broken-group"] = config.Role("owner") }, - wantErr: true, + wantErrContains: `unsupported role "owner" for users.role_mapping["broken-group"]`, }, } @@ -67,8 +72,8 @@ func TestConfigValidate(t *testing.T) { tc.prepare(&cfg) err := cfg.Validate() - if tc.wantErr { - require.Error(t, err) + if tc.wantErrContains != "" { + require.ErrorContains(t, err, tc.wantErrContains) } else { require.NoError(t, err) } diff --git a/internal/facade/api_lrpc_test.go b/internal/facade/api_lrpc_test.go new file mode 100644 index 0000000..a9c6c1f --- /dev/null +++ b/internal/facade/api_lrpc_test.go @@ -0,0 +1,305 @@ +// Database Gateway provides access to servers with ACL for safe and restricted database interactions. +// Copyright (C) 2024 Kirill Zhuravlev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package facade //nolint:exhaustruct,testpackage + +import ( + "context" + "testing" + + "github.com/kazhuravlev/database-gateway/internal/app" + "github.com/kazhuravlev/database-gateway/internal/config" + "github.com/kazhuravlev/database-gateway/internal/structs" + "github.com/kazhuravlev/lrpc/ctypes" + "github.com/stretchr/testify/require" +) + +const lrpcTestUUID = "01HZ7N9R8Y9V1B9KZ6ZQVV8A1S" + +const lrpcTestCallID = ctypes.ID("") + +func TestLRPCHandlersRequireAPITokenUserContext(t *testing.T) { + t.Parallel() + + svc := new(Service) + ctx := context.Background() + + tests := []struct { + name string + call func() error + }{ + { + name: "target list", + call: func() error { + _, err := svc.lrpcTargetList(ctx, lrpcTestCallID, nil) + + return err + }, + }, + { + name: "target get", + call: func() error { + _, err := svc.lrpcTargetGet(ctx, lrpcTestCallID, lrpcTargetGetReq{TargetID: "pg-1"}) + + return err + }, + }, + { + name: "bookmark add", + call: func() error { + _, err := svc.lrpcBookmarksAdd(ctx, lrpcTestCallID, lrpcBookmarksAddReq{ + TargetID: "pg-1", + Title: "title", + Query: "select 1", + }) + + return err + }, + }, + { + name: "bookmark delete", + call: func() error { + _, err := svc.lrpcBookmarksDelete(ctx, lrpcTestCallID, lrpcBookmarksDeleteReq{ID: lrpcTestUUID}) + + return err + }, + }, + { + name: "query run", + call: func() error { + _, err := svc.lrpcQueryRun(ctx, lrpcTestCallID, lrpcQueryRunReq{ + TargetID: "pg-1", + Query: "select 1", + }) + + return err + }, + }, + { + name: "query results get", + call: func() error { + _, err := svc.lrpcQueryResultsGet(ctx, lrpcTestCallID, lrpcQueryResultsGetReq{ + QueryResultID: lrpcTestUUID, + }) + + return err + }, + }, + { + name: "export link", + call: func() error { + _, err := svc.lrpcQueryResultsExportLink(ctx, lrpcTestCallID, lrpcQueryResultsExportLinkReq{ + QueryResultID: lrpcTestUUID, + Format: "json", + }) + + return err + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + err := tc.call() + require.ErrorIs(t, err, app.ErrForbidden) + }) + } +} + +func TestLRPCHandlersRejectBadUUIDInputs(t *testing.T) { + t.Parallel() + + svc := new(Service) + ctx := lrpcTestContext() + + tests := []struct { + name string + call func() error + }{ + { + name: "bookmark delete id", + call: func() error { + _, err := svc.lrpcBookmarksDelete(ctx, lrpcTestCallID, lrpcBookmarksDeleteReq{ID: "bad-id"}) + + return err + }, + }, + { + name: "query results get id", + call: func() error { + _, err := svc.lrpcQueryResultsGet(ctx, lrpcTestCallID, lrpcQueryResultsGetReq{ID: "bad-id"}) + + return err + }, + }, + { + name: "query results get query_result_id", + call: func() error { + _, err := svc.lrpcQueryResultsGet(ctx, lrpcTestCallID, lrpcQueryResultsGetReq{QueryResultID: "bad-id"}) + + return err + }, + }, + { + name: "export link query_result_id", + call: func() error { + _, err := svc.lrpcQueryResultsExportLink(ctx, lrpcTestCallID, lrpcQueryResultsExportLinkReq{ + QueryResultID: "bad-id", + Format: "json", + }) + + return err + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + err := tc.call() + require.ErrorIs(t, err, errBadInput) + }) + } +} + +func TestLRPCHandlersRejectTrimmedRequiredFields(t *testing.T) { + t.Parallel() + + svc := new(Service) + ctx := lrpcTestContext() + + tests := []struct { + name string + call func() error + }{ + { + name: "target get target_id", + call: func() error { + _, err := svc.lrpcTargetGet(ctx, lrpcTestCallID, lrpcTargetGetReq{TargetID: " \t\n "}) + + return err + }, + }, + { + name: "bookmark add target_id", + call: func() error { + _, err := svc.lrpcBookmarksAdd(ctx, lrpcTestCallID, lrpcBookmarksAddReq{ + TargetID: " \t\n ", + Title: "title", + Query: "select 1", + }) + + return err + }, + }, + { + name: "bookmark add title", + call: func() error { + _, err := svc.lrpcBookmarksAdd(ctx, lrpcTestCallID, lrpcBookmarksAddReq{ + TargetID: "pg-1", + Title: " \t\n ", + Query: "select 1", + }) + + return err + }, + }, + { + name: "bookmark add query", + call: func() error { + _, err := svc.lrpcBookmarksAdd(ctx, lrpcTestCallID, lrpcBookmarksAddReq{ + TargetID: "pg-1", + Title: "title", + Query: " \t\n ", + }) + + return err + }, + }, + { + name: "query run target_id", + call: func() error { + _, err := svc.lrpcQueryRun(ctx, lrpcTestCallID, lrpcQueryRunReq{ + TargetID: " \t\n ", + Query: "select 1", + }) + + return err + }, + }, + { + name: "query run query", + call: func() error { + _, err := svc.lrpcQueryRun(ctx, lrpcTestCallID, lrpcQueryRunReq{ + TargetID: "pg-1", + Query: " \t\n ", + }) + + return err + }, + }, + { + name: "query results get id", + call: func() error { + _, err := svc.lrpcQueryResultsGet(ctx, lrpcTestCallID, lrpcQueryResultsGetReq{ID: " \t\n "}) + + return err + }, + }, + { + name: "export link query_result_id", + call: func() error { + _, err := svc.lrpcQueryResultsExportLink(ctx, lrpcTestCallID, lrpcQueryResultsExportLinkReq{ + QueryResultID: " \t\n ", + Format: "json", + }) + + return err + }, + }, + { + name: "export link format", + call: func() error { + _, err := svc.lrpcQueryResultsExportLink(ctx, lrpcTestCallID, lrpcQueryResultsExportLinkReq{ + QueryResultID: lrpcTestUUID, + Format: " \t\n ", + }) + + return err + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + err := tc.call() + require.ErrorIs(t, err, errBadInput) + }) + } +} + +func lrpcTestContext() context.Context { + return context.WithValue(context.Background(), ctxAPITokenUser, structs.User{ + ID: config.UserID("alice@example.com"), + Username: "alice", + Role: config.RoleUser, + }) +} diff --git a/internal/facade/service.go b/internal/facade/service.go index a2c097d..dc42b26 100644 --- a/internal/facade/service.go +++ b/internal/facade/service.go @@ -51,11 +51,13 @@ import ( ) const ( - ctxUser = "c-user" - keySession = "session" - keyUserID = "uid" - keyOIDCState = "oidc-state" - exportNonceLen = 8 + ctxUser = "c-user" + keySession = "session" + keyUserID = "uid" + keyOIDCState = "oidc-state" + exportFormatJSON = "json" + exportFormatCSV = "csv" + exportNonceLen = 8 ) var ( @@ -468,12 +470,23 @@ func marshalQueryResultsJSON(qRes *app.QueryResults) ([]byte, error) { } func marshalQueryResultsCSV(qRes *app.QueryResults) ([]byte, error) { + var csvBuf bytes.Buffer + csvWriter := csv.NewWriter(&csvBuf) if qRes.State != structs.QueryStateCompleted { - return []byte(`state\nNot Completed`), nil + if err := csvWriter.Write([]string{"state"}); err != nil { + return nil, err + } + if err := csvWriter.Write([]string{"Not Completed"}); err != nil { + return nil, err + } + csvWriter.Flush() + if err := csvWriter.Error(); err != nil { + return nil, err + } + + return csvBuf.Bytes(), nil } - var csvBuf bytes.Buffer - csvWriter := csv.NewWriter(&csvBuf) if err := csvWriter.Write(qRes.QTable.Val().Headers); err != nil { return nil, err } @@ -617,13 +630,13 @@ func serveExport(c echo.Context, format string, qRes *app.QueryResults) error { ) switch format { - case "json": + case exportFormatJSON: filename = "response.json" payload, err = marshalQueryResultsJSON(qRes) if err != nil { return fmt.Errorf("marshal query results json: %w", err) } - case "csv": + case exportFormatCSV: filename = "response.csv" payload, err = marshalQueryResultsCSV(qRes) if err != nil { @@ -641,7 +654,7 @@ func serveExport(c echo.Context, format string, qRes *app.QueryResults) error { func isSupportedExportFormat(format string) bool { switch format { - case "json", "csv": + case exportFormatJSON, exportFormatCSV: return true default: return false diff --git a/internal/facade/ui/dist/assets/index-B9cxeZ8l.js b/internal/facade/ui/dist/assets/index-B9cxeZ8l.js deleted file mode 100644 index 03b9dd3..0000000 --- a/internal/facade/ui/dist/assets/index-B9cxeZ8l.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Database Gateway provides access to servers with ACL for safe and restricted database interactions. - * Copyright (C) 2024 Kirill Zhuravlev - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=Array.isArray,t=Array.prototype.indexOf,n=Array.prototype.includes,r=Array.from,i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyDescriptors,s=Object.prototype,c=Array.prototype,l=Object.getPrototypeOf,u=Object.isExtensible,d=()=>{};function f(e){return e()}function p(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}var h=1024,g=2048,_=4096,v=8192,y=16384,b=32768,x=1<<25,S=65536,C=1<<18,w=1<<19,T=1<<20,ee=1<<25,te=65536,ne=1<<21,re=1<<22,ie=1<<23,ae=Symbol(`$state`),oe=Symbol(`legacy props`),se=Symbol(``),ce=Symbol(`attributes`),le=Symbol(`class`),ue=Symbol(`style`),de=Symbol(`text`),fe=Symbol(`form reset`),pe=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},me=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function he(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function ge(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function _e(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function ve(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function ye(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function be(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function xe(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function Se(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ce(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function we(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Te(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Ee(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var De={},E=Symbol(`uninitialized`),Oe=`http://www.w3.org/1999/xhtml`;function ke(){console.warn(`https://svelte.dev/e/derived_inert`)}function Ae(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function je(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var D=!1;function Me(e){D=e}var O;function k(e){if(e===null)throw Ae(),De;return O=e}function Ne(){return k(cn(O))}function A(e){if(D){if(cn(O)!==null)throw Ae(),De;O=e}}function Pe(e=1){if(D){for(var t=e,n=O;t--;)n=cn(n);O=n}}function Fe(e=!0){for(var t=0,n=O;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else (r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=cn(n);e&&n.remove(),n=i}}function Ie(e){if(!e||e.nodeType!==8)throw Ae(),De;return e.data}function Le(e){return e===this.v}function Re(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function ze(e){return!Re(e,this.v)}var Be=!1,Ve=!1;function He(){Ve=!0}var j=null;function Ue(e){j=e}function We(e,t=!1,n){j={p:j,i:!1,c:null,e:null,s:e,x:null,r:H,l:Ve&&!t?{s:null,u:null,$:[]}:null}}function M(e){var t=j,n=t.e;if(n!==null){t.e=null;for(var r of n)Tn(r)}return e!==void 0&&(t.x=e),t.i=!0,j=t.p,e??{}}function Ge(){return!Ve||j!==null&&j.l===null}var Ke=[];function qe(){var e=Ke;Ke=[],p(e)}function Je(e){if(Ke.length===0&&!Ot){var t=Ke;queueMicrotask(()=>{t===Ke&&qe()})}Ke.push(e)}function Ye(){for(;Ke.length>0;)qe()}function Xe(e){var t=H;if(t===null)return V.f|=ie,e;if(!(t.f&32768)&&!(t.f&4))throw e;Ze(e,t)}function Ze(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}var Qe=~(g|_|h);function N(e,t){e.f=e.f&Qe|t}function $e(e){e.f&512||e.deps===null?N(e,h):N(e,_)}function et(e){if(e!==null)for(let t of e)!(t.f&2)||!(t.f&65536)||(t.f^=te,et(t.deps))}function tt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),et(e.deps),N(e,h)}var nt=!1,rt=!1;function it(e){var t=rt;try{return rt=!1,[e(),rt]}finally{rt=t}}function at(e){let t=0,n=qt(0),r;return()=>{Sn()&&(U(n),Mn(()=>(t===0&&(r=W(()=>e(()=>Xt(n)))),t+=1,()=>{Je(()=>{--t,t===0&&(r?.(),r=void 0,Xt(n))})})))}}var ot=S|w;function st(e,t,n,r){new ct(e,t,n,r)}var ct=class{parent;is_pending=!1;transform_error;#e;#t=D?O:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=at(()=>(this.#m=qt(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=H;t.b=this,t.f|=128,n(e)},this.parent=H.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=Nn(()=>{if(D){let e=this.#t;Ne();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#v():this.#g()}else this.#y()},ot),D&&(this.#e=O)}#g(){try{this.#a=Pn(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed;t&&(this.#s=Pn(()=>{t(this.#e,()=>e,()=>()=>{})}))}#v(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=Pn(()=>e(this.#e)),Je(()=>{var e=this.#c=document.createDocumentFragment(),t=on();e.append(t),this.#a=this.#x(()=>Pn(()=>this.#r(t))),this.#u===0&&(this.#e.before(e),this.#c=null,Vn(this.#o,()=>{this.#o=null}),this.#b(P))}))}#y(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=Pn(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Gn(this.#a,e);let t=this.#n.pending;this.#o=Pn(()=>t(this.#e))}else this.#b(P)}catch(e){this.error(e)}}#b(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){tt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#x(e){var t=H,n=V,r=j;Qn(this.#i),Zn(this.#i),Ue(this.#i.ctx);try{return Pt.ensure(),e()}catch(e){return Xe(e),null}finally{Qn(t),Zn(n),Ue(r)}}#S(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#S(e,t);return}this.#u+=e,this.#u===0&&(this.#b(t),this.#o&&Vn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#S(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Je(()=>{this.#d=!1,this.#m&&Jt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),U(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;P?.is_fork?(this.#a&&P.skip_effect(this.#a),this.#o&&P.skip_effect(this.#o),this.#s&&P.skip_effect(this.#s),P.oncommit(()=>{this.#C(e)})):this.#C(e)}#C(e){this.#a&&=(Rn(this.#a),null),this.#o&&=(Rn(this.#o),null),this.#s&&=(Rn(this.#s),null),D&&(k(this.#t),Pe(),k(Fe()));var t=this.#n.onerror;let n=this.#n.failed;var r=!1,i=!1;let a=()=>{if(r){je();return}r=!0,i&&Ee(),this.#s!==null&&Vn(this.#s,()=>{this.#s=null}),this.#x(()=>{this.#y()})},o=e=>{try{i=!0,t?.(e,a),i=!1}catch(e){Ze(e,this.#i&&this.#i.parent)}n&&(this.#s=this.#x(()=>{try{return Pn(()=>{var t=H;t.b=this,t.f|=128,n(this.#e,()=>e,()=>a)})}catch(e){return Ze(e,this.#i.parent),null}}))};Je(()=>{var t;try{t=this.transform_error(e)}catch(e){Ze(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(o,e=>Ze(e,this.#i&&this.#i.parent)):o(t)})}};function lt(e,t,n,r){let i=Ge()?pt:_t;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=H,c=ut(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){Ze(e,s)}dt()}}var d=ft();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>ht(e))).then(u).catch(e=>Ze(e,s)).finally(d)}l?l.then(()=>{c(),f(),dt()}):f()}function ut(){var e=H,t=V,n=j,r=P;return function(i=!0){Qn(e),Zn(t),Ue(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function dt(e=!0){Qn(null),Zn(null),Ue(null),e&&P?.deactivate()}function ft(){var e=H,t=e.b,n=P,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function pt(e){var t=2|g;return H!==null&&(H.f|=w),{ctx:j,deps:null,effects:null,equals:Le,f:t,fn:e,reactions:null,rv:0,v:E,wv:0,parent:H,ac:null}}var mt=Symbol(`obsolete`);function ht(e,t,n){let r=H;r===null&&ge();var i=void 0,a=qt(E),o=!V,s=new Set;return jn(()=>{var t=H,n=m();i=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==pe&&n.reject(e)}).finally(dt)}catch(e){n.reject(e),dt()}var c=P;if(o){if(t.f&32768)var l=ft();if(r.b?.is_rendered())c.async_deriveds.get(t)?.reject(mt);else for(let e of s.values())e.reject(mt);s.add(n),c.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),s.delete(n),t!==mt&&(c.activate(),t?(a.f|=ie,Jt(a,t)):(a.f&8388608&&(a.f^=ie),Jt(a,e)),c.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),Cn(()=>{for(let e of s)e.reject(mt)}),new Promise(e=>{function t(n){function r(){n===i?e(a):t(i)}n.then(r,r)}t(i)})}function gt(e){let t=pt(e);return Be||er(t),t}function _t(e){let t=pt(e);return t.equals=ze,t}function vt(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;nthis.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)N(r,g),t(r);for(r of n.m)N(r,_),t(r)}this.#p.add(e)}#g(){this.#e=!0,Mt++>1e3&&(this.#S(),It());for(let e of this.#u)this.#d.delete(e),N(e,g),this.schedule(e);for(let e of this.#d)N(e,_),this.schedule(e);let t=this.#c;this.#c=[],this.apply();var n=At=[],r=[],i=jt=[];for(let e of t)try{this.#_(e,n,r)}catch(t){throw Ut(e),this.#h()||this.discard(),t}if(P=null,i.length>0){var a=e.ensure();for(let e of i)a.schedule(e)}if(At=null,jt=null,this.#h()){this.#b(r),this.#b(n);for(let[e,t]of this.#f)Ht(e,t);i.length>0&&P.#g();return}let o=this.#v();if(o){this.#b(r),this.#b(n),o.#y(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),Tt=this,Rt(r),Rt(n),Tt=null,this.#s?.resolve();var s=P;if(this.#a===0&&(this.#c.length===0||s!==null)&&(this.#S(),Be&&(this.#x(),P=s)),this.#c.length>0)if(s!==null){let e=s;e.#c.push(...this.#c.filter(t=>!e.#c.includes(t)))}else s=this;s!==null&&s.#g()}#_(e,t,n){e.f^=h;for(var r=e.first;r!==null;){var i=r.f,a=(i&96)!=0;if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=h:i&4?t.push(r):Be&&i&16777224?n.push(r):ur(r)&&(i&16&&this.#d.add(r),hr(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#v(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#y(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null)for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),N(i,g),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),P=this,this.#g()}#b(e){for(var t=0;t!u.current.get(e)[1]);if(!(!u.#e||r.length===0)){var i=r.filter(e=>!this.current.has(e));if(i.length===0)e&&u.discard();else if(t.length>0){if(e)for(let e of this.#p)u.unskip_effect(e,e=>{e.f&4194320?u.schedule(e):u.#b([e])});u.activate();var a=new Set,o=new Map;for(var s of t)zt(s,i,a,o);o=new Map;var c=[...u.current].filter(([e,t])=>{let n=this.current.get(e);return n?n[0]!==t[0]||n[1]!==t[1]:!0}).map(([e])=>e);if(c.length>0)for(let e of this.#l)!(e.f&155648)&&Bt(e,c,o)&&(e.f&4194320?(N(e,g),u.schedule(e)):u.#u.add(e));if(u.#c.length>0&&!u.#m){u.apply();for(var l of u.#c)u.#_(l,[],[]);u.#c=[]}u.deactivate()}}}}increment(e,t){if(this.#a+=1,e){let e=this.#o.get(t)??0;this.#o.set(t,e+1)}}decrement(e,t){if(--this.#a,e){let e=this.#o.get(t)??0;e===1?this.#o.delete(t):this.#o.set(t,e-1)}this.#m||(this.#m=!0,Je(()=>{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=m()).promise}static ensure(){if(P===null){let t=P=new e;!kt&&!Ot&&Je(()=>{t.#e||t.flush()})}return P}apply(){if(!Be||!this.is_fork&&this.#t===null&&this.#n===null){Et=null;return}Et=new Map;for(let[e,[t]]of this.current)Et.set(e,t);for(let t=Ct;t!==null;t=t.#n)if(!(t===this||t.is_fork)){var e=!1;if(t.id0)){Gt.clear();for(let e of Lt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Lt.has(n)&&(Lt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||hr(n)}}Lt.clear()}}Lt=null}}function zt(e,t,n,r){if(!n.has(e)&&(n.add(e),e.reactions!==null))for(let i of e.reactions){let e=i.f;e&2?zt(i,t,n,r):e&4194320&&!(e&2048)&&Bt(i,t,r)&&(N(i,g),Vt(i))}}function Bt(e,t,r){let i=r.get(e);if(i!==void 0)return i;if(e.deps!==null)for(let i of e.deps){if(n.call(t,i))return!0;if(i.f&2&&Bt(i,t,r))return r.set(i,!0),!0}return r.set(e,!1),!1}function Vt(e){P.schedule(e)}function Ht(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),N(e,h);for(var n=e.first;n!==null;)Ht(n,t),n=n.next}}function Ut(e){N(e,h);for(var t=e.first;t!==null;)Ut(t),t=t.next}var Wt=new Set,Gt=new Map,Kt=!1;function qt(e,t){return{f:0,v:e,reactions:null,equals:Le,rv:0,wv:0}}function F(e,t){let n=qt(e,t);return er(n),n}function I(e,t=!1,n=!0){let r=qt(e);return t||(r.equals=ze),Ve&&n&&j!==null&&j.l!==null&&(j.l.s??=[]).push(r),r}function L(e,t,n=!1){return V!==null&&(!Xn||V.f&131072)&&Ge()&&V.f&4325394&&($n===null||!$n.has(e))&&Te(),Jt(e,n?Qt(t):t,jt)}function Jt(e,t,n=null){if(!e.equals(t)){Gt.set(e,Jn?t:e.v);var r=Pt.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&&yt(t),Et===null&&$e(t)}e.wv=lr(),Zt(e,g,n),Ge()&&H!==null&&H.f&1024&&!(H.f&96)&&(rr===null?ir([e]):rr.push(e)),!r.is_fork&&Wt.size>0&&!Kt&&Yt()}return t}function Yt(){Kt=!1;for(let e of Wt){e.f&1024&&N(e,_);let t;try{t=ur(e)}catch{t=!0}t&&hr(e)}Wt.clear()}function Xt(e){L(e,e.v+1)}function Zt(e,t,n){var r=e.reactions;if(r!==null)for(var i=Ge(),a=r.length,o=0;o{if(sr===d)return e();var t=V,n=sr;Zn(null),cr(d);var r=e();return Zn(t),cr(n),r};return i&&r.set(`length`,F(t.length,u)),new Proxy(t,{defineProperty(e,t,n){(!(`value`in n)||n.configurable===!1||n.enumerable===!1||n.writable===!1)&&Ce();var i=r.get(t);return i===void 0?f(()=>{var e=F(n.value,u);return r.set(t,e),e}):L(i,n.value,!0),!0},deleteProperty(e,t){var n=r.get(t);if(n===void 0){if(t in e){let e=f(()=>F(E,u));r.set(t,e),Xt(o)}}else L(n,E),Xt(o);return!0},get(e,n,i){if(n===ae)return t;var o=r.get(n),s=n in e;if(o===void 0&&(!s||a(e,n)?.writable)&&(o=f(()=>F(Qt(s?e[n]:E),u)),r.set(n,o)),o!==void 0){var c=U(o);return c===E?void 0:c}return Reflect.get(e,n,i)},getOwnPropertyDescriptor(e,t){var n=Reflect.getOwnPropertyDescriptor(e,t);if(n&&`value`in n){var i=r.get(t);i&&(n.value=U(i))}else if(n===void 0){var a=r.get(t),o=a?.v;if(a!==void 0&&o!==E)return{enumerable:!0,configurable:!0,value:o,writable:!0}}return n},has(e,t){if(t===ae)return!0;var n=r.get(t),i=n!==void 0&&n.v!==E||Reflect.has(e,t);return(n!==void 0||H!==null&&(!i||a(e,t)?.writable))&&(n===void 0&&(n=f(()=>F(i?Qt(e[t]):E,u)),r.set(t,n)),U(n)===E)?!1:i},set(e,t,n,s){var c=r.get(t),l=t in e;if(i&&t===`length`)for(var d=n;dF(E,u)),r.set(d+``,p)):L(p,E)}if(c===void 0)(!l||a(e,t)?.writable)&&(c=f(()=>F(void 0,u)),L(c,Qt(n)),r.set(t,c));else{l=c.v!==E;var m=f(()=>Qt(n));L(c,m)}var h=Reflect.getOwnPropertyDescriptor(e,t);if(h?.set&&h.set.call(s,n),!l){if(i&&typeof t==`string`){var g=r.get(`length`),_=Number(t);Number.isInteger(_)&&_>=g.v&&L(g,_+1)}Xt(o)}return!0},ownKeys(e){U(o);var t=Reflect.ownKeys(e).filter(e=>{var t=r.get(e);return t===void 0||t.v!==E});for(var[n,i]of r)i.v!==E&&!(n in e)&&t.push(n);return t},setPrototypeOf(){we()}})}new Set([`copyWithin`,`fill`,`pop`,`push`,`reverse`,`shift`,`sort`,`splice`,`unshift`]);var $t,en,tn,nn,rn;function an(){if($t===void 0){$t=window,en=document,tn=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;nn=a(t,`firstChild`).get,rn=a(t,`nextSibling`).get,u(e)&&(e[le]=void 0,e[ce]=null,e[ue]=void 0,e.__e=void 0),u(n)&&(n[de]=void 0)}}function on(e=``){return document.createTextNode(e)}function sn(e){return nn.call(e)}function cn(e){return rn.call(e)}function R(e,t){if(!D)return sn(e);var n=sn(O);if(n===null)n=O.appendChild(on());else if(t&&n.nodeType!==3){var r=on();return n?.before(r),k(r),r}return t&&pn(n),k(n),n}function ln(e,t=!1){if(!D){var n=sn(e);return n instanceof Comment&&n.data===``?cn(n):n}if(t){if(O?.nodeType!==3){var r=on();return O?.before(r),k(r),r}pn(O)}return O}function z(e,t=1,n=!1){let r=D?O:e;for(var i;t--;)i=r,r=cn(r);if(!D)return r;if(n){if(r?.nodeType!==3){var a=on();return r===null?i?.after(a):r.before(a),k(a),a}pn(r)}return k(r),r}function un(e){e.textContent=``}function dn(){return!Be||Lt!==null?!1:(H.f&b)!==0}function fn(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function pn(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function mn(e){D&&sn(e)!==null&&un(e)}var hn=!1;function gn(){hn||(hn=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[fe]?.()})},{capture:!0}))}function _n(e){var t=V,n=H;Zn(null),Qn(null);try{return e()}finally{Zn(t),Qn(n)}}function vn(e,t,n,r=n){e.addEventListener(t,()=>_n(n));let i=e[fe];i?e[fe]=()=>{i(),r(!0)}:e[fe]=()=>r(!0),gn()}function yn(e){H===null&&(V===null&&be(e),ye()),Jn&&ve(e)}function bn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function xn(e,t){var n=H;n!==null&&n.f&8192&&(e|=v);var r={ctx:j,deps:null,nodes:null,f:e|g|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};P?.register_created_effect(r);var i=r;if(e&4)At===null?Pt.ensure().schedule(r):At.push(r);else if(t!==null){try{hr(r)}catch(e){throw Rn(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=S))}if(i!==null&&(i.parent=n,n!==null&&bn(i,n),V!==null&&V.f&2&&!(e&64))){var a=V;(a.effects??=[]).push(i)}return r}function Sn(){return V!==null&&!Xn}function Cn(e){let t=xn(8,null);return N(t,h),t.teardown=e,t}function wn(e){yn(`$effect`);var t=H.f;if(!V&&t&32&&j!==null&&!j.i){var n=j;(n.e??=[]).push(e)}else return Tn(e)}function Tn(e){return xn(4|T,e)}function En(e){return yn(`$effect.pre`),xn(8|T,e)}function Dn(e){Pt.ensure();let t=xn(64|w,e);return(e={})=>new Promise(n=>{e.outro?Vn(t,()=>{Rn(t),n(void 0)}):(Rn(t),n(void 0))})}function On(e){return xn(4,e)}function kn(e,t){var n=j,r={effect:null,ran:!1,deps:e};n.l.$.push(r),r.effect=Mn(()=>{if(e(),!r.ran){r.ran=!0;var n=H;try{Qn(n.parent),W(t)}finally{Qn(n)}}})}function An(){var e=j;Mn(()=>{for(var t of e.l.$){t.deps();var n=t.effect;n.f&1024&&n.deps!==null&&N(n,_),ur(n)&&hr(n),t.ran=!1}})}function jn(e){return xn(re|w,e)}function Mn(e,t=0){return xn(8|t,e)}function B(e,t=[],n=[],r=[]){lt(r,t,n,t=>{xn(8,()=>{e(...t.map(U))})})}function Nn(e,t=0){return xn(16|t,e)}function Pn(e){return xn(32|w,e)}function Fn(e){var t=e.teardown;if(t!==null){let e=Jn,n=V;Yn(!0),Zn(null);try{t.call(null)}finally{Yn(e),Zn(n)}}}function In(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&_n(()=>{e.abort(pe)});var r=n.next;n.f&64?n.parent=null:Rn(n,t),n=r}}function Ln(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||Rn(t),t=n}}function Rn(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(zn(e.nodes.start,e.nodes.end),n=!0),e.f|=x,In(e,t&&!n),mr(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();Fn(e),e.f^=x,e.f|=y;var i=e.parent;i!==null&&i.first!==null&&Bn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function zn(e,t){for(;e!==null;){var n=e===t?null:cn(e);e.remove(),e=n}}function Bn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Vn(e,t,n=!0){var r=[];Hn(e,r,!0);var i=()=>{n&&Rn(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Hn(e,t,n){if(!(e.f&8192)){e.f^=v;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=(i.f&65536)!=0||(i.f&32)!=0&&(e.f&16)!=0;Hn(i,t,o?n:!1)}i=a}}}function Un(e){Wn(e,!0)}function Wn(e,t){if(e.f&8192){e.f^=v,e.f&1024||(N(e,g),Pt.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=(n.f&65536)!=0||(n.f&32)!=0;Wn(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Gn(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:cn(n);t.append(n),n=i}}var Kn=null,qn=!1,Jn=!1;function Yn(e){Jn=e}var V=null,Xn=!1;function Zn(e){V=e}var H=null;function Qn(e){H=e}var $n=null;function er(e){V!==null&&(!Be||V.f&2)&&($n??=new Set).add(e)}var tr=null,nr=0,rr=null;function ir(e){rr=e}var ar=1,or=0,sr=or;function cr(e){sr=e}function lr(){return++ar}function ur(e){var t=e.f;if(t&2048)return!0;if(t&2&&(e.f&=~te),t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&Et===null&&N(e,h)}return!1}function dr(e,t,n=!0){var r=e.reactions;if(r!==null&&!(!Be&&$n!==null&&$n.has(e)))for(var i=0;i{e.ac.abort(pe)}),e.ac=null);try{e.f|=ne;var u=e.fn,d=u();e.f|=b;var f=e.deps,p=P?.is_fork;if(tr!==null){var m;if(p||mr(e,nr),f!==null&&nr>0)for(f.length=nr+tr.length,m=0;m{requestAnimationFrame(()=>e()),setTimeout(()=>e())});await Promise.resolve(),Ft()}function U(e){var t=(e.f&2)!=0;if(Kn?.add(e),V!==null&&!Xn&&!(H!==null&&H.f&16384)&&($n===null||!$n.has(e))){var r=V.deps;if(V.f&2097152)e.rv{throw e});throw p}}finally{e[Cr]=t,delete e.currentTarget,Zn(d),Qn(f)}}}var Ar=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function jr(e){return Ar?.createHTML(e)??e}function Mr(e){var t=fn(`template`);return t.innerHTML=jr(e.replaceAll(``,``)),t.content}function Nr(e,t){var n=H;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function G(e,t){var n=(t&1)!=0,r=(t&2)!=0,i,a=!e.startsWith(``);return()=>{if(D)return Nr(O,null),O;i===void 0&&(i=Mr(a?e:``+e),n||(i=sn(i)));var t=r||tn?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=sn(t),s=t.lastChild;Nr(o,s)}else Nr(t,t);return t}}function Pr(e=``){if(!D){var t=on(e+``);return Nr(t,t),t}var n=O;return n.nodeType===3?pn(n):(n.before(n=on()),k(n)),Nr(n,n),n}function Fr(){if(D)return Nr(O,null),O;var e=document.createDocumentFragment(),t=document.createComment(``),n=on();return e.append(t,n),Nr(t,n),e}function K(e,t){if(D){var n=H;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=O),Ne();return}e!==null&&e.before(t)}function q(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[de]??=e.nodeValue)&&(e[de]=n,e.nodeValue=`${n}`)}function Ir(e,t){return Rr(e,t)}var Lr=new Map;function Rr(e,{target:t,anchor:n,props:i={},events:a,context:o,intro:s=!0,transformError:c}){an();var l=void 0,u=Dn(()=>{var s=n??t.appendChild(on());st(s,{pending:()=>{}},t=>{We({});var n=j;if(o&&(n.c=o),a&&(i.$$events=a),D&&Nr(t,null),l=e(t,i)||{},D&&(H.nodes.end=O,O===null||O.nodeType!==8||O.data!==`]`))throw Ae(),De;M()},c);var u=new Set,d=e=>{for(var n=0;n{for(var e of u)for(let n of[t,document]){var r=Lr.get(n),i=r.get(e);--i==0?(n.removeEventListener(e,kr),r.delete(e),r.size===0&&Lr.delete(n)):r.set(e,i)}Tr.delete(d),s!==n&&s.parentNode?.removeChild(s)}});return zr.set(l,u),l}var zr=new WeakMap,Br=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)Un(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(Un(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(Rn(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Gn(r,t),t.append(on()),this.#n.set(e,{effect:r,fragment:t})}else Rn(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Vn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(Rn(n.effect),this.#n.delete(e))};ensure(e,t){var n=P,r=dn();if(t&&!this.#t.has(e)&&!this.#n.has(e))if(r){var i=document.createDocumentFragment(),a=on();i.append(a),this.#n.set(e,{effect:Pn(()=>t(a)),fragment:i})}else this.#t.set(e,Pn(()=>t(this.anchor)));if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else D&&(this.anchor=O),this.#a(n)}};function J(e,t,n=!1){var r;D&&(r=O,Ne());var i=new Br(e),a=n?S:0;function o(e,t){if(D){var n=Ie(r);if(e!==parseInt(n.substring(1))){var a=Fe();k(a),i.anchor=a,Me(!1),i.ensure(e,t),Me(!0);return}}i.ensure(e,t)}Nn(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}function Vr(e,t){return t}function Hr(e,t,n){for(var i=[],a=t.length,o,s=t.length,c=0;c{if(o){if(o.pending.delete(n),o.done.add(n),o.pending.size===0){var t=e.outrogroups;Ur(e,r(o.done)),t.delete(o),t.size===0&&(e.outrogroups=null)}}else --s},!1)}if(s===0){var l=i.length===0&&n!==null;if(l){var u=n,d=u.parentNode;un(d),d.append(u),e.items.clear()}Ur(e,t,!l)}else o={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(o)}function Ur(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var t=i();return e(t)?t:t==null?[]:r(t)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,qr(v,p,c,n,a),d!==null&&(p.length===0?d.f&33554432?(d.f^=ee,Yr(d,null,c)):Un(d):Vn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:Nn(()=>{p=U(f);var e=p.length;let t=!1;D&&Ie(c)===`[!`!=(e===0)&&(c=Fe(),k(c),Me(!1),t=!0);for(var r=new Set,u=P,v=dn(),y=0;ys(c)):(d=Pn(()=>s(Wr??=on())),d.f|=ee)),e>r.size&&_e(``,``,``),D&&e>0&&k(Fe()),!h)if(m.set(u,r),v){for(let[e,t]of l)r.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u);t&&Me(!0),U(f)}),flags:n,items:l,pending:m,outrogroups:null,fallback:d};h=!1,D&&(c=O)}function Kr(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function qr(e,t,n,i,a){var o=(i&8)!=0,s=t.length,c=e.items,l=Kr(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(o)for(v=0;v0){var te=i&4&&s===0?n:null;if(o){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function Jr(e,t,n,r,i,a,o,s){var c=o&1?o&16?qt(n):I(n,!1,!1):null,l=o&2?qt(i):null;return{v:c,i:l,e:Pn(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function Yr(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=cn(r);if(a.before(r),r===i)return;r=o}}function Xr(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function Zr(e,t){let n=null,r=D;var i;if(D){n=O;for(var a=sn(document.head);a!==null&&(a.nodeType!==8||a.data!==e);)a=cn(a);if(a===null)Me(!1);else{var o=cn(a);a.remove(),k(o)}}D||(i=document.head.appendChild(on()));try{Nn(()=>{var e=Pn(()=>t(i));e.f|=C})}finally{r&&(Me(!0),k(n))}}function Qr(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`)if(Array.isArray(e)){var i=e.length;for(t=0;t=0;){var s=o+a;(o===0||ti.includes(r[o-1]))&&(s===r.length||ti.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function Y(e,t,n,r,i,a){var o=e[le];if(D||o!==n||o===void 0){var s=ni(n,r,a);(!D||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[le]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}var ri=Symbol(`is custom element`),ii=Symbol(`is html`),ai=me?`link`:`LINK`;function oi(e){if(D){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;X(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;X(e,`checked`,null),e.checked=r}}};e[fe]=n,Je(n),gn()}}function si(e,t){var n=ci(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function X(e,t,n,r){var i=ci(e);D&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===ai)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[se]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&ui(e).includes(t)?e[t]=n:e.setAttribute(t,n))}function ci(e){return e[ce]??={[ri]:e.nodeName.includes(`-`),[ii]:e.namespaceURI===Oe}}var li=new Map;function ui(e){var t=e.getAttribute(`is`)||e.nodeName,n=li.get(t);if(n)return n;li.set(t,n=[]);for(var r,i=e,a=Element.prototype;a!==i;){for(var s in r=o(i),r)r[s].set&&s!==`innerHTML`&&s!==`textContent`&&s!==`innerText`&&n.push(s);i=l(i)}return n}function di(e,t,n=t){var r=new WeakSet;vn(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=fi(e)?pi(a):a,n(a),P!==null&&r.add(P),await gr(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(D&&e.defaultValue!==e.value||W(t)==null&&e.value)&&(n(fi(e)?pi(e.value):e.value),P!==null&&r.add(P)),Mn(()=>{var n=t();if(e===document.activeElement){var i=Be?Tt:P;if(r.has(i))return}fi(e)&&n===pi(e.value)||e.type===`date`&&!n&&!e.value||n!==e.value&&(e.value=n??``)})}function fi(e){var t=e.type;return t===`number`||t===`range`}function pi(e){return e===``?null:+e}function mi(e=!1){let t=j,n=t.l.u;if(!n)return;let r=()=>yr(t.s);if(e){let e=0,n={},i=pt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>U(i)}n.b.length&&En(()=>{hi(t,r),p(n.b)}),wn(()=>{let e=W(()=>n.m.map(f));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&wn(()=>{hi(t,r),p(n.a)})}function hi(e,t){if(e.l.s)for(let t of e.l.s)U(t);t()}function Z(e,t,n,r){var i=!Ve||(n&2)!=0,o=(n&8)!=0,s=(n&16)!=0,c=r,l=!0,u=void 0,d=()=>s&&i?(u??=pt(r),U(u)):(l&&(l=!1,c=s?W(r):r),c);let f;if(o){var p=ae in e||oe in e;f=a(e,t)?.set??(p&&t in e?n=>e[t]=n:void 0)}var m,h=!1;o?[m,h]=it(()=>e[t]):m=e[t],m===void 0&&r!==void 0&&(m=d(),f&&(i&&Se(t),f(m)));var g=i?()=>{var n=e[t];return n===void 0?d():(l=!0,n)}:()=>{var n=e[t];return n!==void 0&&(c=void 0),n===void 0?c:n};if(i&&!(n&4))return g;if(f){var _=e.$$legacy;return(function(e,t){return arguments.length>0?((!i||!t||_||h)&&f(t?g():e),e):g()})}var v=!1,y=(n&1?pt:_t)(()=>(v=!1,g()));o&&U(y);var b=H;return(function(e,t){if(arguments.length>0){let n=t?U(y):i&&o?Qt(e):e;return L(y,n),v=!0,c!==void 0&&(c=n),e}return Jn&&v||b.f&16384?y.v:U(y)})}function gi(e){j===null&&he(`onMount`),Ve&&j.l!==null?_i(j).m.push(e):wn(()=>{let t=W(e);if(typeof t==`function`)return t})}function _i(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`),He();var vi=`dbgw_api_access_token`,yi=window.location.origin,bi=`${yi}/auth`,xi=`${yi}/api/v1`;function Si(e,t=`Request failed`){if(!e?.error)return null;let n=typeof e.error.message==`string`&&e.error.message?e.error.message:`${t}${e.error.code?` (${e.error.code})`:``}`;return Error(n)}function Q(e,t=`Request failed`){return e instanceof Error&&e.message?e.message:t}function Ci(){return typeof crypto<`u`&&crypto.randomUUID?crypto.randomUUID():`request-${Date.now()}`}function wi(e){return e===401||e===403}function Ti(){window.location.href=bi}function Ei(){return window.localStorage.getItem(vi)||``}function Di(e){if(!e){window.localStorage.removeItem(vi);return}window.localStorage.setItem(vi,e)}function Oi(){Di(``)}function ki(){let e=window.location.hash.startsWith(`#`)?window.location.hash.slice(1):window.location.hash;if(!e)return``;let t=new URLSearchParams(e).get(`access_token`)||``;return t?(Di(t),window.history.replaceState({},document.title,window.location.pathname+window.location.search),t):``}async function Ai(e,t,n){let r;try{r=await fetch(`${xi}/${t}`,{method:`POST`,headers:{Authorization:`Bearer ${e}`,"Content-Type":`application/json`},body:JSON.stringify({lrpc:`1`,id:Ci(),params:n})})}catch(e){throw Error(Q(e,`Network request failed`))}let i=null;try{i=await r.json()}catch(e){throw wi(r.status)?Error(`Unauthorized (${r.status})`):r.ok?Error(Q(e,`Failed to parse server response`)):Error(`Failed to do request (${r.status})`)}let a=Si(i);if(a)throw a;if(wi(r.status))throw Error(`Unauthorized (${r.status})`);if(!r.ok)throw Error(`Failed to do request (${r.status})`);return i.result}async function ji(e){let t=Ei();if(!t)return Ti(),null;try{return await e(t)}catch(e){if(!Q(e,``).includes(`Unauthorized`))throw e;return Oi(),Ti(),null}}function Mi(e){return Ai(e,`targets.list.v1`,{})}function Ni(e,t){return Ai(e,`targets.get.v1`,{target_id:t})}function Pi(e){return Ai(e,`profile.get.v1`,{})}function Fi(e,t=``){return Ai(e,`bookmarks.list.v1`,t?{target_id:t}:{})}function Ii(e,t){return Ai(e,`queries.list.v1`,typeof t==`number`?{limit:t}:{})}function Li(e,t,n){return Ai(e,`query.run.v1`,{target_id:t,query:n})}function Ri(e){if(!e)return e;let t=e.table??{headers:[],rows:[]},n=e.error,r=typeof n==`string`?n:n?.error??``;return{...e,state:e.state??``,status:e.state??e.status??``,table:{headers:t.headers??[],rows:t.rows??[]},meta:e.meta??null,error:r}}function zi(e,t){return Ai(e,`admin.requests.list.v1`,{page:t})}function Bi(e,t){return Ai(e,`query-results.get.v1`,{id:t}).then(Ri)}function Vi(e,t,n){return Ai(e,`query-results.export-link.v1`,{query_result_id:t,format:n})}function Hi(e,t,n,r){return Ai(e,`bookmarks.add.v1`,{target_id:t,title:n,query:r})}function Ui(e,t){return Ai(e,`bookmarks.delete.v1`,{id:t})}var Wi=Gi(`/ui/`);function Gi(e){let t=e.endsWith(`/`)?e.slice(0,-1):e;return t===``?``:t}function Ki(e,t=``){return`${Wi}${e.startsWith(`/`)?e:`/${e}`}${t}`}function qi(){return{pathname:Xi(window.location.pathname),searchParams:new URLSearchParams(window.location.search)}}function Ji(e,t=``){window.history.pushState({},``,Ki(e,t)),window.dispatchEvent(new PopStateEvent(`popstate`))}function Yi(e,t){let n=Zi(e),r=Zi(t);if(n.length!==r.length)return null;let i={};for(let e=0;e· `,1),$i=G(` `,1),ea=G(`Admin requests`),ta=G(`
DG
Database Gateway
`);function na(e,t){We(t,!0);let n=Z(t,`isAdminRequestsPage`,3,!1),r=`inline-flex min-h-8 items-center justify-center gap-2 rounded-lg border px-2.5 text-[13px] font-semibold leading-none transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-lime-200`,i=`border-lime-300 bg-lime-300 text-zinc-950`,a=`border-zinc-700 bg-zinc-800/90 text-zinc-100 hover:border-zinc-500 hover:bg-zinc-700/90`,o=gt(()=>t.profile?.username||t.profile?.id||`Unknown user`),s=gt(()=>t.profile?.role||``),c=gt(()=>U(s)===`admin`);var l=ta();Y(l,1,`rounded-xl border border-zinc-700/90 bg-zinc-900/90 px-3 py-2.5 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl mb-3 flex flex-col gap-3 md:flex-row md:items-center md:justify-between`);var u=R(l),d=R(u),f=z(d,2),p=z(R(f),2),m=R(p),h=e=>{K(e,Pr(`Loading session`))},g=e=>{var n=Pr();B(()=>q(n,t.error)),K(e,n)},_=e=>{var t=$i(),n=ln(t),r=R(n,!0);A(n);var i=z(n,2),a=e=>{var t=Qi(),n=z(ln(t),2),r=R(n,!0);A(n),B(()=>q(r,U(s))),K(e,t)};J(i,e=>{U(s)&&e(a)}),B(()=>q(r,U(o))),K(e,t)};J(m,e=>{t.loading?e(h):t.error?e(g,1):e(_,-1)}),A(p),A(f),A(u);var v=z(u,2),y=R(v),b=z(y,2),x=e=>{var t=ea();B(e=>{Y(t,1,`${r} ${n()?i:a} flex-1 no-underline md:flex-none`),X(t,`href`,e)},[()=>Ki(`/admin/requests`)]),K(e,t)};J(b,e=>{U(c)&&e(x)});var S=z(b,2);Y(S,1,`${r} ${a} flex-1 no-underline md:flex-none`),A(v),A(l),B((e,t)=>{X(d,`href`,e),Y(y,1,`${r} ${n()===!1?i:a} flex-1 no-underline md:flex-none`),X(y,`href`,t)},[()=>Ki(`/`),()=>Ki(`/`)]),Er(`click`,S,function(...e){Oi?.apply(this,e)}),K(e,l),M()}Dr([`click`]);var ra=G(` `),ia=G(` `),aa=G(``),oa=G(`
`);function sa(e,t){We(t,!0);let n=Z(t,`table`,19,()=>({headers:[],rows:[]}));var r=oa(),i=R(r),a=R(i),o=R(a),s=R(o);Gr(s,21,()=>n().headers??[],Vr,(e,t)=>{var n=ra(),r=R(n,!0);A(n),B(()=>q(r,U(t))),K(e,n)}),A(s),A(o);var c=z(o);Gr(c,21,()=>n().rows??[],Vr,(e,t,n)=>{var r=aa();Y(r,1,ei(n%2==0?`bg-zinc-900/80`:`bg-zinc-800/90`)),Gr(r,21,()=>U(t),Vr,(e,t)=>{var n=ia(),r=R(n,!0);A(n),B(()=>{X(n,`title`,U(t)),q(r,U(t))}),K(e,n)}),A(r),K(e,r)}),A(c),A(a),A(i),A(r),K(e,r),M()}var ca=G(`
`);function $(e,t){let n=Z(t,`title`,3,`No title`),r=Z(t,`color`,3,`zinc`),i=Z(t,`code`,3,!1),a=Z(t,`value`,3,`No value`);var o=ca();Y(o,1,`rounded-md border border-zinc-700/80 bg-zinc-800/90 p-2`);var s=R(o),c=R(s,!0);A(s);var l=z(s,2),u=R(l,!0);A(l),A(o),B(()=>{q(c,n()),Y(l,1,`mt-1 text-sm text-${r()??``}-300 ${i()?`whitespace-pre-wrap`:``}`),q(u,a())}),K(e,o)}var la=G(`

`),ua=G(`
`),da=G(`
`),fa=G(``);function pa(e,t){We(t,!0);let n=Z(t,`subtitle`,3,``),r=Z(t,`targetID`,3,``),i=Z(t,`userID`,3,``),a=Z(t,`requestID`,3,``),o=Z(t,`table`,19,()=>({headers:[],rows:[]})),s=Z(t,`meta`,3,null),c=Z(t,`status`,3,``),l=Z(t,`error`,3,``);console.log(s());let u=`rounded-xl border border-zinc-700/90 bg-zinc-900/90 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl`;var d=fa(),f=R(d);Y(f,1,`${u} p-5 md:p-6`);var p=R(f),m=R(p),h=R(m),g=R(h),_=R(g,!0);A(g);var v=z(g,2),y=e=>{var t=la(),r=R(t,!0);A(t),B(()=>q(r,n())),K(e,t)};J(v,e=>{n()&&e(y)}),A(h);var b=z(h,2);Y(b,1,`inline-flex min-h-8 items-center justify-center gap-2 rounded-lg border border-lime-300 bg-lime-200 px-2.5 text-[13px] font-semibold leading-none text-zinc-900 no-underline transition-colors hover:border-lime-300 hover:bg-lime-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-lime-200 justify-center`);var x=R(b,!0);A(b),A(m);var S=z(m,2),C=R(S);$(C,{title:`ID`,get value(){return a()}});var w=z(C,2);{let e=gt(()=>c()===`failed`?`red`:`green`);$(w,{title:`Status`,get value(){return c()},get color(){return U(e)}})}var T=z(w,2);$(T,{title:`Created`,get value(){return t.createdAt}});var ee=z(T,2);$(ee,{title:`Target`,get value(){return r()}});var te=z(ee,2);$(te,{title:`User`,get value(){return i()}});var ne=z(te,2);$(ne,{title:`Rows`,get value(){return s().rows_count}});var re=z(ne,2);$(re,{title:`Columns`,get value(){return s().columns_count}});var ie=z(re,2);$(ie,{title:`Vectors`,get value(){return s().vectors_count}}),Gr(z(ie,2),17,()=>s().trace.records,Vr,(e,t)=>{{let n=gt(()=>U(t).duration/1e6);$(e,{get title(){return`${U(t).name??``} (ms)`},get value(){return U(n)}})}}),A(S);var ae=z(S,2);$(R(ae),{title:`Query`,get value(){return t.query},code:!0}),A(ae);var oe=z(ae,2),se=e=>{var t=ua(),n=R(t,!0);A(t),B(()=>q(n,l())),K(e,t)};J(oe,e=>{l()&&e(se)}),A(p),A(f);var ce=z(f,2),le=e=>{var t=da();Y(t,1,`${u} p-3`),sa(R(t),{get table(){return o()}}),A(t),K(e,t)};J(ce,e=>{l()||e(le)}),A(d),B(()=>{q(_,t.title),X(b,`href`,t.backHref),q(x,t.backLabel)}),K(e,d),M()}var ma=G(`
Loading request result...
`),ha=G(`
`);function ga(e,t){We(t,!0);let n=F(null),r=F(``),i=F(!0),a=F(``),o=`rounded-xl border border-zinc-700/90 bg-zinc-900/90 p-3 text-sm shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl`;async function s(){L(i,!0),L(r,``);try{let e=await ji(e=>Bi(e,t.requestID));if(!e)return;L(n,e,!0)}catch(e){L(r,Q(e,`Failed to load request result`),!0)}finally{L(i,!1)}}wn(()=>{t.requestID&&t.requestID!==U(a)&&(L(a,t.requestID,!0),L(n,null),s())});var c=Fr(),l=ln(c),u=e=>{var t=ma();Y(t,1,`${o} text-zinc-400`),K(e,t)},d=e=>{var t=ha();Y(t,1,`${o} border-red-500/70 bg-red-950/20 text-red-300`);var n=R(t,!0);A(t),B(()=>q(n,U(r))),K(e,t)},f=e=>{{let t=gt(()=>Ki(`/admin/requests`));pa(e,{title:`Request details`,subtitle:`Inspect the stored SQL response for an administrative request.`,get backHref(){return U(t)},backLabel:`Back to requests`,get query(){return U(n).query},get createdAt(){return U(n).created_at},get targetID(){return U(n).target_id},get userID(){return U(n).user_id},get requestID(){return U(n).id},get table(){return U(n).table},get meta(){return U(n).meta},get status(){return U(n).state},get error(){return U(n).error}})}};J(l,e=>{U(i)?e(u):U(r)?e(d,1):U(n)&&e(f,2)}),K(e,c),M()}var _a=G(`Previous`),va=G(`Next`),ya=G(`
Loading admin requests...
`),ba=G(`
`),xa=G(`
No requests yet.
`),Sa=G(`
HTML
`),Ca=G(`
`),wa=G(`
`);function Ta(e,t){We(t,!0);let n=F(0),r=F(Qt([])),i=F(``),a=F(!0),o=F(!1),s=F(!1),c=F(``),l=F(``),u=F(!1),d=null,f=!1,p=`rounded-xl border border-zinc-700/90 bg-zinc-900/90 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl`,m=`inline-flex min-h-8 items-center justify-center gap-2 rounded-lg border border-lime-300 bg-lime-200 px-2.5 text-[13px] font-semibold leading-none text-zinc-900 no-underline transition-colors hover:border-lime-300 hover:bg-lime-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-lime-200`;async function h(e=!0){if(!f){f=!0,e&&L(a,!0),L(i,``),L(s,t.page>1);try{let e=await ji(e=>zi(e,t.page));if(!e)return;L(r,e.requests??[],!0),L(o,!!e.has_next,!0),L(s,!!e.has_prev,!0)}catch(e){L(i,Q(e,`Failed to load admin requests`),!0)}finally{f=!1,e&&L(a,!1)}}}wn(()=>{t.page&&t.page!==U(n)&&(L(n,t.page,!0),h())});function g(){d&&=(window.clearInterval(d),null)}function _(e){L(u,!!e.currentTarget?.checked,!0)}wn(()=>{if(!U(u)){g();return}if(t.page>1){Ji(`/admin/requests`);return}return h(!1),g(),d=window.setInterval(()=>{h(!1)},5e3),()=>{g()}});async function v(e,t){if(!(!e||U(c)||U(l))){L(c,e,!0),L(l,t,!0),L(i,``);try{let n=await ji(n=>Vi(n,e,t));if(!n?.url)return;window.open(n.url,`_blank`,`noopener`)}catch(e){L(i,Q(e,`Failed to prepare ${t.toUpperCase()} export`),!0)}finally{L(c,``),L(l,``)}}}var y=wa(),b=R(y);Y(b,1,`${p} flex items-center justify-between gap-2 p-3`);var x=R(b),S=R(x),C=R(S);A(S);var w=z(S,2),T=R(w);oi(T),Pe(2),A(w),A(x);var ee=z(x,2),te=R(ee),ne=e=>{var n=_a();Y(n,1,ei(m)),B(e=>X(n,`href`,e),[()=>Ki(`/admin/requests`,`?page=${t.page-1}`)]),K(e,n)};J(te,e=>{U(s)&&e(ne)});var re=z(te,2),ie=e=>{var n=va();Y(n,1,ei(m)),B(e=>X(n,`href`,e),[()=>Ki(`/admin/requests`,`?page=${t.page+1}`)]),K(e,n)};J(re,e=>{U(o)&&e(ie)}),A(ee),A(b);var ae=z(b,2),oe=e=>{var t=ya();Y(t,1,`${p} p-3 text-sm text-zinc-400`),K(e,t)},se=e=>{var t=ba();Y(t,1,`${p} border-red-500/70 bg-red-950/20 p-3 text-sm text-red-300`);var n=R(t,!0);A(t),B(()=>q(n,U(i))),K(e,t)},ce=e=>{var t=xa();Y(t,1,`${p} p-3 text-sm text-zinc-400`),K(e,t)},le=e=>{var t=Ca();Gr(t,21,()=>U(r),Vr,(e,t)=>{var n=Sa();Y(n,1,`${p} flex flex-col gap-1.5 p-3`);var r=R(n),i=R(r),a=R(i);A(i);var o=z(i,2),s=R(o);Y(s,1,`${m} px-2 py-1 text-[11px]`);var l=z(s,2);Y(l,1,`${m} px-2 py-1 text-[11px] disabled:cursor-not-allowed disabled:opacity-60`);var u=z(l,2);Y(u,1,`${m} px-2 py-1 text-[11px] disabled:cursor-not-allowed disabled:opacity-60`),A(o),A(r);var d=z(r,2);Y(d,1,`rounded-lg border border-zinc-700/80 bg-zinc-800/90 mt-1 break-words p-2 text-xs text-zinc-200`);var f=R(d,!0);A(d),A(n),B((e,n,r)=>{q(a,`${U(t).created_at??``} | ${U(t).user_id??``} | ${U(t).target_id??``}`),X(s,`href`,e),l.disabled=n,u.disabled=r,q(f,U(t).query)},[()=>Ki(`/admin/requests/${U(t).id}`),()=>!!U(c),()=>!!U(c)]),Er(`click`,l,()=>v(U(t).id,`json`)),Er(`click`,u,()=>v(U(t).id,`csv`)),K(e,n)}),A(t),K(e,t)};J(ae,e=>{U(a)?e(oe):U(i)?e(se,1):U(r).length===0?e(ce,2):e(le,-1)});var ue=z(ae,2);Y(ue,1,`${p} flex items-center justify-between gap-2 p-3`);var de=R(ue),fe=R(de);A(de);var pe=z(de,2),me=R(pe),he=e=>{var n=_a();Y(n,1,ei(m)),B(e=>X(n,`href`,e),[()=>Ki(`/admin/requests`,`?page=${t.page-1}`)]),K(e,n)};J(me,e=>{U(s)&&e(he)});var ge=z(me,2),_e=e=>{var n=va();Y(n,1,ei(m)),B(e=>X(n,`href`,e),[()=>Ki(`/admin/requests`,`?page=${t.page+1}`)]),K(e,n)};J(ge,e=>{U(o)&&e(_e)}),A(pe),A(ue),A(y),B(()=>{q(C,`Page ${t.page??``}`),si(T,U(u)),q(fe,`Page ${t.page??``}`)}),Er(`change`,T,_),K(e,y),M()}Dr([`change`,`click`]);var Ea=G(`
`),Da=G(`
`),Oa=G(`
`),ka=G(`
`);function Aa(e,t){We(t,!0);let n=Z(t,`bookmarks`,19,()=>[]),r=Z(t,`emptyText`,3,`No bookmarks found.`),i=Z(t,`compact`,3,!1),a=Z(t,`runningBookmarkID`,3,``),o=Z(t,`deletingBookmarkID`,3,``),s=Z(t,`onRun`,3,()=>{}),c=Z(t,`onDelete`,3,()=>{}),l=`rounded-lg border border-zinc-700/80 bg-zinc-800/90`,u=`inline-flex min-h-8 min-w-20 items-center justify-center gap-2 rounded-lg border border-lime-300 bg-lime-200 px-2.5 text-[13px] font-semibold leading-none text-zinc-900 transition-colors hover:border-lime-300 hover:bg-lime-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-lime-200 disabled:cursor-not-allowed disabled:opacity-60`;var d=ka(),f=R(d),p=e=>{var t=Ea(),n=R(t,!0);A(t),B(()=>q(n,r())),K(e,t)},m=e=>{var t=Fr();Gr(ln(t),17,n,Vr,(e,t)=>{var n=Fr(),r=ln(n),d=e=>{var n=Da();Y(n,1,`${l} flex items-center gap-2.5 p-2.5`);var r=R(n),i=R(r,!0);A(r);var d=z(r,2),f=R(d,!0);A(d);var p=z(d,2),m=R(p);Y(m,1,ei(u));var h=R(m,!0);A(m);var g=z(m,2);Y(g,1,`${u} border-red-400 bg-red-600 text-white hover:border-red-400 hover:bg-red-600/70`);var _=R(g,!0);A(g),A(p),A(n),B(()=>{q(i,U(t).title),X(d,`title`,U(t).query),q(f,U(t).query),m.disabled=a()===U(t).id,q(h,a()===U(t).id?`Running...`:`Run`),g.disabled=o()===U(t).id,q(_,o()===U(t).id?`Deleting...`:`Delete`)}),Er(`click`,m,()=>s()(U(t))),Er(`click`,g,()=>c()(U(t))),K(e,n)},f=e=>{var n=Oa();Y(n,1,`${l} flex flex-col gap-2.5 p-2.5`);var r=R(n),i=R(r),d=R(i),f=R(d,!0);A(d);var p=z(d,2),m=R(p,!0);A(p),A(i);var h=z(i,2),g=R(h,!0);A(h),A(r);var _=z(r,2),v=R(_);Y(v,1,ei(u));var y=R(v,!0);A(v);var b=z(v,2);Y(b,1,`${u} border-red-400 bg-red-600 text-white hover:border-red-400 hover:bg-red-600/70`);var x=R(b,!0);A(b),A(_),A(n),B(()=>{q(f,U(t).title),q(m,U(t).query),q(g,U(t).target_id),v.disabled=a()===U(t).id,q(y,a()===U(t).id?`Running...`:`Run`),b.disabled=o()===U(t).id,q(x,o()===U(t).id?`Deleting...`:`Delete`)}),Er(`click`,v,()=>s()(U(t))),Er(`click`,b,()=>c()(U(t))),K(e,n)};J(r,e=>{i()?e(d):e(f,-1)}),K(e,n)}),K(e,t)};J(f,e=>{n().length===0?e(p):e(m,-1)}),A(d),K(e,d),M()}Dr([`click`]);var ja=G(`
Loading bookmarks...
`),Ma=G(`
`),Na=G(`
Saved work

Bookmarks

Keep repeatable queries close at hand and rerun them without re-entering SQL.

`);function Pa(e,t){We(t,!0);let n=Z(t,`runningBookmarkID`,3,``),r=Z(t,`deletingBookmarkID`,3,``),i=Z(t,`onRun`,3,()=>{}),a=Z(t,`onDelete`,3,()=>{});var o=Na();Y(o,1,`rounded-xl border border-zinc-700/90 bg-zinc-900/90 p-3 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl flex flex-col gap-3`);var s=R(o),c=z(R(s),2),l=R(c,!0);A(c),A(s);var u=z(s,2),d=e=>{K(e,ja())},f=e=>{var n=Ma(),r=R(n,!0);A(n),B(()=>q(r,t.error)),K(e,n)},p=e=>{Aa(e,{get bookmarks(){return t.bookmarks},emptyText:`No bookmarks found.`,get runningBookmarkID(){return n()},get deletingBookmarkID(){return r()},get onRun(){return i()},get onDelete(){return a()}})};J(u,e=>{t.loading?e(d):t.error?e(f,1):e(p,-1)}),A(o),B(()=>q(l,t.bookmarks.length)),K(e,o),M()}var Fa=G(`
Loading queries...
`),Ia=G(`
`),La=G(`
No queries found.
`),Ra=G(`
`),za=G(`
`),Ba=G(`
`),Va=G(``),Ha=G(`
`),Ua=G(`
Activity

Recent queries

Jump back into the latest executions and inspect results without repeating the request.

`);function Wa(e,t){We(t,!0);function n(e){if(!e)return`Unknown time`;let t=new Date(e);return Number.isNaN(t.getTime())?e:new Intl.DateTimeFormat(void 0,{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`}).format(t)}var r=Ua();Y(r,1,`rounded-xl border border-zinc-700/90 bg-zinc-900/90 p-3 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl flex flex-col gap-3`);var i=R(r),a=z(R(i),2),o=R(a,!0);A(a),A(i);var s=z(i,2),c=e=>{K(e,Fa())},l=e=>{var n=Ia(),r=R(n,!0);A(n),B(()=>q(r,t.error)),K(e,n)},u=e=>{K(e,La())},d=e=>{var r=Ha();Gr(r,21,()=>t.queries,Vr,(e,t)=>{var r=Va();Y(r,1,`rounded-lg border border-zinc-700/80 bg-zinc-800/90 flex flex-col gap-2.5 p-2.5`);var i=R(r),a=R(i),o=R(a),s=R(o,!0);A(o);var c=z(o,2),l=e=>{var n=Ra(),r=R(n,!0);A(n),B(()=>q(r,U(t).state)),K(e,n)},u=e=>{var n=za(),r=R(n,!0);A(n),B(()=>q(r,U(t).state)),K(e,n)},d=e=>{var n=Ba(),r=R(n);A(n),B(()=>q(r,`unknown - ${U(t).state??``}`)),K(e,n)};J(c,e=>{U(t).state===`completed`?e(l):U(t).state===`failed`?e(u,1):e(d,-1)});var f=z(c,2),p=R(f,!0);A(f),A(a);var m=z(a,2);Y(m,1,`inline-flex min-h-8 items-center justify-center gap-2 rounded-lg border border-lime-300 bg-lime-200 px-2.5 text-[13px] font-semibold leading-none text-zinc-900 no-underline transition-colors hover:border-lime-300 hover:bg-lime-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-lime-200 min-w-[72px]`),A(i);var h=z(i,2),g=R(h,!0);A(h),A(r),B((e,n)=>{q(s,e),q(p,U(t).target_id),X(m,`href`,n),q(g,U(t).query)},[()=>n(U(t).created_at),()=>Ki(`/servers/${U(t).target_id}/${U(t).id}`)]),K(e,r)}),A(r),K(e,r)};J(s,e=>{t.loading?e(c):t.error?e(l,1):t.queries.length===0?e(u,2):e(d,-1)}),A(r),B(()=>q(o,t.queries.length)),K(e,r),M()}var Ga=G(`
Loading servers...
`),Ka=G(`
`),qa=G(`
No servers found.
`),Ja=G(`
`),Ya=G(`
`),Xa=G(`
`),Za=G(`
Exposed tables
`),Qa=G(`
Tables
Fields
`),$a=G(`
`),eo=G(`
Targets

Available servers

Browse approved data sources and inspect the tables exposed for safe querying.

`);function to(e,t){We(t,!0);var n=eo();Y(n,1,`rounded-xl border border-zinc-700/90 bg-zinc-900/90 p-3 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl flex flex-col gap-3`);var r=R(n),i=z(R(r),2),a=R(i,!0);A(i),A(r);var o=z(r,2),s=e=>{K(e,Ga())},c=e=>{var n=Ka(),r=R(n,!0);A(n),B(()=>q(r,t.error)),K(e,n)},l=e=>{K(e,qa())},u=e=>{var n=$a();Gr(n,21,()=>t.servers,Vr,(e,t)=>{var n=Qa(),r=R(n);Y(r,1,`rounded-lg border border-zinc-700/80 bg-zinc-800/90 flex flex-col gap-3 p-3 transition-colors hover:border-zinc-500 hover:bg-zinc-800`);var i=R(r),a=R(i),o=R(a),s=R(o),c=R(s),l=R(c,!0);A(c);var u=z(c,2),d=R(u,!0);A(u),A(s);var f=z(s,2),p=R(f,!0);A(f),A(o),A(a);var m=z(a,2),h=e=>{var n=Ya();Gr(n,21,()=>U(t).Tags??[],Vr,(e,t)=>{var n=Ja(),r=R(n,!0);A(n),B(()=>q(r,U(t).Name)),K(e,n)}),A(n),K(e,n)};J(m,e=>{(U(t).Tags??[]).length>0&&e(h)}),A(i);var g=z(i,2),_=R(g),v=z(R(_),2),y=R(v,!0);A(v),A(_);var b=z(_,2),x=z(R(b),2),S=R(x,!0);A(x),A(b),A(g);var C=z(g,2),w=e=>{var n=Za(),r=z(R(n),2);Gr(r,21,()=>U(t).Tables??[],Vr,(e,t)=>{var n=Xa(),r=R(n),i=R(r,!0);A(r);var a=z(r,2),o=R(a),s=e=>{var n=Pr();B(e=>q(n,e),[()=>U(t).fields.join(`, `)]),K(e,n)},c=e=>{K(e,Pr(`No exposed fields`))};J(o,e=>{(U(t).fields??[]).length>0?e(s):e(c,-1)}),A(a),A(n),B(()=>q(i,U(t).table)),K(e,n)}),A(r),A(n),K(e,n)};J(C,e=>{(U(t).Tables??[]).length>0&&e(w)}),A(r),A(n),B((e,r)=>{X(n,`href`,e),q(l,U(t).ID),q(d,U(t).Type),q(p,U(t).Description||`No description provided.`),q(y,U(t).Tables?.length??0),q(S,r)},[()=>Ki(`/servers/${U(t).ID}`),()=>(U(t).Tables??[]).reduce((e,t)=>e+(t.fields?.length??0),0)]),K(e,n)}),A(n),K(e,n)};J(o,e=>{t.loading?e(s):t.error?e(c,1):t.servers.length===0?e(l,2):e(u,-1)}),A(n),B(()=>q(a,t.servers.length)),K(e,n),M()}var no=G(`
Servers
Bookmarks
Schema fields
Recent queries
`);function ro(e,t){We(t,!1);let n=I([]),r=I(``),i=I(!0),a=I([]),o=I(``),s=I(!0),c=I(``),l=I(``),u=I([]),d=I(``),f=I(!0),p=`rounded-lg border border-zinc-700/80 bg-zinc-800/90`;function m(e,t){return`${e} ${t}`}function h(){return U(n).reduce((e,t)=>e+(t.Tables??[]).reduce((e,t)=>e+(t.fields?.length??0),0),0)}function g(e,t,n,r){return e?`Loading ${r}`:t||(n===0?`No ${r} available`:m(n,r))}function _(e){L(r,e),L(o,e),L(d,e),L(i,!1),L(s,!1),L(f,!1)}function v(e,t,n,r,i){e.status===`fulfilled`?t(e.value):n(Q(e.reason,i)),r()}async function y(){let e;try{e=await ji(async e=>Promise.allSettled([Mi(e),Fi(e),Ii(e)]))}catch(e){_(Q(e,`Failed to load dashboard`));return}e&&(v(e[0],e=>{L(n,e.targets??[]),L(r,``)},e=>{L(r,e)},()=>{L(i,!1)},`Failed to load servers`),v(e[1],e=>{L(a,e.bookmarks??[]),L(o,``)},e=>{L(o,e)},()=>{L(s,!1)},`Failed to load bookmarks`),v(e[2],e=>{L(u,e.queries??[]),L(d,``)},e=>{L(d,e)},()=>{L(f,!1)},`Failed to load queries`))}async function b(e){L(c,e.id);try{let t=await ji(t=>Li(t,e.target_id,e.query));if(!t)return;L(o,``),Ji(`/servers/${e.target_id}/${t.query_id}`)}catch(e){L(o,Q(e,`Failed to run bookmark`))}finally{L(c,``)}}async function x(e){L(l,e.id);try{let t=await ji(t=>Ui(t,e.id));if(!t&&t!==void 0)return;L(a,U(a).filter(t=>t.id!==e.id)),L(o,``)}catch(e){L(o,Q(e,`Failed to delete bookmark`))}finally{L(l,``)}}gi(y),mi();var S=no();Zr(`1eem88v`,e=>{On(()=>{en.title=`Database Gateway`})});var C=R(S),w=R(C),T=R(w);to(R(T),{get loading(){return U(i)},get error(){return U(r)},get servers(){return U(n)}}),A(T);var ee=z(T,2);Pa(R(ee),{get loading(){return U(s)},get error(){return U(o)},get bookmarks(){return U(a)},get runningBookmarkID(){return U(c)},get deletingBookmarkID(){return U(l)},onRun:b,onDelete:x}),A(ee),A(w);var te=z(w,2);Wa(R(te),{get loading(){return U(f)},get error(){return U(d)},get queries(){return U(u)}}),A(te),A(C);var ne=z(C,2);Y(ne,1,`rounded-xl border border-zinc-700/90 bg-zinc-900/90 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl grid gap-3.5 p-3.5 xl:grid-cols-[minmax(0,1.3fr)_minmax(0,1fr)]`);var re=R(ne),ie=R(re);Y(ie,1,`${p} p-2.5`);var ae=z(R(ie),2),oe=R(ae,!0);A(ae);var se=z(ae,2),ce=R(se,!0);A(se),A(ie);var le=z(ie,2);Y(le,1,`${p} p-2.5`);var ue=z(R(le),2),de=R(ue,!0);A(ue);var fe=z(ue,2),pe=R(fe,!0);A(fe),A(le);var me=z(le,2);Y(me,1,`${p} p-2.5`);var he=z(R(me),2),ge=R(he,!0);A(he);var _e=z(he,2),ve=R(_e);A(_e),A(me);var ye=z(me,2);Y(ye,1,`${p} p-2.5`);var be=z(R(ye),2),xe=R(be,!0);A(be);var Se=z(be,2),Ce=R(Se,!0);A(Se),A(ye),A(re),A(ne),A(S),B((e,t,r,i)=>{q(oe,U(n).length),q(ce,e),q(de,U(a).length),q(pe,t),q(ge,r),q(ve,`Across ${U(n).length??``} configured targets`),q(xe,U(u).length),q(Ce,i)},[()=>g(U(i),U(r),U(n).length,`servers`),()=>g(U(s),U(o),U(a).length,`bookmarks`),()=>h(),()=>g(U(f),U(d),U(u).length,`recent queries`)]),K(e,S),M()}var io=G(`
Error 404
Route unavailable

This page does not exist in Database Gateway.

The address you opened is not mapped to an active view. It may be outdated, incomplete, or outside the - routes exposed by this deployment.

Requested path
`);function ao(e,t){We(t,!0);let n=Z(t,`pathname`,3,`/`),r=`rounded-xl border border-zinc-700/90 bg-zinc-900/90 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl`,i=`rounded-lg border border-zinc-700/80 bg-zinc-800/90`;var a=io(),o=R(a),s=R(o);Y(s,1,`${r} p-6 md:p-8`);var c=R(s),l=R(c),u=R(l);Y(R(u),1,`${i} px-2 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-zinc-300`),Pe(2),A(u),Pe(2),A(l);var d=z(l,2);Y(d,1,`${i} p-4`);var f=z(R(d),2),p=R(f,!0);A(f),A(d);var m=z(d,2),h=R(m);Y(h,1,`inline-flex min-h-8 items-center justify-center gap-2 rounded-lg border border-lime-300 bg-lime-200 px-2.5 text-[13px] font-semibold leading-none text-zinc-900 no-underline transition-colors hover:border-lime-300 hover:bg-lime-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-lime-200 justify-center`),Pe(2),A(m),A(c),A(s),Y(z(s,2),1,`${r} p-5 md:p-6`),A(o),A(a),B(e=>{q(p,n()),X(h,`href`,e)},[()=>Ki(`/`)]),K(e,a),M()}var oo=G(`
Loading query result...
`),so=G(`
`);function co(e,t){We(t,!0);let n=F(null),r=F(``),i=F(!0),a=F(``),o=`rounded-xl border border-zinc-700/90 bg-zinc-900/90 p-3 text-sm shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl`;async function s(){L(i,!0),L(r,``);try{let e=await ji(e=>Bi(e,t.queryID));if(!e)return;L(n,e,!0)}catch(e){L(r,Q(e,`Failed to load query result`),!0)}finally{L(i,!1)}}wn(()=>{t.queryID&&t.queryID!==U(a)&&(L(a,t.queryID,!0),L(n,null),s())});var c=Fr(),l=ln(c),u=e=>{var t=oo();Y(t,1,`${o} text-zinc-400`),K(e,t)},d=e=>{var t=so();Y(t,1,`${o} border-red-500/70 bg-red-950/20 text-red-300`);var n=R(t,!0);A(t),B(()=>q(n,U(r))),K(e,t)},f=e=>{{let r=gt(()=>Ki(`/`));pa(e,{title:`Query results`,subtitle:`Review a previously executed query without returning to the server page.`,get backHref(){return U(r)},backLabel:`Back to dashboard`,get query(){return U(n).query},get createdAt(){return U(n).created_at},get queryID(){return t.queryID},get table(){return U(n).table},get meta(){return U(n).meta},get status(){return U(n).state},get error(){return U(n).error}})}};J(l,e=>{U(i)?e(u):U(r)?e(d,1):U(n)&&e(f,2)}),K(e,c),M()}var lo=G(`
Loading server...
`),uo=G(`
`),fo=G(`
`),po=G(`
`),mo=G(`
`),ho=G(`
Saved bookmarks
Show
`),go=G(`
`),_o=G(`
`),vo=G(`
`),yo=G(`
`),bo=G(`
Exposed tables
Show
`);function xo(e,t){We(t,!0);let n=Z(t,`queryID`,3,``),r=Z(t,`initialQuery`,3,``),i=Z(t,`autoRun`,3,!1),a=F(null),o=F(Qt([])),s=F(``),c=F(``),l=F(null),u=F(!0),d=F(!1),f=F(!1),p=F(``),m=F(``),h=F(``),g=F(``),_=F(``),v=F(``),y=F(``),b=F(``),x=`rounded-xl border border-zinc-700/90 bg-zinc-900/90 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl`,S=`rounded-lg border border-zinc-700/80 bg-zinc-800/90`,C=`inline-flex min-h-8 items-center justify-center gap-2 rounded-lg border border-lime-300 bg-lime-200 px-2.5 text-[13px] font-semibold leading-none text-zinc-900 transition-colors hover:border-lime-300 hover:bg-lime-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-lime-200 disabled:cursor-not-allowed disabled:opacity-60`,w=`w-full rounded-md border border-zinc-600 bg-zinc-800 px-2 py-1.5 text-zinc-100 placeholder:text-zinc-400 focus:outline-none focus:ring-2 focus:ring-lime-200`;async function T(){let e=await ji(e=>Fi(e,t.serverID));return e?(L(o,e.bookmarks??[],!0),!0):!1}async function ee(){L(u,!0),L(g,``);try{let e=await ji(e=>Promise.all([Ni(e,t.serverID),Fi(e,t.serverID)]));if(!e)return;let[i,c]=e;if(L(a,i.target??null,!0),L(o,c.bookmarks??[],!0),!U(a)){L(g,`Server ${t.serverID} was not found`);return}if(n()){let e=await ji(e=>Bi(e,n()));if(!e)return;L(l,e,!0),L(s,e.query,!0)}else L(l,null),L(s,r()||``,!0)}catch(e){L(g,Q(e,`Failed to load server page`),!0)}finally{L(u,!1)}}function te(e){return`select ${(e.fields??[]).join(`, `)} - from ${e.table} limit 10`}function ne(e){return`select count(*) - from ${e.table}`}async function re(e=U(s)){let t=e.trim();if(!U(a)||!t){L(_,`Query is required`);return}L(d,!0),L(_,``),L(s,t,!0);try{let e=await ji(async e=>Li(e,U(a).ID,t));if(!e)return;Ji(`/servers/${U(a).ID}/${e.query_id}`)}catch(e){L(_,Q(e,`Failed to run query`),!0)}finally{L(d,!1)}}async function ie(){if(!U(a)||!U(s).trim()){L(v,`Run or enter a query before saving a bookmark`);return}L(f,!0),L(v,``);try{await ji(e=>Hi(e,U(a).ID,U(c).trim(),U(s).trim())),L(c,``),await T()}catch(e){L(v,Q(e,`Failed to save bookmark`),!0)}finally{L(f,!1)}}async function ae(e){L(m,e,!0),L(v,``);try{await ji(t=>Ui(t,e)),await T()}catch(e){L(v,Q(e,`Failed to delete bookmark`),!0)}finally{L(m,``)}}async function oe(e){L(p,e.id,!0);try{await re(e.query)}finally{L(p,``)}}function se(e){e.key===`Enter`&&e.shiftKey&&(e.preventDefault(),re())}async function ce(e){if(!(!U(l)?.id||U(h))){L(h,e,!0),L(_,``);try{let t=await ji(t=>Vi(t,U(l).id,e));if(!t?.url)return;window.location.assign(t.url)}catch(t){L(_,Q(t,`Failed to prepare ${e.toUpperCase()} export`),!0)}finally{L(h,``)}}}wn(()=>{let e=`${t.serverID}:${n()}:${r()}:${i()}`;t.serverID&&e!==U(y)&&(L(y,e),L(a,null),L(o,[],!0),L(s,n()?``:r()||``,!0),L(c,``),L(l,null),L(_,``),L(v,``),L(b,``),ee())}),wn(()=>{let e=`${t.serverID}:${n()}:${r()}:${i()}`;!n()&&U(a)&&i()&&r().trim()&&U(b)!==e&&!U(d)&&(L(b,e),re(r()))});var le=Fr(),ue=ln(le),de=e=>{var t=lo();Y(t,1,`${x} p-3 text-sm text-zinc-400`),K(e,t)},fe=e=>{var t=uo();Y(t,1,`${x} border-red-500/70 bg-red-950/20 p-3 text-sm text-red-300`);var n=R(t,!0);A(t),B(()=>q(n,U(g))),K(e,t)},pe=e=>{var t=bo(),n=R(t);Y(n,1,`${x} w-full p-3`);var r=R(n),i=R(r),u=R(i),g=R(u),y=R(g,!0);A(g);var b=z(g,2),T=R(b,!0);A(b);var ee=z(b,2),le=e=>{var t=fo(),n=R(t,!0);A(t),B(()=>q(n,U(a).Description)),K(e,t)};J(ee,e=>{U(a).Description&&e(le)});var ue=z(ee,2);Gr(ue,21,()=>U(a).Tags??[],Vr,(e,t)=>{var n=po(),r=R(n,!0);A(n),B(()=>q(r,U(t).Name)),K(e,n)}),A(ue),A(u);var de=z(u,2);Y(de,1,`${S} overflow-hidden`);var fe=R(de),pe=R(fe),me=z(R(pe),2),he=R(me);A(me),A(pe),Pe(2),A(fe);var ge=z(fe,2),_e=R(ge);Gr(_e,21,()=>U(a).Tables??[],Vr,(e,t)=>{var n=mo();Y(n,1,`${S} flex flex-wrap items-center justify-between gap-2 p-2.5`);var r=R(n),i=R(r),a=R(i,!0);A(i),Gr(z(i,2),17,()=>U(t).fields??[],Vr,(e,n,r)=>{var i=fo(),a=R(i);A(i),B(()=>q(a,`${U(n)??``}${r<(U(t).fields?.length??0)-1?`,`:``}`)),K(e,i)}),A(r);var o=z(r,2),s=R(o);Y(s,1,`${C} px-2 py-1 text-xs`);var c=z(s,2);Y(c,1,`${C} px-2 py-1 text-xs`),A(o),A(n),B(()=>q(a,U(t).table)),Er(`click`,s,()=>re(te(U(t)))),Er(`click`,c,()=>re(ne(U(t)))),K(e,n)}),A(_e),A(ge),A(de);var ve=z(de,2),ye=e=>{var t=ho();Y(t,1,`${S} overflow-hidden`);var n=R(t),r=R(n),i=z(R(r),2),a=R(i);A(i),A(r),Pe(2),A(n);var s=z(n,2);Aa(R(s),{get bookmarks(){return U(o)},compact:!0,emptyText:`No bookmarks yet`,get runningBookmarkID(){return U(p)},get deletingBookmarkID(){return U(m)},onRun:oe,onDelete:e=>ae(e.id)}),A(s),A(t),B(()=>q(a,`${U(o).length??``} ${U(o).length===1?`bookmark`:`bookmarks`}`)),K(e,t)};J(ve,e=>{U(o).length>0&&e(ye)}),A(i),A(r),A(n);var be=z(n,2);Y(be,1,`${x} w-full p-3`);var xe=R(be),Se=R(xe);mn(Se),Y(Se,1,`${w} h-24`);var Ce=z(Se,2),we=R(Ce);Y(we,1,ei(C));var Te=R(we,!0);A(we);var Ee=z(we,2);Y(Ee,1,ei(C));var De=R(Ee,!0);A(Ee);var E=z(Ee,2);Y(E,1,ei(C));var Oe=R(E,!0);A(E);var ke=z(E,4);oi(ke),Y(ke,1,`${w} w-full md:ml-auto md:w-[200px] md:min-w-[200px] md:flex-none`);var Ae=z(ke,2);Y(Ae,1,`${C} whitespace-nowrap`);var je=R(Ae,!0);A(Ae),A(Ce);var D=z(Ce,2),Me=e=>{var t=go(),n=R(t,!0);A(t),B(()=>q(n,U(_))),K(e,t)};J(D,e=>{U(_)&&e(Me)});var O=z(D,2),k=e=>{var t=_o(),n=R(t,!0);A(t),B(()=>q(n,U(v))),K(e,t)};J(O,e=>{U(v)&&e(k)}),A(xe),A(be);var Ne=z(be,2),Fe=e=>{var t=yo();Y(t,1,`${x} w-full p-3`);var n=R(t),r=R(n),i=R(r);$(i,{title:`ID`,get value(){return U(l).id}});var a=z(i,2);{let e=gt(()=>U(l).state===`failed`?`red`:`green`);$(a,{title:`Status`,get value(){return U(l).state},get color(){return U(e)}})}var o=z(a,2);$(o,{title:`Created`,get value(){return U(l).created_at}});var s=z(o,2);$(s,{title:`Target`,get value(){return U(l).target_id}});var c=z(s,2);$(c,{title:`User`,get value(){return U(l).user_id}});var u=z(c,2);$(u,{title:`Rows`,get value(){return U(l).meta.rows_count}});var d=z(u,2);$(d,{title:`Columns`,get value(){return U(l).meta.columns_count}});var f=z(d,2);$(f,{title:`Vectors`,get value(){return U(l).meta.vectors_count}}),Gr(z(f,2),17,()=>U(l).meta.trace.records,Vr,(e,t)=>{{let n=gt(()=>U(t).duration/1e6);$(e,{get title(){return`${U(t).name??``} (ms)`},get value(){return U(n)}})}}),A(r);var p=z(r,2),m=e=>{var t=vo(),n=R(t,!0);A(t),B(()=>q(n,U(l).error)),K(e,t)},h=e=>{sa(e,{get table(){return U(l).table}})};J(p,e=>{U(l).error?e(m):e(h,-1)}),A(n),A(t),K(e,t)};J(Ne,e=>{U(l)&&e(Fe)}),A(t),B((e,t)=>{q(y,U(a).Type),q(T,U(a).ID),q(he,`${U(a).Tables?.length??0??``} ${(U(a).Tables?.length??0)===1?`table`:`tables`}`),we.disabled=U(d),q(Te,U(d)?`Running...`:`Run (Shift + Enter)`),Ee.disabled=e,q(De,U(h)===`json`?`Preparing JSON...`:`Get JSON`),E.disabled=t,q(Oe,U(h)===`csv`?`Preparing CSV...`:`Get CSV`),Ae.disabled=U(f),q(je,U(f)?`Saving...`:`Save bookmark`)},[()=>!U(l)||!!U(h),()=>!U(l)||!!U(h)]),Er(`keydown`,Se,se),di(Se,()=>U(s),e=>L(s,e)),Er(`click`,we,()=>re()),Er(`click`,Ee,()=>ce(`json`)),Er(`click`,E,()=>ce(`csv`)),di(ke,()=>U(c),e=>L(c,e)),Er(`click`,Ae,ie),K(e,t)};J(ue,e=>{U(u)?e(de):U(g)?e(fe,1):U(a)&&e(pe,2)}),K(e,le),M()}Dr([`click`,`keydown`]);var So=G(`
`);function Co(e,t){We(t,!1);let n=I(),r=I(),i=I(),a=I(),o=I(),s=I({}),c=I(``),l=I(!0),u=I({pathname:`/`,searchParams:new URLSearchParams});function d(e){let t=e&&typeof e==`object`?e:{},n=t.id??t.ID??``;return{id:n,username:t.username??t.Username??n,role:t.role??t.Role??``}}function f(){L(u,qi())}async function p(){try{let e=await ji(e=>Pi(e));if(!e)return;L(s,d(e)),L(c,``)}catch(e){L(c,Q(e,`Failed to load profile`))}finally{L(l,!1)}}gi(()=>{ki(),f(),p();let e=()=>{f()};return window.addEventListener(`popstate`,e),()=>{window.removeEventListener(`popstate`,e)}}),kn(()=>U(u),()=>{L(n,Math.max(1,Number.parseInt(U(u).searchParams.get(`page`)||`1`,10)||1))}),kn(()=>U(u),()=>{L(r,Yi(U(u).pathname,`/admin/requests/:requestID`))}),kn(()=>U(u),()=>{L(i,Yi(U(u).pathname,`/servers/:serverID/:queryID`))}),kn(()=>U(u),()=>{L(a,Yi(U(u).pathname,`/queries/:queryID`))}),kn(()=>U(u),()=>{L(o,Yi(U(u).pathname,`/servers/:serverID`))}),An(),mi();var m=So(),h=z(R(m),2),g=R(h);{let e=_t(()=>(U(u),W(()=>U(u).pathname.startsWith(`/admin/requests`))));na(g,{get loading(){return U(l)},get error(){return U(c)},get profile(){return U(s)},get isAdminRequestsPage(){return U(e)}})}var _=z(g,2),v=R(_),y=e=>{ga(e,{get requestID(){return U(r),W(()=>U(r).requestID)}})},b=e=>{Ta(e,{get page(){return U(n)}})},x=e=>{{let t=_t(()=>(U(u),W(()=>U(u).searchParams.get(`query`)||``))),n=_t(()=>(U(u),W(()=>U(u).searchParams.get(`autorun`)===`1`)));xo(e,{get serverID(){return U(i),W(()=>U(i).serverID)},get queryID(){return U(i),W(()=>U(i).queryID)},get initialQuery(){return U(t)},get autoRun(){return U(n)}})}},S=e=>{{let t=_t(()=>(U(u),W(()=>U(u).searchParams.get(`query`)||``))),n=_t(()=>(U(u),W(()=>U(u).searchParams.get(`autorun`)===`1`)));xo(e,{get serverID(){return U(o),W(()=>U(o).serverID)},get initialQuery(){return U(t)},get autoRun(){return U(n)}})}},C=e=>{co(e,{get queryID(){return U(a),W(()=>U(a).queryID)}})},w=e=>{ro(e,{})},T=e=>{ao(e,{get pathname(){return U(u),W(()=>U(u).pathname)}})};J(v,e=>{U(r)?e(y):(U(u),W(()=>U(u).pathname===`/admin/requests`)?e(b,1):U(i)?e(x,2):U(o)?e(S,3):U(a)?e(C,4):(U(u),W(()=>U(u).pathname===`/`)?e(w,5):e(T,-1)))}),A(_),A(h),A(m),K(e,m),M()}Ir(Co,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/internal/facade/ui/dist/assets/index-Bck9yC3-.css b/internal/facade/ui/dist/assets/index-ClHUA4RO.css similarity index 60% rename from internal/facade/ui/dist/assets/index-Bck9yC3-.css rename to internal/facade/ui/dist/assets/index-ClHUA4RO.css index b6f4f95..aa84ec0 100644 --- a/internal/facade/ui/dist/assets/index-Bck9yC3-.css +++ b/internal/facade/ui/dist/assets/index-ClHUA4RO.css @@ -17,4 +17,4 @@ */ /*! tailwindcss v4.3.1 | MIT License | https://tailwindcss.com */ -@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-outline-style:solid}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-950:oklch(25.8% .092 26.042);--color-lime-200:oklch(93.8% .127 124.321);--color-lime-300:oklch(89.7% .196 126.665);--color-green-300:oklch(87.1% .15 154.449);--color-slate-700:oklch(37.2% .044 257.287);--color-slate-900:oklch(20.8% .042 265.755);--color-zinc-100:oklch(96.7% .001 286.375);--color-zinc-200:oklch(92% .004 286.32);--color-zinc-300:oklch(87.1% .006 286.286);--color-zinc-400:oklch(70.5% .015 286.067);--color-zinc-500:oklch(55.2% .016 285.938);--color-zinc-600:oklch(44.2% .017 285.786);--color-zinc-700:oklch(37% .013 285.805);--color-zinc-800:oklch(27.4% .006 286.033);--color-zinc-900:oklch(21% .006 285.885);--color-zinc-950:oklch(14.1% .005 285.823);--color-white:#fff;--spacing:.25rem;--container-2xl:42rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--tracking-wide:.025em;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--blur-xl:24px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.visible{visibility:visible}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.top-0{top:0}.z-10{z-index:10}.m-0{margin:0}.mx-1\.5{margin-inline:calc(var(--spacing) * 1.5)}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.table{display:table}.h-4{height:calc(var(--spacing) * 4)}.h-9{height:calc(var(--spacing) * 9)}.h-24{height:calc(var(--spacing) * 24)}.min-h-7{min-height:calc(var(--spacing) * 7)}.min-h-8{min-height:calc(var(--spacing) * 8)}.min-h-24{min-height:calc(var(--spacing) * 24)}.min-h-\[calc\(100vh-88px\)\]{min-height:calc(100vh - 88px)}.min-h-screen{min-height:100vh}.w-4{width:calc(var(--spacing) * 4)}.w-9{width:calc(var(--spacing) * 9)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.min-w-0{min-width:0}.min-w-7{min-width:calc(var(--spacing) * 7)}.min-w-20{min-width:calc(var(--spacing) * 20)}.min-w-\[72px\]{min-width:72px}.min-w-full{min-width:100%}.flex-1{flex:1}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-start{justify-content:flex-start}.justify-stretch{justify-content:stretch}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-3\.5{gap:calc(var(--spacing) * 3.5)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-8{gap:calc(var(--spacing) * 8)}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-zinc-800\/90>:not(:last-child)){border-color:#27272ae6}@supports (color:color-mix(in lab, red, red)){:where(.divide-zinc-800\/90>:not(:last-child)){border-color:color-mix(in oklab, var(--color-zinc-800) 90%, transparent)}}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-lime-300{border-color:var(--color-lime-300)}.border-red-400{border-color:var(--color-red-400)}.border-red-500\/70{border-color:#fb2c36b3}@supports (color:color-mix(in lab, red, red)){.border-red-500\/70{border-color:color-mix(in oklab, var(--color-red-500) 70%, transparent)}}.border-slate-700\/40{border-color:#31415866}@supports (color:color-mix(in lab, red, red)){.border-slate-700\/40{border-color:color-mix(in oklab, var(--color-slate-700) 40%, transparent)}}.border-zinc-600{border-color:var(--color-zinc-600)}.border-zinc-700{border-color:var(--color-zinc-700)}.border-zinc-700\/70{border-color:#3f3f46b3}@supports (color:color-mix(in lab, red, red)){.border-zinc-700\/70{border-color:color-mix(in oklab, var(--color-zinc-700) 70%, transparent)}}.border-zinc-700\/80{border-color:#3f3f46cc}@supports (color:color-mix(in lab, red, red)){.border-zinc-700\/80{border-color:color-mix(in oklab, var(--color-zinc-700) 80%, transparent)}}.border-zinc-700\/90{border-color:#3f3f46e6}@supports (color:color-mix(in lab, red, red)){.border-zinc-700\/90{border-color:color-mix(in oklab, var(--color-zinc-700) 90%, transparent)}}.border-zinc-800{border-color:var(--color-zinc-800)}.bg-lime-200{background-color:var(--color-lime-200)}.bg-lime-300{background-color:var(--color-lime-300)}.bg-red-600{background-color:var(--color-red-600)}.bg-red-950\/20{background-color:#46080933}@supports (color:color-mix(in lab, red, red)){.bg-red-950\/20{background-color:color-mix(in oklab, var(--color-red-950) 20%, transparent)}}.bg-red-950\/30{background-color:#4608094d}@supports (color:color-mix(in lab, red, red)){.bg-red-950\/30{background-color:color-mix(in oklab, var(--color-red-950) 30%, transparent)}}.bg-red-950\/50{background-color:#46080980}@supports (color:color-mix(in lab, red, red)){.bg-red-950\/50{background-color:color-mix(in oklab, var(--color-red-950) 50%, transparent)}}.bg-slate-900\/75{background-color:#0f172bbf}@supports (color:color-mix(in lab, red, red)){.bg-slate-900\/75{background-color:color-mix(in oklab, var(--color-slate-900) 75%, transparent)}}.bg-zinc-800{background-color:var(--color-zinc-800)}.bg-zinc-800\/70{background-color:#27272ab3}@supports (color:color-mix(in lab, red, red)){.bg-zinc-800\/70{background-color:color-mix(in oklab, var(--color-zinc-800) 70%, transparent)}}.bg-zinc-800\/80{background-color:#27272acc}@supports (color:color-mix(in lab, red, red)){.bg-zinc-800\/80{background-color:color-mix(in oklab, var(--color-zinc-800) 80%, transparent)}}.bg-zinc-800\/90{background-color:#27272ae6}@supports (color:color-mix(in lab, red, red)){.bg-zinc-800\/90{background-color:color-mix(in oklab, var(--color-zinc-800) 90%, transparent)}}.bg-zinc-900{background-color:var(--color-zinc-900)}.bg-zinc-900\/80{background-color:#18181bcc}@supports (color:color-mix(in lab, red, red)){.bg-zinc-900\/80{background-color:color-mix(in oklab, var(--color-zinc-900) 80%, transparent)}}.bg-zinc-900\/90{background-color:#18181be6}@supports (color:color-mix(in lab, red, red)){.bg-zinc-900\/90{background-color:color-mix(in oklab, var(--color-zinc-900) 90%, transparent)}}.bg-zinc-950{background-color:var(--color-zinc-950)}.bg-\[radial-gradient\(circle_at_10\%_12\%\,rgba\(113\,113\,122\,0\.16\)\,transparent_26\%\)\,radial-gradient\(circle_at_86\%_18\%\,rgba\(24\,24\,27\,0\.22\)\,transparent_28\%\)\,linear-gradient\(180deg\,rgba\(39\,39\,42\,1\)_0\%\,rgba\(24\,24\,27\,1\)_100\%\)\]{background-image:radial-gradient(circle at 10% 12%,#71717a29,#0000 26%),radial-gradient(circle at 86% 18%,#18181b38,#0000 28%),linear-gradient(#27272a 0%,#18181b 100%)}.p-2{padding:calc(var(--spacing) * 2)}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-3{padding:calc(var(--spacing) * 3)}.p-3\.5{padding:calc(var(--spacing) * 3.5)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.pt-2{padding-top:calc(var(--spacing) * 2)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[13px\]{font-size:13px}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-6{--tw-leading:calc(var(--spacing) * 6);line-height:calc(var(--spacing) * 6)}.leading-none{--tw-leading:1;line-height:1}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[-0\.01em\]{--tw-tracking:-.01em;letter-spacing:-.01em}.tracking-\[-0\.02em\]{--tw-tracking:-.02em;letter-spacing:-.02em}.tracking-\[-0\.03em\]{--tw-tracking:-.03em;letter-spacing:-.03em}.tracking-\[-0\.04em\]{--tw-tracking:-.04em;letter-spacing:-.04em}.tracking-\[0\.04em\]{--tw-tracking:.04em;letter-spacing:.04em}.tracking-\[0\.08em\]{--tw-tracking:.08em;letter-spacing:.08em}.tracking-\[0\.12em\]{--tw-tracking:.12em;letter-spacing:.12em}.tracking-\[0\.16em\]{--tw-tracking:.16em;letter-spacing:.16em}.tracking-\[0\.18em\]{--tw-tracking:.18em;letter-spacing:.18em}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-green-300{color:var(--color-green-300)}.text-inherit{color:inherit}.text-lime-200{color:var(--color-lime-200)}.text-lime-300{color:var(--color-lime-300)}.text-red-200{color:var(--color-red-200)}.text-red-300{color:var(--color-red-300)}.text-white{color:var(--color-white)}.text-zinc-100{color:var(--color-zinc-100)}.text-zinc-200{color:var(--color-zinc-200)}.text-zinc-300{color:var(--color-zinc-300)}.text-zinc-400{color:var(--color-zinc-400)}.text-zinc-500{color:var(--color-zinc-500)}.text-zinc-600{color:var(--color-zinc-600)}.text-zinc-900{color:var(--color-zinc-900)}.text-zinc-950{color:var(--color-zinc-950)}.lowercase{text-transform:lowercase}.uppercase{text-transform:uppercase}.no-underline{text-decoration-line:none}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.shadow-\[0_18px_42px_rgb\(0_0_0_\/_0\.28\)\]{--tw-shadow:0 18px 42px var(--tw-shadow-color,#00000047);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-zinc-700{--tw-ring-color:var(--color-zinc-700)}.ring-zinc-700\/70{--tw-ring-color:#3f3f46b3}@supports (color:color-mix(in lab, red, red)){.ring-zinc-700\/70{--tw-ring-color:color-mix(in oklab, var(--color-zinc-700) 70%, transparent)}}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur-xl{--tw-backdrop-blur:blur(var(--blur-xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ring-inset{--tw-ring-inset:inset}.marker\:hidden ::marker{display:none}.marker\:hidden::marker{display:none}.marker\:hidden ::-webkit-details-marker{display:none}.marker\:hidden::-webkit-details-marker{display:none}.placeholder\:text-zinc-400::placeholder{color:var(--color-zinc-400)}.first\:pt-0:first-child{padding-top:0}.last\:pb-0:last-child{padding-bottom:0}@media (hover:hover){.hover\:border-lime-300:hover{border-color:var(--color-lime-300)}.hover\:border-red-400:hover{border-color:var(--color-red-400)}.hover\:border-zinc-500:hover{border-color:var(--color-zinc-500)}.hover\:bg-lime-300:hover{background-color:var(--color-lime-300)}.hover\:bg-red-600\/70:hover{background-color:#e40014b3}@supports (color:color-mix(in lab, red, red)){.hover\:bg-red-600\/70:hover{background-color:color-mix(in oklab, var(--color-red-600) 70%, transparent)}}.hover\:bg-zinc-700\/70:hover{background-color:#3f3f46b3}@supports (color:color-mix(in lab, red, red)){.hover\:bg-zinc-700\/70:hover{background-color:color-mix(in oklab, var(--color-zinc-700) 70%, transparent)}}.hover\:bg-zinc-700\/90:hover{background-color:#3f3f46e6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-zinc-700\/90:hover{background-color:color-mix(in oklab, var(--color-zinc-700) 90%, transparent)}}.hover\:bg-zinc-800:hover{background-color:var(--color-zinc-800)}.hover\:text-white:hover{color:var(--color-white)}}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-lime-200:focus{--tw-ring-color:var(--color-lime-200)}.focus\:ring-lime-300:focus{--tw-ring-color:var(--color-lime-300)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:outline:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:outline-offset-2:focus-visible{outline-offset:2px}.focus-visible\:outline-lime-200:focus-visible{outline-color:var(--color-lime-200)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-60:disabled{opacity:.6}@media (width>=40rem){.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}}@media (width>=48rem){.md\:ml-auto{margin-left:auto}.md\:block{display:block}.md\:w-\[200px\]{width:200px}.md\:min-w-\[200px\]{min-width:200px}.md\:flex-none{flex:none}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}.md\:items-start{align-items:flex-start}.md\:justify-between{justify-content:space-between}.md\:justify-end{justify-content:flex-end}.md\:p-6{padding:calc(var(--spacing) * 6)}.md\:p-8{padding:calc(var(--spacing) * 8)}.md\:px-3{padding-inline:calc(var(--spacing) * 3)}.md\:py-2{padding-block:calc(var(--spacing) * 2)}.md\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}}@media (width>=64rem){.lg\:grid-cols-\[minmax\(0\,1\.7fr\)_minmax\(280px\,0\.9fr\)\]{grid-template-columns:minmax(0,1.7fr) minmax(280px,.9fr)}}@media (width>=80rem){.xl\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.xl\:grid-cols-\[minmax\(0\,1\.2fr\)_minmax\(0\,0\.9fr\)\]{grid-template-columns:minmax(0,1.2fr) minmax(0,.9fr)}.xl\:grid-cols-\[minmax\(0\,1\.3fr\)_minmax\(0\,1fr\)\]{grid-template-columns:minmax(0,1.3fr) minmax(0,1fr)}.xl\:grid-cols-\[minmax\(0\,1\.75fr\)_minmax\(320px\,0\.9fr\)\]{grid-template-columns:minmax(0,1.75fr) minmax(320px,.9fr)}}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid} +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-outline-style:solid}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-200:oklch(88.5% .062 18.334);--color-red-300:oklch(80.8% .114 19.571);--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-red-950:oklch(25.8% .092 26.042);--color-lime-200:oklch(93.8% .127 124.321);--color-lime-300:oklch(89.7% .196 126.665);--color-green-300:oklch(87.1% .15 154.449);--color-slate-700:oklch(37.2% .044 257.287);--color-slate-900:oklch(20.8% .042 265.755);--color-zinc-100:oklch(96.7% .001 286.375);--color-zinc-200:oklch(92% .004 286.32);--color-zinc-300:oklch(87.1% .006 286.286);--color-zinc-400:oklch(70.5% .015 286.067);--color-zinc-500:oklch(55.2% .016 285.938);--color-zinc-600:oklch(44.2% .017 285.786);--color-zinc-700:oklch(37% .013 285.805);--color-zinc-800:oklch(27.4% .006 286.033);--color-zinc-900:oklch(21% .006 285.885);--color-zinc-950:oklch(14.1% .005 285.823);--color-white:#fff;--spacing:.25rem;--container-2xl:42rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--font-weight-extrabold:800;--tracking-wide:.025em;--radius-md:.375rem;--radius-lg:.5rem;--radius-xl:.75rem;--blur-xl:24px;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.top-0{top:0}.z-10{z-index:10}.m-0{margin:0}.mx-1\.5{margin-inline:calc(var(--spacing) * 1.5)}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-1{margin-top:var(--spacing)}.mt-1\.5{margin-top:calc(var(--spacing) * 1.5)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.block{display:block}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline-flex{display:inline-flex}.table{display:table}.h-4{height:calc(var(--spacing) * 4)}.h-9{height:calc(var(--spacing) * 9)}.h-24{height:calc(var(--spacing) * 24)}.min-h-7{min-height:calc(var(--spacing) * 7)}.min-h-8{min-height:calc(var(--spacing) * 8)}.min-h-24{min-height:calc(var(--spacing) * 24)}.min-h-\[calc\(100vh-88px\)\]{min-height:calc(100vh - 88px)}.min-h-screen{min-height:100vh}.w-4{width:calc(var(--spacing) * 4)}.w-9{width:calc(var(--spacing) * 9)}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.min-w-0{min-width:0}.min-w-7{min-width:calc(var(--spacing) * 7)}.min-w-20{min-width:calc(var(--spacing) * 20)}.min-w-\[72px\]{min-width:72px}.min-w-full{min-width:100%}.flex-1{flex:1}.shrink-0{flex-shrink:0}.border-collapse{border-collapse:collapse}.cursor-pointer{cursor:pointer}.list-none{list-style-type:none}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-start{justify-content:flex-start}.justify-stretch{justify-content:stretch}.gap-0\.5{gap:calc(var(--spacing) * .5)}.gap-1{gap:var(--spacing)}.gap-1\.5{gap:calc(var(--spacing) * 1.5)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-2\.5{gap:calc(var(--spacing) * 2.5)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-3\.5{gap:calc(var(--spacing) * 3.5)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-8{gap:calc(var(--spacing) * 8)}:where(.space-y-3>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}:where(.divide-y>:not(:last-child)){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}:where(.divide-zinc-800\/90>:not(:last-child)){border-color:#27272ae6}@supports (color:color-mix(in lab, red, red)){:where(.divide-zinc-800\/90>:not(:last-child)){border-color:color-mix(in oklab, var(--color-zinc-800) 90%, transparent)}}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.rounded{border-radius:.25rem}.rounded-full{border-radius:3.40282e38px}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.rounded-xl{border-radius:var(--radius-xl)}.border{border-style:var(--tw-border-style);border-width:1px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-lime-300{border-color:var(--color-lime-300)}.border-red-400{border-color:var(--color-red-400)}.border-red-500\/70{border-color:#fb2c36b3}@supports (color:color-mix(in lab, red, red)){.border-red-500\/70{border-color:color-mix(in oklab, var(--color-red-500) 70%, transparent)}}.border-slate-700\/40{border-color:#31415866}@supports (color:color-mix(in lab, red, red)){.border-slate-700\/40{border-color:color-mix(in oklab, var(--color-slate-700) 40%, transparent)}}.border-zinc-600{border-color:var(--color-zinc-600)}.border-zinc-700{border-color:var(--color-zinc-700)}.border-zinc-700\/70{border-color:#3f3f46b3}@supports (color:color-mix(in lab, red, red)){.border-zinc-700\/70{border-color:color-mix(in oklab, var(--color-zinc-700) 70%, transparent)}}.border-zinc-700\/80{border-color:#3f3f46cc}@supports (color:color-mix(in lab, red, red)){.border-zinc-700\/80{border-color:color-mix(in oklab, var(--color-zinc-700) 80%, transparent)}}.border-zinc-700\/90{border-color:#3f3f46e6}@supports (color:color-mix(in lab, red, red)){.border-zinc-700\/90{border-color:color-mix(in oklab, var(--color-zinc-700) 90%, transparent)}}.border-zinc-800{border-color:var(--color-zinc-800)}.bg-lime-200{background-color:var(--color-lime-200)}.bg-lime-300{background-color:var(--color-lime-300)}.bg-red-600{background-color:var(--color-red-600)}.bg-red-950\/20{background-color:#46080933}@supports (color:color-mix(in lab, red, red)){.bg-red-950\/20{background-color:color-mix(in oklab, var(--color-red-950) 20%, transparent)}}.bg-red-950\/30{background-color:#4608094d}@supports (color:color-mix(in lab, red, red)){.bg-red-950\/30{background-color:color-mix(in oklab, var(--color-red-950) 30%, transparent)}}.bg-red-950\/50{background-color:#46080980}@supports (color:color-mix(in lab, red, red)){.bg-red-950\/50{background-color:color-mix(in oklab, var(--color-red-950) 50%, transparent)}}.bg-slate-900\/75{background-color:#0f172bbf}@supports (color:color-mix(in lab, red, red)){.bg-slate-900\/75{background-color:color-mix(in oklab, var(--color-slate-900) 75%, transparent)}}.bg-zinc-800{background-color:var(--color-zinc-800)}.bg-zinc-800\/70{background-color:#27272ab3}@supports (color:color-mix(in lab, red, red)){.bg-zinc-800\/70{background-color:color-mix(in oklab, var(--color-zinc-800) 70%, transparent)}}.bg-zinc-800\/80{background-color:#27272acc}@supports (color:color-mix(in lab, red, red)){.bg-zinc-800\/80{background-color:color-mix(in oklab, var(--color-zinc-800) 80%, transparent)}}.bg-zinc-800\/90{background-color:#27272ae6}@supports (color:color-mix(in lab, red, red)){.bg-zinc-800\/90{background-color:color-mix(in oklab, var(--color-zinc-800) 90%, transparent)}}.bg-zinc-900{background-color:var(--color-zinc-900)}.bg-zinc-900\/80{background-color:#18181bcc}@supports (color:color-mix(in lab, red, red)){.bg-zinc-900\/80{background-color:color-mix(in oklab, var(--color-zinc-900) 80%, transparent)}}.bg-zinc-900\/90{background-color:#18181be6}@supports (color:color-mix(in lab, red, red)){.bg-zinc-900\/90{background-color:color-mix(in oklab, var(--color-zinc-900) 90%, transparent)}}.bg-zinc-950{background-color:var(--color-zinc-950)}.bg-\[radial-gradient\(circle_at_10\%_12\%\,rgba\(113\,113\,122\,0\.16\)\,transparent_26\%\)\,radial-gradient\(circle_at_86\%_18\%\,rgba\(24\,24\,27\,0\.22\)\,transparent_28\%\)\,linear-gradient\(180deg\,rgba\(39\,39\,42\,1\)_0\%\,rgba\(24\,24\,27\,1\)_100\%\)\]{background-image:radial-gradient(circle at 10% 12%,#71717a29,#0000 26%),radial-gradient(circle at 86% 18%,#18181b38,#0000 28%),linear-gradient(#27272a 0%,#18181b 100%)}.p-2{padding:calc(var(--spacing) * 2)}.p-2\.5{padding:calc(var(--spacing) * 2.5)}.p-3{padding:calc(var(--spacing) * 3)}.p-3\.5{padding:calc(var(--spacing) * 3.5)}.p-4{padding:calc(var(--spacing) * 4)}.p-5{padding:calc(var(--spacing) * 5)}.p-6{padding:calc(var(--spacing) * 6)}.px-1\.5{padding-inline:calc(var(--spacing) * 1.5)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-2\.5{padding-inline:calc(var(--spacing) * 2.5)}.px-3{padding-inline:calc(var(--spacing) * 3)}.py-0\.5{padding-block:calc(var(--spacing) * .5)}.py-1{padding-block:var(--spacing)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2{padding-block:calc(var(--spacing) * 2)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.pt-2{padding-top:calc(var(--spacing) * 2)}.text-center{text-align:center}.text-left{text-align:left}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[13px\]{font-size:13px}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-6{--tw-leading:calc(var(--spacing) * 6);line-height:calc(var(--spacing) * 6)}.leading-none{--tw-leading:1;line-height:1}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-extrabold{--tw-font-weight:var(--font-weight-extrabold);font-weight:var(--font-weight-extrabold)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-\[-0\.01em\]{--tw-tracking:-.01em;letter-spacing:-.01em}.tracking-\[-0\.02em\]{--tw-tracking:-.02em;letter-spacing:-.02em}.tracking-\[-0\.03em\]{--tw-tracking:-.03em;letter-spacing:-.03em}.tracking-\[-0\.04em\]{--tw-tracking:-.04em;letter-spacing:-.04em}.tracking-\[0\.04em\]{--tw-tracking:.04em;letter-spacing:.04em}.tracking-\[0\.08em\]{--tw-tracking:.08em;letter-spacing:.08em}.tracking-\[0\.12em\]{--tw-tracking:.12em;letter-spacing:.12em}.tracking-\[0\.16em\]{--tw-tracking:.16em;letter-spacing:.16em}.tracking-\[0\.18em\]{--tw-tracking:.18em;letter-spacing:.18em}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.break-words{overflow-wrap:break-word}.break-all{word-break:break-all}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.text-green-300{color:var(--color-green-300)}.text-inherit{color:inherit}.text-lime-200{color:var(--color-lime-200)}.text-lime-300{color:var(--color-lime-300)}.text-red-200{color:var(--color-red-200)}.text-red-300{color:var(--color-red-300)}.text-white{color:var(--color-white)}.text-zinc-100{color:var(--color-zinc-100)}.text-zinc-200{color:var(--color-zinc-200)}.text-zinc-300{color:var(--color-zinc-300)}.text-zinc-400{color:var(--color-zinc-400)}.text-zinc-500{color:var(--color-zinc-500)}.text-zinc-600{color:var(--color-zinc-600)}.text-zinc-900{color:var(--color-zinc-900)}.text-zinc-950{color:var(--color-zinc-950)}.uppercase{text-transform:uppercase}.no-underline{text-decoration-line:none}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.shadow-\[0_18px_42px_rgb\(0_0_0_\/_0\.28\)\]{--tw-shadow:0 18px 42px var(--tw-shadow-color,#00000047);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-1{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring-zinc-700{--tw-ring-color:var(--color-zinc-700)}.ring-zinc-700\/70{--tw-ring-color:#3f3f46b3}@supports (color:color-mix(in lab, red, red)){.ring-zinc-700\/70{--tw-ring-color:color-mix(in oklab, var(--color-zinc-700) 70%, transparent)}}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.backdrop-blur-xl{--tw-backdrop-blur:blur(var(--blur-xl));-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.ring-inset{--tw-ring-inset:inset}.marker\:hidden ::marker{display:none}.marker\:hidden::marker{display:none}.marker\:hidden ::-webkit-details-marker{display:none}.marker\:hidden::-webkit-details-marker{display:none}.placeholder\:text-zinc-400::placeholder{color:var(--color-zinc-400)}.first\:pt-0:first-child{padding-top:0}.last\:pb-0:last-child{padding-bottom:0}@media (hover:hover){.hover\:border-lime-300:hover{border-color:var(--color-lime-300)}.hover\:border-red-400:hover{border-color:var(--color-red-400)}.hover\:border-zinc-500:hover{border-color:var(--color-zinc-500)}.hover\:bg-lime-300:hover{background-color:var(--color-lime-300)}.hover\:bg-red-600\/70:hover{background-color:#e40014b3}@supports (color:color-mix(in lab, red, red)){.hover\:bg-red-600\/70:hover{background-color:color-mix(in oklab, var(--color-red-600) 70%, transparent)}}.hover\:bg-zinc-700\/70:hover{background-color:#3f3f46b3}@supports (color:color-mix(in lab, red, red)){.hover\:bg-zinc-700\/70:hover{background-color:color-mix(in oklab, var(--color-zinc-700) 70%, transparent)}}.hover\:bg-zinc-700\/90:hover{background-color:#3f3f46e6}@supports (color:color-mix(in lab, red, red)){.hover\:bg-zinc-700\/90:hover{background-color:color-mix(in oklab, var(--color-zinc-700) 90%, transparent)}}.hover\:bg-zinc-800:hover{background-color:var(--color-zinc-800)}.hover\:text-white:hover{color:var(--color-white)}}.focus\:ring-2:focus{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.focus\:ring-lime-200:focus{--tw-ring-color:var(--color-lime-200)}.focus\:ring-lime-300:focus{--tw-ring-color:var(--color-lime-300)}.focus\:outline-none:focus{--tw-outline-style:none;outline-style:none}.focus-visible\:outline:focus-visible{outline-style:var(--tw-outline-style);outline-width:1px}.focus-visible\:outline-2:focus-visible{outline-style:var(--tw-outline-style);outline-width:2px}.focus-visible\:outline-offset-2:focus-visible{outline-offset:2px}.focus-visible\:outline-lime-200:focus-visible{outline-color:var(--color-lime-200)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-60:disabled{opacity:.6}@media (width>=40rem){.sm\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}}@media (width>=48rem){.md\:ml-auto{margin-left:auto}.md\:block{display:block}.md\:w-\[200px\]{width:200px}.md\:min-w-\[200px\]{min-width:200px}.md\:flex-none{flex:none}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}.md\:items-start{align-items:flex-start}.md\:justify-between{justify-content:space-between}.md\:justify-end{justify-content:flex-end}.md\:p-6{padding:calc(var(--spacing) * 6)}.md\:p-8{padding:calc(var(--spacing) * 8)}.md\:px-3{padding-inline:calc(var(--spacing) * 3)}.md\:py-2{padding-block:calc(var(--spacing) * 2)}.md\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}}@media (width>=64rem){.lg\:grid-cols-\[minmax\(0\,1\.7fr\)_minmax\(280px\,0\.9fr\)\]{grid-template-columns:minmax(0,1.7fr) minmax(280px,.9fr)}}@media (width>=80rem){.xl\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.xl\:grid-cols-\[minmax\(0\,1\.2fr\)_minmax\(0\,0\.9fr\)\]{grid-template-columns:minmax(0,1.2fr) minmax(0,.9fr)}.xl\:grid-cols-\[minmax\(0\,1\.3fr\)_minmax\(0\,1fr\)\]{grid-template-columns:minmax(0,1.3fr) minmax(0,1fr)}.xl\:grid-cols-\[minmax\(0\,1\.75fr\)_minmax\(320px\,0\.9fr\)\]{grid-template-columns:minmax(0,1.75fr) minmax(320px,.9fr)}}}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-divide-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid} diff --git a/internal/facade/ui/dist/assets/index-CwZNRYB8.js b/internal/facade/ui/dist/assets/index-CwZNRYB8.js new file mode 100644 index 0000000..fc5c236 --- /dev/null +++ b/internal/facade/ui/dist/assets/index-CwZNRYB8.js @@ -0,0 +1,24 @@ +/** + * Database Gateway provides access to servers with ACL for safe and restricted database interactions. + * Copyright (C) 2024 Kirill Zhuravlev + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +(function(){let e=document.createElement(`link`).relList;if(e&&e.supports&&e.supports(`modulepreload`))return;for(let e of document.querySelectorAll(`link[rel="modulepreload"]`))n(e);new MutationObserver(e=>{for(let t of e)if(t.type===`childList`)for(let e of t.addedNodes)e.tagName===`LINK`&&e.rel===`modulepreload`&&n(e)}).observe(document,{childList:!0,subtree:!0});function t(e){let t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin===`use-credentials`?t.credentials=`include`:e.crossOrigin===`anonymous`?t.credentials=`omit`:t.credentials=`same-origin`,t}function n(e){if(e.ep)return;e.ep=!0;let n=t(e);fetch(e.href,n)}})();var e=Array.isArray,t=Array.prototype.indexOf,n=Array.prototype.includes,r=Array.from,i=Object.defineProperty,a=Object.getOwnPropertyDescriptor,o=Object.getOwnPropertyDescriptors,s=Object.prototype,c=Array.prototype,l=Object.getPrototypeOf,u=Object.isExtensible,d=()=>{};function f(e){return e()}function p(e){for(var t=0;t{e=n,t=r}),resolve:e,reject:t}}var h=1024,g=2048,_=4096,v=8192,y=16384,b=32768,x=1<<25,S=65536,C=1<<18,w=1<<19,T=1<<20,ee=1<<25,te=65536,ne=1<<21,re=1<<22,ie=1<<23,ae=Symbol(`$state`),oe=Symbol(`legacy props`),se=Symbol(``),ce=Symbol(`attributes`),le=Symbol(`class`),ue=Symbol(`style`),de=Symbol(`text`),fe=Symbol(`form reset`),pe=new class extends Error{name=`StaleReactionError`;message="The reaction that called `getAbortSignal()` was re-run or destroyed"},me=!!globalThis.document?.contentType&&globalThis.document.contentType.includes(`xml`);function he(e){throw Error(`https://svelte.dev/e/lifecycle_outside_component`)}function ge(){throw Error(`https://svelte.dev/e/async_derived_orphan`)}function _e(e,t,n){throw Error(`https://svelte.dev/e/each_key_duplicate`)}function ve(e){throw Error(`https://svelte.dev/e/effect_in_teardown`)}function ye(){throw Error(`https://svelte.dev/e/effect_in_unowned_derived`)}function be(e){throw Error(`https://svelte.dev/e/effect_orphan`)}function xe(){throw Error(`https://svelte.dev/e/effect_update_depth_exceeded`)}function Se(e){throw Error(`https://svelte.dev/e/props_invalid_value`)}function Ce(){throw Error(`https://svelte.dev/e/state_descriptors_fixed`)}function we(){throw Error(`https://svelte.dev/e/state_prototype_fixed`)}function Te(){throw Error(`https://svelte.dev/e/state_unsafe_mutation`)}function Ee(){throw Error(`https://svelte.dev/e/svelte_boundary_reset_onerror`)}var De={},E=Symbol(`uninitialized`),Oe=`http://www.w3.org/1999/xhtml`;function ke(){console.warn(`https://svelte.dev/e/derived_inert`)}function Ae(e){console.warn(`https://svelte.dev/e/hydration_mismatch`)}function je(){console.warn(`https://svelte.dev/e/svelte_boundary_reset_noop`)}var D=!1;function Me(e){D=e}var O;function k(e){if(e===null)throw Ae(),De;return O=e}function Ne(){return k(ln(O))}function A(e){if(D){if(ln(O)!==null)throw Ae(),De;O=e}}function Pe(e=1){if(D){for(var t=e,n=O;t--;)n=ln(n);O=n}}function Fe(e=!0){for(var t=0,n=O;;){if(n.nodeType===8){var r=n.data;if(r===`]`){if(t===0)return n;--t}else (r===`[`||r===`[!`||r[0]===`[`&&!isNaN(Number(r.slice(1))))&&(t+=1)}var i=ln(n);e&&n.remove(),n=i}}function Ie(e){if(!e||e.nodeType!==8)throw Ae(),De;return e.data}function Le(e){return e===this.v}function Re(e,t){return e==e?e!==t||typeof e==`object`&&!!e||typeof e==`function`:t==t}function ze(e){return!Re(e,this.v)}var Be=!1,Ve=!1;function He(){Ve=!0}var j=null;function Ue(e){j=e}function We(e,t=!1,n){j={p:j,i:!1,c:null,e:null,s:e,x:null,r:V,l:Ve&&!t?{s:null,u:null,$:[]}:null}}function Ge(e){var t=j,n=t.e;if(n!==null){t.e=null;for(var r of n)En(r)}return e!==void 0&&(t.x=e),t.i=!0,j=t.p,e??{}}function Ke(){return!Ve||j!==null&&j.l===null}var qe=[];function Je(){var e=qe;qe=[],p(e)}function Ye(e){if(qe.length===0&&!kt){var t=qe;queueMicrotask(()=>{t===qe&&Je()})}qe.push(e)}function Xe(){for(;qe.length>0;)Je()}function Ze(e){var t=V;if(t===null)return B.f|=ie,e;if(!(t.f&32768)&&!(t.f&4))throw e;Qe(e,t)}function Qe(e,t){if(!(t!==null&&t.f&16384)){for(;t!==null;){if(t.f&128){if(!(t.f&32768))throw e;try{t.b.error(e);return}catch(t){e=t}}t=t.parent}throw e}}var $e=~(g|_|h);function M(e,t){e.f=e.f&$e|t}function et(e){e.f&512||e.deps===null?M(e,h):M(e,_)}function tt(e){if(e!==null)for(let t of e)!(t.f&2)||!(t.f&65536)||(t.f^=te,tt(t.deps))}function nt(e,t,n){e.f&2048?t.add(e):e.f&4096&&n.add(e),tt(e.deps),M(e,h)}var rt=!1,it=!1;function at(e){var t=it;try{return it=!1,[e(),it]}finally{it=t}}function ot(e){let t=0,n=Jt(0),r;return()=>{Cn()&&(H(n),Nn(()=>(t===0&&(r=U(()=>e(()=>Zt(n)))),t+=1,()=>{Ye(()=>{--t,t===0&&(r?.(),r=void 0,Zt(n))})})))}}var st=S|w;function ct(e,t,n,r){new lt(e,t,n,r)}var lt=class{parent;is_pending=!1;transform_error;#e;#t=D?O:null;#n;#r;#i;#a=null;#o=null;#s=null;#c=null;#l=0;#u=0;#d=!1;#f=new Set;#p=new Set;#m=null;#h=ot(()=>(this.#m=Jt(this.#l),()=>{this.#m=null}));constructor(e,t,n,r){this.#e=e,this.#n=t,this.#r=e=>{var t=V;t.b=this,t.f|=128,n(e)},this.parent=V.b,this.transform_error=r??this.parent?.transform_error??(e=>e),this.#i=Pn(()=>{if(D){let e=this.#t;Ne();let t=e.data===`[!`;if(e.data.startsWith(`[?`)){let t=JSON.parse(e.data.slice(2));this.#_(t)}else t?this.#v():this.#g()}else this.#y()},st),D&&(this.#e=O)}#g(){try{this.#a=Fn(()=>this.#r(this.#e))}catch(e){this.error(e)}}#_(e){let t=this.#n.failed;t&&(this.#s=Fn(()=>{t(this.#e,()=>e,()=>()=>{})}))}#v(){let e=this.#n.pending;e&&(this.is_pending=!0,this.#o=Fn(()=>e(this.#e)),Ye(()=>{var e=this.#c=document.createDocumentFragment(),t=sn();e.append(t),this.#a=this.#x(()=>Fn(()=>this.#r(t))),this.#u===0&&(this.#e.before(e),this.#c=null,Hn(this.#o,()=>{this.#o=null}),this.#b(N))}))}#y(){try{if(this.is_pending=this.has_pending_snippet(),this.#u=0,this.#l=0,this.#a=Fn(()=>{this.#r(this.#e)}),this.#u>0){var e=this.#c=document.createDocumentFragment();Kn(this.#a,e);let t=this.#n.pending;this.#o=Fn(()=>t(this.#e))}else this.#b(N)}catch(e){this.error(e)}}#b(e){this.is_pending=!1,e.transfer_effects(this.#f,this.#p)}defer_effect(e){nt(e,this.#f,this.#p)}is_rendered(){return!this.is_pending&&(!this.parent||this.parent.is_rendered())}has_pending_snippet(){return!!this.#n.pending}#x(e){var t=V,n=B,r=j;$n(this.#i),Qn(this.#i),Ue(this.#i.ctx);try{return Ft.ensure(),e()}catch(e){return Ze(e),null}finally{$n(t),Qn(n),Ue(r)}}#S(e,t){if(!this.has_pending_snippet()){this.parent&&this.parent.#S(e,t);return}this.#u+=e,this.#u===0&&(this.#b(t),this.#o&&Hn(this.#o,()=>{this.#o=null}),this.#c&&=(this.#e.before(this.#c),null))}update_pending_count(e,t){this.#S(e,t),this.#l+=e,!(!this.#m||this.#d)&&(this.#d=!0,Ye(()=>{this.#d=!1,this.#m&&Yt(this.#m,this.#l)}))}get_effect_pending(){return this.#h(),H(this.#m)}error(e){if(!this.#n.onerror&&!this.#n.failed)throw e;N?.is_fork?(this.#a&&N.skip_effect(this.#a),this.#o&&N.skip_effect(this.#o),this.#s&&N.skip_effect(this.#s),N.oncommit(()=>{this.#C(e)})):this.#C(e)}#C(e){this.#a&&=(zn(this.#a),null),this.#o&&=(zn(this.#o),null),this.#s&&=(zn(this.#s),null),D&&(k(this.#t),Pe(),k(Fe()));var t=this.#n.onerror;let n=this.#n.failed;var r=!1,i=!1;let a=()=>{if(r){je();return}r=!0,i&&Ee(),this.#s!==null&&Hn(this.#s,()=>{this.#s=null}),this.#x(()=>{this.#y()})},o=e=>{try{i=!0,t?.(e,a),i=!1}catch(e){Qe(e,this.#i&&this.#i.parent)}n&&(this.#s=this.#x(()=>{try{return Fn(()=>{var t=V;t.b=this,t.f|=128,n(this.#e,()=>e,()=>a)})}catch(e){return Qe(e,this.#i.parent),null}}))};Ye(()=>{var t;try{t=this.transform_error(e)}catch(e){Qe(e,this.#i&&this.#i.parent);return}typeof t==`object`&&t&&typeof t.then==`function`?t.then(o,e=>Qe(e,this.#i&&this.#i.parent)):o(t)})}};function ut(e,t,n,r){let i=Ke()?mt:vt;var a=e.filter(e=>!e.settled),o=t.map(i);if(n.length===0&&a.length===0){r(o);return}var s=V,c=dt(),l=a.length===1?a[0].promise:a.length>1?Promise.all(a.map(e=>e.promise)):null;function u(e){if(!(s.f&16384)){c();try{r([...o,...e])}catch(e){Qe(e,s)}ft()}}var d=pt();if(n.length===0){l.then(()=>u([])).finally(d);return}function f(){Promise.all(n.map(e=>gt(e))).then(u).catch(e=>Qe(e,s)).finally(d)}l?l.then(()=>{c(),f(),ft()}):f()}function dt(){var e=V,t=B,n=j,r=N;return function(i=!0){$n(e),Qn(t),Ue(n),i&&!(e.f&16384)&&(r?.activate(),r?.apply())}}function ft(e=!0){$n(null),Qn(null),Ue(null),e&&N?.deactivate()}function pt(){var e=V,t=e.b,n=N,r=!!t?.is_rendered();return t?.update_pending_count(1,n),n.increment(r,e),()=>{t?.update_pending_count(-1,n),n.decrement(r,e)}}function mt(e){var t=2|g;return V!==null&&(V.f|=w),{ctx:j,deps:null,effects:null,equals:Le,f:t,fn:e,reactions:null,rv:0,v:E,wv:0,parent:V,ac:null}}var ht=Symbol(`obsolete`);function gt(e,t,n){let r=V;r===null&&ge();var i=void 0,a=Jt(E),o=!B,s=new Set;return Mn(()=>{var t=V,n=m();i=n.promise;try{Promise.resolve(e()).then(n.resolve,e=>{e!==pe&&n.reject(e)}).finally(ft)}catch(e){n.reject(e),ft()}var c=N;if(o){if(t.f&32768)var l=pt();if(r.b?.is_rendered())c.async_deriveds.get(t)?.reject(ht);else for(let e of s.values())e.reject(ht);s.add(n),c.async_deriveds.set(t,n)}let u=(e,t=void 0)=>{l?.(),s.delete(n),t!==ht&&(c.activate(),t?(a.f|=ie,Yt(a,t)):(a.f&8388608&&(a.f^=ie),Yt(a,e)),c.deactivate())};n.promise.then(u,e=>u(null,e||`unknown`))}),wn(()=>{for(let e of s)e.reject(ht)}),new Promise(e=>{function t(n){function r(){n===i?e(a):t(i)}n.then(r,r)}t(i)})}function _t(e){let t=mt(e);return Be||tr(t),t}function vt(e){let t=mt(e);return t.equals=ze,t}function yt(e){var t=e.effects;if(t!==null){e.effects=null;for(var n=0;nthis.schedule(e)){var n=this.#f.get(e);if(n){this.#f.delete(e);for(var r of n.d)M(r,g),t(r);for(r of n.m)M(r,_),t(r)}this.#p.add(e)}#g(){this.#e=!0,Nt++>1e3&&(this.#S(),Lt());for(let e of this.#u)this.#d.delete(e),M(e,g),this.schedule(e);for(let e of this.#d)M(e,_),this.schedule(e);let t=this.#c;this.#c=[],this.apply();var n=jt=[],r=[],i=Mt=[];for(let e of t)try{this.#_(e,n,r)}catch(t){throw Wt(e),this.#h()||this.discard(),t}if(N=null,i.length>0){var a=e.ensure();for(let e of i)a.schedule(e)}if(jt=null,Mt=null,this.#h()){this.#b(r),this.#b(n);for(let[e,t]of this.#f)Ut(e,t);i.length>0&&N.#g();return}let o=this.#v();if(o){this.#b(r),this.#b(n),o.#y(this);return}this.#u.clear(),this.#d.clear();for(let e of this.#r)e(this);this.#r.clear(),Et=this,zt(r),zt(n),Et=null,this.#s?.resolve();var s=N;if(this.#a===0&&(this.#c.length===0||s!==null)&&(this.#S(),Be&&(this.#x(),N=s)),this.#c.length>0)if(s!==null){let e=s;e.#c.push(...this.#c.filter(t=>!e.#c.includes(t)))}else s=this;s!==null&&s.#g()}#_(e,t,n){e.f^=h;for(var r=e.first;r!==null;){var i=r.f,a=(i&96)!=0;if(!(a&&i&1024||i&8192||this.#f.has(r))&&r.fn!==null){a?r.f^=h:i&4?t.push(r):Be&&i&16777224?n.push(r):dr(r)&&(i&16&&this.#d.add(r),gr(r));var o=r.first;if(o!==null){r=o;continue}}for(;r!==null;){var s=r.next;if(s!==null){r=s;break}r=r.parent}}}#v(){for(var e=this.#t;e!==null;){if(!e.is_fork){for(let[t,[,n]]of this.current)if(e.current.has(t)&&!n)return e}e=e.#t}return null}#y(e){for(let[t,n]of e.current)!this.previous.has(t)&&e.previous.has(t)&&this.previous.set(t,e.previous.get(t)),this.current.set(t,n);for(let[t,n]of e.async_deriveds){let e=this.async_deriveds.get(t);e&&n.promise.then(e.resolve).catch(e.reject)}e.async_deriveds.clear(),this.transfer_effects(e.#u,e.#d);let t=e=>{var n=e.reactions;if(n!==null)for(let e of n){var r=e.f;if(r&2)t(e);else{var i=e;r&4194320&&!this.async_deriveds.has(i)&&(this.#d.delete(i),M(i,g),this.schedule(i))}}};for(let e of this.current.keys())t(e);this.oncommit(()=>e.discard()),e.#S(),N=this,this.#g()}#b(e){for(var t=0;t!u.current.get(e)[1]);if(!(!u.#e||r.length===0)){var i=r.filter(e=>!this.current.has(e));if(i.length===0)e&&u.discard();else if(t.length>0){if(e)for(let e of this.#p)u.unskip_effect(e,e=>{e.f&4194320?u.schedule(e):u.#b([e])});u.activate();var a=new Set,o=new Map;for(var s of t)Bt(s,i,a,o);o=new Map;var c=[...u.current].filter(([e,t])=>{let n=this.current.get(e);return n?n[0]!==t[0]||n[1]!==t[1]:!0}).map(([e])=>e);if(c.length>0)for(let e of this.#l)!(e.f&155648)&&Vt(e,c,o)&&(e.f&4194320?(M(e,g),u.schedule(e)):u.#u.add(e));if(u.#c.length>0&&!u.#m){u.apply();for(var l of u.#c)u.#_(l,[],[]);u.#c=[]}u.deactivate()}}}}increment(e,t){if(this.#a+=1,e){let e=this.#o.get(t)??0;this.#o.set(t,e+1)}}decrement(e,t){if(--this.#a,e){let e=this.#o.get(t)??0;e===1?this.#o.delete(t):this.#o.set(t,e-1)}this.#m||(this.#m=!0,Ye(()=>{this.#m=!1,this.linked&&this.flush()}))}transfer_effects(e,t){for(let t of e)this.#u.add(t);for(let e of t)this.#d.add(e);e.clear(),t.clear()}oncommit(e){this.#r.add(e)}ondiscard(e){this.#i.add(e)}settled(){return(this.#s??=m()).promise}static ensure(){if(N===null){let t=N=new e;!At&&!kt&&Ye(()=>{t.#e||t.flush()})}return N}apply(){if(!Be||!this.is_fork&&this.#t===null&&this.#n===null){Dt=null;return}Dt=new Map;for(let[e,[t]]of this.current)Dt.set(e,t);for(let t=wt;t!==null;t=t.#n)if(!(t===this||t.is_fork)){var e=!1;if(t.id0)){Kt.clear();for(let e of Rt){if(e.f&24576)continue;let t=[e],n=e.parent;for(;n!==null;)Rt.has(n)&&(Rt.delete(n),t.push(n)),n=n.parent;for(let e=t.length-1;e>=0;e--){let n=t[e];n.f&24576||gr(n)}}Rt.clear()}}Rt=null}}function Bt(e,t,n,r){if(!n.has(e)&&(n.add(e),e.reactions!==null))for(let i of e.reactions){let e=i.f;e&2?Bt(i,t,n,r):e&4194320&&!(e&2048)&&Vt(i,t,r)&&(M(i,g),Ht(i))}}function Vt(e,t,r){let i=r.get(e);if(i!==void 0)return i;if(e.deps!==null)for(let i of e.deps){if(n.call(t,i))return!0;if(i.f&2&&Vt(i,t,r))return r.set(i,!0),!0}return r.set(e,!1),!1}function Ht(e){N.schedule(e)}function Ut(e,t){if(!(e.f&32&&e.f&1024)){e.f&2048?t.d.push(e):e.f&4096&&t.m.push(e),M(e,h);for(var n=e.first;n!==null;)Ut(n,t),n=n.next}}function Wt(e){M(e,h);for(var t=e.first;t!==null;)Wt(t),t=t.next}var Gt=new Set,Kt=new Map,qt=!1;function Jt(e,t){return{f:0,v:e,reactions:null,equals:Le,rv:0,wv:0}}function P(e,t){let n=Jt(e,t);return tr(n),n}function F(e,t=!1,n=!0){let r=Jt(e);return t||(r.equals=ze),Ve&&n&&j!==null&&j.l!==null&&(j.l.s??=[]).push(r),r}function I(e,t,n=!1){return B!==null&&(!Zn||B.f&131072)&&Ke()&&B.f&4325394&&(er===null||!er.has(e))&&Te(),Yt(e,n?$t(t):t,Mt)}function Yt(e,t,n=null){if(!e.equals(t)){Kt.set(e,Yn?t:e.v);var r=Ft.ensure();if(r.capture(e,t),e.f&2){let t=e;e.f&2048&&bt(t),Dt===null&&et(t)}e.wv=ur(),Qt(e,g,n),Ke()&&V!==null&&V.f&1024&&!(V.f&96)&&(ir===null?ar([e]):ir.push(e)),!r.is_fork&&Gt.size>0&&!qt&&Xt()}return t}function Xt(){qt=!1;for(let e of Gt){e.f&1024&&M(e,_);let t;try{t=dr(e)}catch{t=!0}t&&gr(e)}Gt.clear()}function Zt(e){I(e,e.v+1)}function Qt(e,t,n){var r=e.reactions;if(r!==null)for(var i=Ke(),a=r.length,o=0;o{if(cr===d)return e();var t=B,n=cr;Qn(null),lr(d);var r=e();return Qn(t),lr(n),r};return i&&r.set(`length`,P(t.length,u)),new Proxy(t,{defineProperty(e,t,n){(!(`value`in n)||n.configurable===!1||n.enumerable===!1||n.writable===!1)&&Ce();var i=r.get(t);return i===void 0?f(()=>{var e=P(n.value,u);return r.set(t,e),e}):I(i,n.value,!0),!0},deleteProperty(e,t){var n=r.get(t);if(n===void 0){if(t in e){let e=f(()=>P(E,u));r.set(t,e),Zt(o)}}else I(n,E),Zt(o);return!0},get(e,n,i){if(n===ae)return t;var o=r.get(n),s=n in e;if(o===void 0&&(!s||a(e,n)?.writable)&&(o=f(()=>P($t(s?e[n]:E),u)),r.set(n,o)),o!==void 0){var c=H(o);return c===E?void 0:c}return Reflect.get(e,n,i)},getOwnPropertyDescriptor(e,t){var n=Reflect.getOwnPropertyDescriptor(e,t);if(n&&`value`in n){var i=r.get(t);i&&(n.value=H(i))}else if(n===void 0){var a=r.get(t),o=a?.v;if(a!==void 0&&o!==E)return{enumerable:!0,configurable:!0,value:o,writable:!0}}return n},has(e,t){if(t===ae)return!0;var n=r.get(t),i=n!==void 0&&n.v!==E||Reflect.has(e,t);return(n!==void 0||V!==null&&(!i||a(e,t)?.writable))&&(n===void 0&&(n=f(()=>P(i?$t(e[t]):E,u)),r.set(t,n)),H(n)===E)?!1:i},set(e,t,n,s){var c=r.get(t),l=t in e;if(i&&t===`length`)for(var d=n;dP(E,u)),r.set(d+``,p)):I(p,E)}if(c===void 0)(!l||a(e,t)?.writable)&&(c=f(()=>P(void 0,u)),I(c,$t(n)),r.set(t,c));else{l=c.v!==E;var m=f(()=>$t(n));I(c,m)}var h=Reflect.getOwnPropertyDescriptor(e,t);if(h?.set&&h.set.call(s,n),!l){if(i&&typeof t==`string`){var g=r.get(`length`),_=Number(t);Number.isInteger(_)&&_>=g.v&&I(g,_+1)}Zt(o)}return!0},ownKeys(e){H(o);var t=Reflect.ownKeys(e).filter(e=>{var t=r.get(e);return t===void 0||t.v!==E});for(var[n,i]of r)i.v!==E&&!(n in e)&&t.push(n);return t},setPrototypeOf(){we()}})}new Set([`copyWithin`,`fill`,`pop`,`push`,`reverse`,`shift`,`sort`,`splice`,`unshift`]);var en,tn,nn,rn,an;function on(){if(en===void 0){en=window,tn=document,nn=/Firefox/.test(navigator.userAgent);var e=Element.prototype,t=Node.prototype,n=Text.prototype;rn=a(t,`firstChild`).get,an=a(t,`nextSibling`).get,u(e)&&(e[le]=void 0,e[ce]=null,e[ue]=void 0,e.__e=void 0),u(n)&&(n[de]=void 0)}}function sn(e=``){return document.createTextNode(e)}function cn(e){return rn.call(e)}function ln(e){return an.call(e)}function L(e,t){if(!D)return cn(e);var n=cn(O);if(n===null)n=O.appendChild(sn());else if(t&&n.nodeType!==3){var r=sn();return n?.before(r),k(r),r}return t&&mn(n),k(n),n}function un(e,t=!1){if(!D){var n=cn(e);return n instanceof Comment&&n.data===``?ln(n):n}if(t){if(O?.nodeType!==3){var r=sn();return O?.before(r),k(r),r}mn(O)}return O}function R(e,t=1,n=!1){let r=D?O:e;for(var i;t--;)i=r,r=ln(r);if(!D)return r;if(n){if(r?.nodeType!==3){var a=sn();return r===null?i?.after(a):r.before(a),k(a),a}mn(r)}return k(r),r}function dn(e){e.textContent=``}function fn(){return!Be||Rt!==null?!1:(V.f&b)!==0}function pn(e,t,n){return t==null||t===`http://www.w3.org/1999/xhtml`?n?document.createElement(e,{is:n}):document.createElement(e):n?document.createElementNS(t,e,{is:n}):document.createElementNS(t,e)}function mn(e){if(e.nodeValue.length<65536)return;let t=e.nextSibling;for(;t!==null&&t.nodeType===3;)t.remove(),e.nodeValue+=t.nodeValue,t=e.nextSibling}function hn(e){D&&cn(e)!==null&&dn(e)}var gn=!1;function _n(){gn||(gn=!0,document.addEventListener(`reset`,e=>{Promise.resolve().then(()=>{if(!e.defaultPrevented)for(let t of e.target.elements)t[fe]?.()})},{capture:!0}))}function vn(e){var t=B,n=V;Qn(null),$n(null);try{return e()}finally{Qn(t),$n(n)}}function yn(e,t,n,r=n){e.addEventListener(t,()=>vn(n));let i=e[fe];i?e[fe]=()=>{i(),r(!0)}:e[fe]=()=>r(!0),_n()}function bn(e){V===null&&(B===null&&be(e),ye()),Yn&&ve(e)}function xn(e,t){var n=t.last;n===null?t.last=t.first=e:(n.next=e,e.prev=n,t.last=e)}function Sn(e,t){var n=V;n!==null&&n.f&8192&&(e|=v);var r={ctx:j,deps:null,nodes:null,f:e|g|512,first:null,fn:t,last:null,next:null,parent:n,b:n&&n.b,prev:null,teardown:null,wv:0,ac:null};N?.register_created_effect(r);var i=r;if(e&4)jt===null?Ft.ensure().schedule(r):jt.push(r);else if(t!==null){try{gr(r)}catch(e){throw zn(r),e}i.deps===null&&i.teardown===null&&i.nodes===null&&i.first===i.last&&!(i.f&524288)&&(i=i.first,e&16&&e&65536&&i!==null&&(i.f|=S))}if(i!==null&&(i.parent=n,n!==null&&xn(i,n),B!==null&&B.f&2&&!(e&64))){var a=B;(a.effects??=[]).push(i)}return r}function Cn(){return B!==null&&!Zn}function wn(e){let t=Sn(8,null);return M(t,h),t.teardown=e,t}function Tn(e){bn(`$effect`);var t=V.f;if(!B&&t&32&&j!==null&&!j.i){var n=j;(n.e??=[]).push(e)}else return En(e)}function En(e){return Sn(4|T,e)}function Dn(e){return bn(`$effect.pre`),Sn(8|T,e)}function On(e){Ft.ensure();let t=Sn(64|w,e);return(e={})=>new Promise(n=>{e.outro?Hn(t,()=>{zn(t),n(void 0)}):(zn(t),n(void 0))})}function kn(e){return Sn(4,e)}function An(e,t){var n=j,r={effect:null,ran:!1,deps:e};n.l.$.push(r),r.effect=Nn(()=>{if(e(),!r.ran){r.ran=!0;var n=V;try{$n(n.parent),U(t)}finally{$n(n)}}})}function jn(){var e=j;Nn(()=>{for(var t of e.l.$){t.deps();var n=t.effect;n.f&1024&&n.deps!==null&&M(n,_),dr(n)&&gr(n),t.ran=!1}})}function Mn(e){return Sn(re|w,e)}function Nn(e,t=0){return Sn(8|t,e)}function z(e,t=[],n=[],r=[]){ut(r,t,n,t=>{Sn(8,()=>{e(...t.map(H))})})}function Pn(e,t=0){return Sn(16|t,e)}function Fn(e){return Sn(32|w,e)}function In(e){var t=e.teardown;if(t!==null){let e=Yn,n=B;Xn(!0),Qn(null);try{t.call(null)}finally{Xn(e),Qn(n)}}}function Ln(e,t=!1){var n=e.first;for(e.first=e.last=null;n!==null;){let e=n.ac;e!==null&&vn(()=>{e.abort(pe)});var r=n.next;n.f&64?n.parent=null:zn(n,t),n=r}}function Rn(e){for(var t=e.first;t!==null;){var n=t.next;t.f&32||zn(t),t=n}}function zn(e,t=!0){var n=!1;(t||e.f&262144)&&e.nodes!==null&&e.nodes.end!==null&&(Bn(e.nodes.start,e.nodes.end),n=!0),e.f|=x,Ln(e,t&&!n),hr(e,0);var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)e.stop();In(e),e.f^=x,e.f|=y;var i=e.parent;i!==null&&i.first!==null&&Vn(e),e.next=e.prev=e.teardown=e.ctx=e.deps=e.fn=e.nodes=e.ac=e.b=null}function Bn(e,t){for(;e!==null;){var n=e===t?null:ln(e);e.remove(),e=n}}function Vn(e){var t=e.parent,n=e.prev,r=e.next;n!==null&&(n.next=r),r!==null&&(r.prev=n),t!==null&&(t.first===e&&(t.first=r),t.last===e&&(t.last=n))}function Hn(e,t,n=!0){var r=[];Un(e,r,!0);var i=()=>{n&&zn(e),t&&t()},a=r.length;if(a>0){var o=()=>--a||i();for(var s of r)s.out(o)}else i()}function Un(e,t,n){if(!(e.f&8192)){e.f^=v;var r=e.nodes&&e.nodes.t;if(r!==null)for(let e of r)(e.is_global||n)&&t.push(e);for(var i=e.first;i!==null;){var a=i.next;if(!(i.f&64)){var o=(i.f&65536)!=0||(i.f&32)!=0&&(e.f&16)!=0;Un(i,t,o?n:!1)}i=a}}}function Wn(e){Gn(e,!0)}function Gn(e,t){if(e.f&8192){e.f^=v,e.f&1024||(M(e,g),Ft.ensure().schedule(e));for(var n=e.first;n!==null;){var r=n.next,i=(n.f&65536)!=0||(n.f&32)!=0;Gn(n,i?t:!1),n=r}var a=e.nodes&&e.nodes.t;if(a!==null)for(let e of a)(e.is_global||t)&&e.in()}}function Kn(e,t){if(e.nodes)for(var n=e.nodes.start,r=e.nodes.end;n!==null;){var i=n===r?null:ln(n);t.append(n),n=i}}var qn=null,Jn=!1,Yn=!1;function Xn(e){Yn=e}var B=null,Zn=!1;function Qn(e){B=e}var V=null;function $n(e){V=e}var er=null;function tr(e){B!==null&&(!Be||B.f&2)&&(er??=new Set).add(e)}var nr=null,rr=0,ir=null;function ar(e){ir=e}var or=1,sr=0,cr=sr;function lr(e){cr=e}function ur(){return++or}function dr(e){var t=e.f;if(t&2048)return!0;if(t&2&&(e.f&=~te),t&4096){for(var n=e.deps,r=n.length,i=0;ie.wv)return!0}t&512&&Dt===null&&M(e,h)}return!1}function fr(e,t,n=!0){var r=e.reactions;if(r!==null&&!(!Be&&er!==null&&er.has(e)))for(var i=0;i{e.ac.abort(pe)}),e.ac=null);try{e.f|=ne;var u=e.fn,d=u();e.f|=b;var f=e.deps,p=N?.is_fork;if(nr!==null){var m;if(p||hr(e,rr),f!==null&&rr>0)for(f.length=rr+nr.length,m=0;m{requestAnimationFrame(()=>e()),setTimeout(()=>e())});await Promise.resolve(),It()}function H(e){var t=(e.f&2)!=0;if(qn?.add(e),B!==null&&!Zn&&!(V!==null&&V.f&16384)&&(er===null||!er.has(e))){var r=B.deps;if(B.f&2097152)e.rv{throw e});throw p}}finally{e[wr]=t,delete e.currentTarget,Qn(d),$n(f)}}}var Ar=globalThis?.window?.trustedTypes&&globalThis.window.trustedTypes.createPolicy(`svelte-trusted-html`,{createHTML:e=>e});function jr(e){return Ar?.createHTML(e)??e}function Mr(e){var t=pn(`template`);return t.innerHTML=jr(e.replaceAll(``,``)),t.content}function Nr(e,t){var n=V;n.nodes===null&&(n.nodes={start:e,end:t,a:null,t:null})}function G(e,t){var n=(t&1)!=0,r=(t&2)!=0,i,a=!e.startsWith(``);return()=>{if(D)return Nr(O,null),O;i===void 0&&(i=Mr(a?e:``+e),n||(i=cn(i)));var t=r||nn?document.importNode(i,!0):i.cloneNode(!0);if(n){var o=cn(t),s=t.lastChild;Nr(o,s)}else Nr(t,t);return t}}function Pr(e=``){if(!D){var t=sn(e+``);return Nr(t,t),t}var n=O;return n.nodeType===3?mn(n):(n.before(n=sn()),k(n)),Nr(n,n),n}function Fr(){if(D)return Nr(O,null),O;var e=document.createDocumentFragment(),t=document.createComment(``),n=sn();return e.append(t,n),Nr(t,n),e}function K(e,t){if(D){var n=V;(!(n.f&32768)||n.nodes.end===null)&&(n.nodes.end=O),Ne();return}e!==null&&e.before(t)}function q(e,t){var n=t==null?``:typeof t==`object`?`${t}`:t;n!==(e[de]??=e.nodeValue)&&(e[de]=n,e.nodeValue=`${n}`)}function Ir(e,t){return Rr(e,t)}var Lr=new Map;function Rr(e,{target:t,anchor:n,props:i={},events:a,context:o,intro:s=!0,transformError:c}){on();var l=void 0,u=On(()=>{var s=n??t.appendChild(sn());ct(s,{pending:()=>{}},t=>{We({});var n=j;if(o&&(n.c=o),a&&(i.$$events=a),D&&Nr(t,null),l=e(t,i)||{},D&&(V.nodes.end=O,O===null||O.nodeType!==8||O.data!==`]`))throw Ae(),De;Ge()},c);var u=new Set,d=e=>{for(var n=0;n{for(var e of u)for(let n of[t,document]){var r=Lr.get(n),i=r.get(e);--i==0?(n.removeEventListener(e,kr),r.delete(e),r.size===0&&Lr.delete(n)):r.set(e,i)}Er.delete(d),s!==n&&s.parentNode?.removeChild(s)}});return zr.set(l,u),l}var zr=new WeakMap,Br=class{anchor;#e=new Map;#t=new Map;#n=new Map;#r=new Set;#i=!0;constructor(e,t=!0){this.anchor=e,this.#i=t}#a=e=>{if(this.#e.has(e)){var t=this.#e.get(e),n=this.#t.get(t);if(n)Wn(n),this.#r.delete(t);else{var r=this.#n.get(t);r&&(Wn(r.effect),this.#t.set(t,r.effect),this.#n.delete(t),r.fragment.lastChild.remove(),this.anchor.before(r.fragment),n=r.effect)}for(let[t,n]of this.#e){if(this.#e.delete(t),t===e)break;let r=this.#n.get(n);r&&(zn(r.effect),this.#n.delete(n))}for(let[e,r]of this.#t){if(e===t||this.#r.has(e))continue;let i=()=>{if(Array.from(this.#e.values()).includes(e)){var t=document.createDocumentFragment();Kn(r,t),t.append(sn()),this.#n.set(e,{effect:r,fragment:t})}else zn(r);this.#r.delete(e),this.#t.delete(e)};this.#i||!n?(this.#r.add(e),Hn(r,i,!1)):i()}}};#o=e=>{this.#e.delete(e);let t=Array.from(this.#e.values());for(let[e,n]of this.#n)t.includes(e)||(zn(n.effect),this.#n.delete(e))};ensure(e,t){var n=N,r=fn();if(t&&!this.#t.has(e)&&!this.#n.has(e))if(r){var i=document.createDocumentFragment(),a=sn();i.append(a),this.#n.set(e,{effect:Fn(()=>t(a)),fragment:i})}else this.#t.set(e,Fn(()=>t(this.anchor)));if(this.#e.set(n,e),r){for(let[t,r]of this.#t)t===e?n.unskip_effect(r):n.skip_effect(r);for(let[t,r]of this.#n)t===e?n.unskip_effect(r.effect):n.skip_effect(r.effect);n.oncommit(this.#a),n.ondiscard(this.#o)}else D&&(this.anchor=O),this.#a(n)}};function J(e,t,n=!1){var r;D&&(r=O,Ne());var i=new Br(e),a=n?S:0;function o(e,t){if(D){var n=Ie(r);if(e!==parseInt(n.substring(1))){var a=Fe();k(a),i.anchor=a,Me(!1),i.ensure(e,t),Me(!0);return}}i.ensure(e,t)}Pn(()=>{var e=!1;t((t,n=0)=>{e=!0,o(n,t)}),e||o(-1,null)},a)}function Vr(e,t){return t}function Hr(e,t,n){for(var i=[],a=t.length,o,s=t.length,c=0;c{if(o){if(o.pending.delete(n),o.done.add(n),o.pending.size===0){var t=e.outrogroups;Ur(e,r(o.done)),t.delete(o),t.size===0&&(e.outrogroups=null)}}else --s},!1)}if(s===0){var l=i.length===0&&n!==null;if(l){var u=n,d=u.parentNode;dn(d),d.append(u),e.items.clear()}Ur(e,t,!l)}else o={pending:new Set(t),done:new Set},(e.outrogroups??=new Set).add(o)}function Ur(e,t,n=!0){var r;if(e.pending.size>0){r=new Set;for(let t of e.pending.values())for(let n of t)r.add(e.items.get(n).e)}for(var i=0;i{var t=i();return e(t)?t:t==null?[]:r(t)}),p,m=new Map,h=!0;function g(e){v.effect.f&16384||(v.pending.delete(e),v.fallback=d,qr(v,p,c,n,a),d!==null&&(p.length===0?d.f&33554432?(d.f^=ee,Yr(d,null,c)):Wn(d):Hn(d,()=>{d=null})))}function _(e){v.pending.delete(e)}var v={effect:Pn(()=>{p=H(f);var e=p.length;let t=!1;D&&Ie(c)===`[!`!=(e===0)&&(c=Fe(),k(c),Me(!1),t=!0);for(var r=new Set,u=N,v=fn(),y=0;ys(c)):(d=Fn(()=>s(Wr??=sn())),d.f|=ee)),e>r.size&&_e(``,``,``),D&&e>0&&k(Fe()),!h)if(m.set(u,r),v){for(let[e,t]of l)r.has(e)||u.skip_effect(t.e);u.oncommit(g),u.ondiscard(_)}else g(u);t&&Me(!0),H(f)}),flags:n,items:l,pending:m,outrogroups:null,fallback:d};h=!1,D&&(c=O)}function Kr(e){for(;e!==null&&!(e.f&32);)e=e.next;return e}function qr(e,t,n,i,a){var o=(i&8)!=0,s=t.length,c=e.items,l=Kr(e.effect.first),u,d=null,f,p=[],m=[],h,g,_,v;if(o)for(v=0;v0){var te=i&4&&s===0?n:null;if(o){for(v=0;v{if(f!==void 0)for(_ of f)_.nodes?.a?.apply()})}function Jr(e,t,n,r,i,a,o,s){var c=o&1?o&16?Jt(n):F(n,!1,!1):null,l=o&2?Jt(i):null;return{v:c,i:l,e:Fn(()=>(a(t,c??n,l??i,s),()=>{e.delete(r)}))}}function Yr(e,t,n){if(e.nodes)for(var r=e.nodes.start,i=e.nodes.end,a=t&&!(t.f&33554432)?t.nodes.start:n;r!==null;){var o=ln(r);if(a.before(r),r===i)return;r=o}}function Xr(e,t,n){t===null?e.effect.first=n:t.next=n,n===null?e.effect.last=t:n.prev=t}function Zr(e,t){let n=null,r=D;var i;if(D){n=O;for(var a=cn(document.head);a!==null&&(a.nodeType!==8||a.data!==e);)a=ln(a);if(a===null)Me(!1);else{var o=ln(a);a.remove(),k(o)}}D||(i=document.head.appendChild(sn()));try{Pn(()=>{var e=Fn(()=>t(i));e.f|=C})}finally{r&&(Me(!0),k(n))}}function Qr(e){var t,n,r=``;if(typeof e==`string`||typeof e==`number`)r+=e;else if(typeof e==`object`)if(Array.isArray(e)){var i=e.length;for(t=0;t=0;){var s=o+a;(o===0||ti.includes(r[o-1]))&&(s===r.length||ti.includes(r[s]))?r=(o===0?``:r.substring(0,o))+r.substring(s+1):o=s}}return r===``?null:r}function Y(e,t,n,r,i,a){var o=e[le];if(D||o!==n||o===void 0){var s=ni(n,r,a);(!D||s!==e.getAttribute(`class`))&&(s==null?e.removeAttribute(`class`):t?e.className=s:e.setAttribute(`class`,s)),e[le]=n}else if(a&&i!==a)for(var c in a){var l=!!a[c];(i==null||l!==!!i[c])&&e.classList.toggle(c,l)}return a}var ri=Symbol(`is custom element`),ii=Symbol(`is html`),ai=me?`link`:`LINK`;function oi(e){if(D){var t=!1,n=()=>{if(!t){if(t=!0,e.hasAttribute(`value`)){var n=e.value;X(e,`value`,null),e.value=n}if(e.hasAttribute(`checked`)){var r=e.checked;X(e,`checked`,null),e.checked=r}}};e[fe]=n,Ye(n),_n()}}function si(e,t){var n=ci(e);n.checked!==(n.checked=t??void 0)&&(e.checked=t)}function X(e,t,n,r){var i=ci(e);D&&(i[t]=e.getAttribute(t),t===`src`||t===`srcset`||t===`href`&&e.nodeName===ai)||i[t]!==(i[t]=n)&&(t===`loading`&&(e[se]=n),n==null?e.removeAttribute(t):typeof n!=`string`&&ui(e).includes(t)?e[t]=n:e.setAttribute(t,n))}function ci(e){return e[ce]??={[ri]:e.nodeName.includes(`-`),[ii]:e.namespaceURI===Oe}}var li=new Map;function ui(e){var t=e.getAttribute(`is`)||e.nodeName,n=li.get(t);if(n)return n;li.set(t,n=[]);for(var r,i=e,a=Element.prototype;a!==i;){for(var s in r=o(i),r)r[s].set&&s!==`innerHTML`&&s!==`textContent`&&s!==`innerText`&&n.push(s);i=l(i)}return n}function di(e,t,n=t){var r=new WeakSet;yn(e,`input`,async i=>{var a=i?e.defaultValue:e.value;if(a=fi(e)?pi(a):a,n(a),N!==null&&r.add(N),await _r(),a!==(a=t())){var o=e.selectionStart,s=e.selectionEnd,c=e.value.length;if(e.value=a??``,s!==null){var l=e.value.length;o===s&&s===c&&l>c?(e.selectionStart=l,e.selectionEnd=l):(e.selectionStart=o,e.selectionEnd=Math.min(s,l))}}}),(D&&e.defaultValue!==e.value||U(t)==null&&e.value)&&(n(fi(e)?pi(e.value):e.value),N!==null&&r.add(N)),Nn(()=>{var n=t();if(e===document.activeElement){var i=Be?Et:N;if(r.has(i))return}fi(e)&&n===pi(e.value)||e.type===`date`&&!n&&!e.value||n!==e.value&&(e.value=n??``)})}function fi(e){var t=e.type;return t===`number`||t===`range`}function pi(e){return e===``?null:+e}function mi(e=!1){let t=j,n=t.l.u;if(!n)return;let r=()=>br(t.s);if(e){let e=0,n={},i=mt(()=>{let r=!1,i=t.s;for(let e in i)i[e]!==n[e]&&(n[e]=i[e],r=!0);return r&&e++,e});r=()=>H(i)}n.b.length&&Dn(()=>{hi(t,r),p(n.b)}),Tn(()=>{let e=U(()=>n.m.map(f));return()=>{for(let t of e)typeof t==`function`&&t()}}),n.a.length&&Tn(()=>{hi(t,r),p(n.a)})}function hi(e,t){if(e.l.s)for(let t of e.l.s)H(t);t()}function Z(e,t,n,r){var i=!Ve||(n&2)!=0,o=(n&8)!=0,s=(n&16)!=0,c=r,l=!0,u=void 0,d=()=>s&&i?(u??=mt(r),H(u)):(l&&(l=!1,c=s?U(r):r),c);let f;if(o){var p=ae in e||oe in e;f=a(e,t)?.set??(p&&t in e?n=>e[t]=n:void 0)}var m,h=!1;o?[m,h]=at(()=>e[t]):m=e[t],m===void 0&&r!==void 0&&(m=d(),f&&(i&&Se(t),f(m)));var g=i?()=>{var n=e[t];return n===void 0?d():(l=!0,n)}:()=>{var n=e[t];return n!==void 0&&(c=void 0),n===void 0?c:n};if(i&&!(n&4))return g;if(f){var _=e.$$legacy;return(function(e,t){return arguments.length>0?((!i||!t||_||h)&&f(t?g():e),e):g()})}var v=!1,y=(n&1?mt:vt)(()=>(v=!1,g()));o&&H(y);var b=V;return(function(e,t){if(arguments.length>0){let n=t?H(y):i&&o?$t(e):e;return I(y,n),v=!0,c!==void 0&&(c=n),e}return Yn&&v||b.f&16384?y.v:H(y)})}function gi(e){j===null&&he(`onMount`),Ve&&j.l!==null?_i(j).m.push(e):Tn(()=>{let t=U(e);if(typeof t==`function`)return t})}function _i(e){var t=e.l;return t.u??={a:[],b:[],m:[]}}typeof window<`u`&&((window.__svelte??={}).v??=new Set).add(`5`),He();var vi=`dbgw_api_access_token`,yi=window.location.origin,bi=`${yi}/auth`,xi=`${yi}/api/v1`;function Si(e,t=`Request failed`){if(!e?.error)return null;let n=typeof e.error.message==`string`&&e.error.message?e.error.message:`${t}${e.error.code?` (${e.error.code})`:``}`;return Error(n)}function Q(e,t=`Request failed`){return e instanceof Error&&e.message?e.message:t}function Ci(){return typeof crypto<`u`&&crypto.randomUUID?crypto.randomUUID():`request-${Date.now()}`}function wi(e){return e===401||e===403}function Ti(){window.location.href=bi}function Ei(){return window.localStorage.getItem(vi)||``}function Di(e){if(!e){window.localStorage.removeItem(vi);return}window.localStorage.setItem(vi,e)}function Oi(){Di(``)}function ki(){let e=window.location.hash.startsWith(`#`)?window.location.hash.slice(1):window.location.hash;if(!e)return``;let t=new URLSearchParams(e).get(`access_token`)||``;return t?(Di(t),window.history.replaceState({},document.title,window.location.pathname+window.location.search),t):``}async function Ai(e,t,n){let r;try{r=await fetch(`${xi}/${t}`,{method:`POST`,headers:{Authorization:`Bearer ${e}`,"Content-Type":`application/json`},body:JSON.stringify({lrpc:`1`,id:Ci(),params:n})})}catch(e){throw Error(Q(e,`Network request failed`))}let i=null;try{i=await r.json()}catch(e){throw wi(r.status)?Error(`Unauthorized (${r.status})`):r.ok?Error(Q(e,`Failed to parse server response`)):Error(`Failed to do request (${r.status})`)}let a=Si(i);if(a)throw a;if(wi(r.status))throw Error(`Unauthorized (${r.status})`);if(!r.ok)throw Error(`Failed to do request (${r.status})`);return i.result}async function ji(e){let t=Ei();if(!t)return Ti(),null;try{return await e(t)}catch(e){if(!Q(e,``).includes(`Unauthorized`))throw e;return Oi(),Ti(),null}}function Mi(e){return Ai(e,`targets.list.v1`,{})}function Ni(e,t){return Ai(e,`targets.get.v1`,{target_id:t})}function Pi(e){return Ai(e,`profile.get.v1`,{})}function Fi(e,t=``){return Ai(e,`bookmarks.list.v1`,t?{target_id:t}:{})}function Ii(e,t){return Ai(e,`queries.list.v1`,typeof t==`number`?{limit:t}:{})}function Li(e,t,n){return Ai(e,`query.run.v1`,{target_id:t,query:n})}function Ri(e){if(!e)return e;let t=e.table??{headers:[],rows:[]},n=e.error,r=typeof n==`string`?n:n?.error??``;return{...e,state:e.state??``,status:e.state??e.status??``,table:{headers:t.headers??[],rows:t.rows??[]},meta:e.meta??null,error:r}}function zi(e,t){return Ai(e,`admin.requests.list.v1`,{page:t})}function Bi(e,t){return Ai(e,`query-results.get.v1`,{id:t}).then(Ri)}function Vi(e,t,n){return Ai(e,`query-results.export-link.v1`,{query_result_id:t,format:n})}function Hi(e,t,n,r){return Ai(e,`bookmarks.add.v1`,{target_id:t,title:n,query:r})}function Ui(e,t){return Ai(e,`bookmarks.delete.v1`,{id:t})}var Wi=Gi(`/ui/`);function Gi(e){let t=e.endsWith(`/`)?e.slice(0,-1):e;return t===``?``:t}function Ki(e,t=``){return`${Wi}${e.startsWith(`/`)?e:`/${e}`}${t}`}function qi(){return{pathname:Xi(window.location.pathname),searchParams:new URLSearchParams(window.location.search)}}function Ji(e,t=``){window.history.pushState({},``,Ki(e,t)),window.dispatchEvent(new PopStateEvent(`popstate`))}function Yi(e,t){let n=Zi(e),r=Zi(t);if(n.length!==r.length)return null;let i={};for(let e=0;e· `,1),$i=G(` `,1),ea=G(`Admin requests`),ta=G(`
DG
Database Gateway
`);function na(e,t){We(t,!0);let n=Z(t,`isAdminRequestsPage`,3,!1),r=`inline-flex min-h-8 items-center justify-center gap-2 rounded-lg border px-2.5 text-[13px] font-semibold leading-none transition-colors focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-lime-200`,i=`border-lime-300 bg-lime-300 text-zinc-950`,a=`border-zinc-700 bg-zinc-800/90 text-zinc-100 hover:border-zinc-500 hover:bg-zinc-700/90`,o=_t(()=>t.profile?.username||t.profile?.id||`Unknown user`),s=_t(()=>t.profile?.role||``),c=_t(()=>H(s)===`admin`);var l=ta();Y(l,1,`rounded-xl border border-zinc-700/90 bg-zinc-900/90 px-3 py-2.5 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl mb-3 flex flex-col gap-3 md:flex-row md:items-center md:justify-between`);var u=L(l),d=L(u),f=R(d,2),p=R(L(f),2),m=L(p),h=e=>{K(e,Pr(`Loading session`))},g=e=>{var n=Pr();z(()=>q(n,t.error)),K(e,n)},_=e=>{var t=$i(),n=un(t),r=L(n,!0);A(n);var i=R(n,2),a=e=>{var t=Qi(),n=R(un(t),2),r=L(n,!0);A(n),z(()=>q(r,H(s))),K(e,t)};J(i,e=>{H(s)&&e(a)}),z(()=>q(r,H(o))),K(e,t)};J(m,e=>{t.loading?e(h):t.error?e(g,1):e(_,-1)}),A(p),A(f),A(u);var v=R(u,2),y=L(v),b=R(y,2),x=e=>{var t=ea();z(e=>{Y(t,1,`${r} ${n()?i:a} flex-1 no-underline md:flex-none`),X(t,`href`,e)},[()=>Ki(`/admin/requests`)]),K(e,t)};J(b,e=>{H(c)&&e(x)});var S=R(b,2);Y(S,1,`${r} ${a} flex-1 no-underline md:flex-none`),A(v),A(l),z((e,t)=>{X(d,`href`,e),Y(y,1,`${r} ${n()===!1?i:a} flex-1 no-underline md:flex-none`),X(y,`href`,t)},[()=>Ki(`/`),()=>Ki(`/`)]),W(`click`,S,function(...e){Oi?.apply(this,e)}),K(e,l),Ge()}Dr([`click`]);var ra=G(` `),ia=G(` `),aa=G(``),oa=G(`
`);function sa(e,t){We(t,!0);let n=Z(t,`table`,19,()=>({headers:[],rows:[]}));var r=oa(),i=L(r),a=L(i),o=L(a),s=L(o);Gr(s,21,()=>n().headers??[],Vr,(e,t)=>{var n=ra(),r=L(n,!0);A(n),z(()=>q(r,H(t))),K(e,n)}),A(s),A(o);var c=R(o);Gr(c,21,()=>n().rows??[],Vr,(e,t,n)=>{var r=aa();Y(r,1,ei(n%2==0?`bg-zinc-900/80`:`bg-zinc-800/90`)),Gr(r,21,()=>H(t),Vr,(e,t)=>{var n=ia(),r=L(n,!0);A(n),z(()=>{X(n,`title`,H(t)),q(r,H(t))}),K(e,n)}),A(r),K(e,r)}),A(c),A(a),A(i),A(r),K(e,r),Ge()}var ca=G(`
`);function $(e,t){let n=Z(t,`title`,3,`No title`),r=Z(t,`color`,3,`zinc`),i=Z(t,`code`,3,!1),a=Z(t,`value`,3,`No value`);var o=ca();Y(o,1,`rounded-md border border-zinc-700/80 bg-zinc-800/90 p-2`);var s=L(o),c=L(s,!0);A(s);var l=R(s,2),u=L(l,!0);A(l),A(o),z(()=>{q(c,n()),Y(l,1,`mt-1 text-sm text-${r()??``}-300 ${i()?`whitespace-pre-wrap`:``}`),q(u,a())}),K(e,o)}var la=G(`

`),ua=G(`
`),da=G(`
`),fa=G(``);function pa(e,t){let n=Z(t,`subtitle`,3,``),r=Z(t,`targetID`,3,``),i=Z(t,`userID`,3,``),a=Z(t,`requestID`,3,``),o=Z(t,`table`,19,()=>({headers:[],rows:[]})),s=Z(t,`meta`,3,null),c=Z(t,`status`,3,``),l=Z(t,`error`,3,``),u=_t(()=>s()??{rows_count:``,columns_count:``,vectors_count:``,trace:{records:[]}}),d=`rounded-xl border border-zinc-700/90 bg-zinc-900/90 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl`;var f=fa(),p=L(f);Y(p,1,`${d} p-5 md:p-6`);var m=L(p),h=L(m),g=L(h),_=L(g),v=L(_,!0);A(_);var y=R(_,2),b=e=>{var t=la(),r=L(t,!0);A(t),z(()=>q(r,n())),K(e,t)};J(y,e=>{n()&&e(b)}),A(g);var x=R(g,2);Y(x,1,`inline-flex min-h-8 items-center justify-center gap-2 rounded-lg border border-lime-300 bg-lime-200 px-2.5 text-[13px] font-semibold leading-none text-zinc-900 no-underline transition-colors hover:border-lime-300 hover:bg-lime-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-lime-200 justify-center`);var S=L(x,!0);A(x),A(h);var C=R(h,2),w=L(C);$(w,{title:`ID`,get value(){return a()}});var T=R(w,2);{let e=_t(()=>c()===`failed`?`red`:`green`);$(T,{title:`Status`,get value(){return c()},get color(){return H(e)}})}var ee=R(T,2);$(ee,{title:`Created`,get value(){return t.createdAt}});var te=R(ee,2);$(te,{title:`Target`,get value(){return r()}});var ne=R(te,2);$(ne,{title:`User`,get value(){return i()}});var re=R(ne,2);$(re,{title:`Rows`,get value(){return H(u).rows_count}});var ie=R(re,2);$(ie,{title:`Columns`,get value(){return H(u).columns_count}});var ae=R(ie,2);$(ae,{title:`Vectors`,get value(){return H(u).vectors_count}}),Gr(R(ae,2),17,()=>H(u).trace.records,Vr,(e,t)=>{{let n=_t(()=>H(t).duration/1e6);$(e,{get title(){return`${H(t).name??``} (ms)`},get value(){return H(n)}})}}),A(C);var oe=R(C,2);$(L(oe),{title:`Query`,get value(){return t.query},code:!0}),A(oe);var se=R(oe,2),ce=e=>{var t=ua(),n=L(t,!0);A(t),z(()=>q(n,l())),K(e,t)};J(se,e=>{l()&&e(ce)}),A(m),A(p);var le=R(p,2),ue=e=>{var t=da();Y(t,1,`${d} p-3`),sa(L(t),{get table(){return o()}}),A(t),K(e,t)};J(le,e=>{l()||e(ue)}),A(f),z(()=>{q(v,t.title),X(x,`href`,t.backHref),q(S,t.backLabel)}),K(e,f)}var ma=G(`
Loading request result...
`),ha=G(`
`);function ga(e,t){We(t,!0);let n=P(null),r=P(``),i=P(!0),a=P(``),o=`rounded-xl border border-zinc-700/90 bg-zinc-900/90 p-3 text-sm shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl`;async function s(){I(i,!0),I(r,``);try{let e=await ji(e=>Bi(e,t.requestID));if(!e)return;I(n,e,!0)}catch(e){I(r,Q(e,`Failed to load request result`),!0)}finally{I(i,!1)}}Tn(()=>{t.requestID&&t.requestID!==H(a)&&(I(a,t.requestID,!0),I(n,null),s())});var c=Fr(),l=un(c),u=e=>{var t=ma();Y(t,1,`${o} text-zinc-400`),K(e,t)},d=e=>{var t=ha();Y(t,1,`${o} border-red-500/70 bg-red-950/20 text-red-300`);var n=L(t,!0);A(t),z(()=>q(n,H(r))),K(e,t)},f=e=>{{let t=_t(()=>Ki(`/admin/requests`));pa(e,{title:`Request details`,subtitle:`Inspect the stored SQL response for an administrative request.`,get backHref(){return H(t)},backLabel:`Back to requests`,get query(){return H(n).query},get createdAt(){return H(n).created_at},get targetID(){return H(n).target_id},get userID(){return H(n).user_id},get requestID(){return H(n).id},get table(){return H(n).table},get meta(){return H(n).meta},get status(){return H(n).state},get error(){return H(n).error}})}};J(l,e=>{H(i)?e(u):H(r)?e(d,1):H(n)&&e(f,2)}),K(e,c),Ge()}var _a=G(`Previous`),va=G(`Next`),ya=G(`
Loading admin requests...
`),ba=G(`
`),xa=G(`
No requests yet.
`),Sa=G(`
HTML
`),Ca=G(`
`),wa=G(`
`);function Ta(e,t){We(t,!0);let n=P(0),r=P($t([])),i=P(``),a=P(!0),o=P(!1),s=P(!1),c=P(``),l=P(``),u=P(!1),d=null,f=!1,p=`rounded-xl border border-zinc-700/90 bg-zinc-900/90 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl`,m=`inline-flex min-h-8 items-center justify-center gap-2 rounded-lg border border-lime-300 bg-lime-200 px-2.5 text-[13px] font-semibold leading-none text-zinc-900 no-underline transition-colors hover:border-lime-300 hover:bg-lime-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-lime-200`;async function h(e=!0){if(!f){f=!0,e&&I(a,!0),I(i,``),I(s,t.page>1);try{let e=await ji(e=>zi(e,t.page));if(!e)return;I(r,e.requests??[],!0),I(o,!!e.has_next,!0),I(s,!!e.has_prev,!0)}catch(e){I(i,Q(e,`Failed to load admin requests`),!0)}finally{f=!1,e&&I(a,!1)}}}Tn(()=>{t.page&&t.page!==H(n)&&(I(n,t.page,!0),h())});function g(){d&&=(window.clearInterval(d),null)}function _(e){I(u,!!e.currentTarget?.checked,!0)}Tn(()=>{if(!H(u)){g();return}if(t.page>1){Ji(`/admin/requests`);return}return h(!1),g(),d=window.setInterval(()=>{h(!1)},5e3),()=>{g()}});async function v(e,t){if(!(!e||H(c)||H(l))){I(c,e,!0),I(l,t,!0),I(i,``);try{let n=await ji(n=>Vi(n,e,t));if(!n?.url)return;window.open(n.url,`_blank`,`noopener`)}catch(e){I(i,Q(e,`Failed to prepare ${t.toUpperCase()} export`),!0)}finally{I(c,``),I(l,``)}}}var y=wa(),b=L(y);Y(b,1,`${p} flex items-center justify-between gap-2 p-3`);var x=L(b),S=L(x),C=L(S);A(S);var w=R(S,2),T=L(w);oi(T),Pe(2),A(w),A(x);var ee=R(x,2),te=L(ee),ne=e=>{var n=_a();Y(n,1,ei(m)),z(e=>X(n,`href`,e),[()=>Ki(`/admin/requests`,`?page=${t.page-1}`)]),K(e,n)};J(te,e=>{H(s)&&e(ne)});var re=R(te,2),ie=e=>{var n=va();Y(n,1,ei(m)),z(e=>X(n,`href`,e),[()=>Ki(`/admin/requests`,`?page=${t.page+1}`)]),K(e,n)};J(re,e=>{H(o)&&e(ie)}),A(ee),A(b);var ae=R(b,2),oe=e=>{var t=ya();Y(t,1,`${p} p-3 text-sm text-zinc-400`),K(e,t)},se=e=>{var t=ba();Y(t,1,`${p} border-red-500/70 bg-red-950/20 p-3 text-sm text-red-300`);var n=L(t,!0);A(t),z(()=>q(n,H(i))),K(e,t)},ce=e=>{var t=xa();Y(t,1,`${p} p-3 text-sm text-zinc-400`),K(e,t)},le=e=>{var t=Ca();Gr(t,21,()=>H(r),Vr,(e,t)=>{var n=Sa();Y(n,1,`${p} flex flex-col gap-1.5 p-3`);var r=L(n),i=L(r),a=L(i);A(i);var o=R(i,2),s=L(o);Y(s,1,`${m} px-2 py-1 text-[11px]`);var l=R(s,2);Y(l,1,`${m} px-2 py-1 text-[11px] disabled:cursor-not-allowed disabled:opacity-60`);var u=R(l,2);Y(u,1,`${m} px-2 py-1 text-[11px] disabled:cursor-not-allowed disabled:opacity-60`),A(o),A(r);var d=R(r,2);Y(d,1,`rounded-lg border border-zinc-700/80 bg-zinc-800/90 mt-1 break-words p-2 text-xs text-zinc-200`);var f=L(d,!0);A(d),A(n),z((e,n,r)=>{q(a,`${H(t).created_at??``} | ${H(t).user_id??``} | ${H(t).target_id??``}`),X(s,`href`,e),l.disabled=n,u.disabled=r,q(f,H(t).query)},[()=>Ki(`/admin/requests/${H(t).id}`),()=>!!H(c),()=>!!H(c)]),W(`click`,l,()=>v(H(t).id,`json`)),W(`click`,u,()=>v(H(t).id,`csv`)),K(e,n)}),A(t),K(e,t)};J(ae,e=>{H(a)?e(oe):H(i)?e(se,1):H(r).length===0?e(ce,2):e(le,-1)});var ue=R(ae,2);Y(ue,1,`${p} flex items-center justify-between gap-2 p-3`);var de=L(ue),fe=L(de);A(de);var pe=R(de,2),me=L(pe),he=e=>{var n=_a();Y(n,1,ei(m)),z(e=>X(n,`href`,e),[()=>Ki(`/admin/requests`,`?page=${t.page-1}`)]),K(e,n)};J(me,e=>{H(s)&&e(he)});var ge=R(me,2),_e=e=>{var n=va();Y(n,1,ei(m)),z(e=>X(n,`href`,e),[()=>Ki(`/admin/requests`,`?page=${t.page+1}`)]),K(e,n)};J(ge,e=>{H(o)&&e(_e)}),A(pe),A(ue),A(y),z(()=>{q(C,`Page ${t.page??``}`),si(T,H(u)),q(fe,`Page ${t.page??``}`)}),W(`change`,T,_),K(e,y),Ge()}Dr([`change`,`click`]);var Ea=G(`
`),Da=G(`
`),Oa=G(`
`),ka=G(`
`);function Aa(e,t){We(t,!0);let n=Z(t,`bookmarks`,19,()=>[]),r=Z(t,`emptyText`,3,`No bookmarks found.`),i=Z(t,`compact`,3,!1),a=Z(t,`runningBookmarkID`,3,``),o=Z(t,`deletingBookmarkID`,3,``),s=Z(t,`onRun`,3,()=>{}),c=Z(t,`onDelete`,3,()=>{}),l=`rounded-lg border border-zinc-700/80 bg-zinc-800/90`,u=`inline-flex min-h-8 min-w-20 items-center justify-center gap-2 rounded-lg border border-lime-300 bg-lime-200 px-2.5 text-[13px] font-semibold leading-none text-zinc-900 transition-colors hover:border-lime-300 hover:bg-lime-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-lime-200 disabled:cursor-not-allowed disabled:opacity-60`;var d=ka(),f=L(d),p=e=>{var t=Ea(),n=L(t,!0);A(t),z(()=>q(n,r())),K(e,t)},m=e=>{var t=Fr();Gr(un(t),17,n,Vr,(e,t)=>{var n=Fr(),r=un(n),d=e=>{var n=Da();Y(n,1,`${l} flex items-center gap-2.5 p-2.5`);var r=L(n),i=L(r,!0);A(r);var d=R(r,2),f=L(d,!0);A(d);var p=R(d,2),m=L(p);Y(m,1,ei(u));var h=L(m,!0);A(m);var g=R(m,2);Y(g,1,`${u} border-red-400 bg-red-600 text-white hover:border-red-400 hover:bg-red-600/70`);var _=L(g,!0);A(g),A(p),A(n),z(()=>{q(i,H(t).title),X(d,`title`,H(t).query),q(f,H(t).query),m.disabled=a()===H(t).id,q(h,a()===H(t).id?`Running...`:`Run`),g.disabled=o()===H(t).id,q(_,o()===H(t).id?`Deleting...`:`Delete`)}),W(`click`,m,()=>s()(H(t))),W(`click`,g,()=>c()(H(t))),K(e,n)},f=e=>{var n=Oa();Y(n,1,`${l} flex flex-col gap-2.5 p-2.5`);var r=L(n),i=L(r),d=L(i),f=L(d,!0);A(d);var p=R(d,2),m=L(p,!0);A(p),A(i);var h=R(i,2),g=L(h,!0);A(h),A(r);var _=R(r,2),v=L(_);Y(v,1,ei(u));var y=L(v,!0);A(v);var b=R(v,2);Y(b,1,`${u} border-red-400 bg-red-600 text-white hover:border-red-400 hover:bg-red-600/70`);var x=L(b,!0);A(b),A(_),A(n),z(()=>{q(f,H(t).title),q(m,H(t).query),q(g,H(t).target_id),v.disabled=a()===H(t).id,q(y,a()===H(t).id?`Running...`:`Run`),b.disabled=o()===H(t).id,q(x,o()===H(t).id?`Deleting...`:`Delete`)}),W(`click`,v,()=>s()(H(t))),W(`click`,b,()=>c()(H(t))),K(e,n)};J(r,e=>{i()?e(d):e(f,-1)}),K(e,n)}),K(e,t)};J(f,e=>{n().length===0?e(p):e(m,-1)}),A(d),K(e,d),Ge()}Dr([`click`]);var ja=G(`
Loading bookmarks...
`),Ma=G(`
`),Na=G(`
Saved work

Bookmarks

Keep repeatable queries close at hand and rerun them without re-entering SQL.

`);function Pa(e,t){We(t,!0);let n=Z(t,`runningBookmarkID`,3,``),r=Z(t,`deletingBookmarkID`,3,``),i=Z(t,`onRun`,3,()=>{}),a=Z(t,`onDelete`,3,()=>{});var o=Na();Y(o,1,`rounded-xl border border-zinc-700/90 bg-zinc-900/90 p-3 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl flex flex-col gap-3`);var s=L(o),c=R(L(s),2),l=L(c,!0);A(c),A(s);var u=R(s,2),d=e=>{K(e,ja())},f=e=>{var n=Ma(),r=L(n,!0);A(n),z(()=>q(r,t.error)),K(e,n)},p=e=>{Aa(e,{get bookmarks(){return t.bookmarks},emptyText:`No bookmarks found.`,get runningBookmarkID(){return n()},get deletingBookmarkID(){return r()},get onRun(){return i()},get onDelete(){return a()}})};J(u,e=>{t.loading?e(d):t.error?e(f,1):e(p,-1)}),A(o),z(()=>q(l,t.bookmarks.length)),K(e,o),Ge()}var Fa=G(`
Loading queries...
`),Ia=G(`
`),La=G(`
No queries found.
`),Ra=G(`
`),za=G(`
`),Ba=G(`
`),Va=G(``),Ha=G(`
`),Ua=G(`
Activity

Recent queries

Jump back into the latest executions and inspect results without repeating the request.

`);function Wa(e,t){We(t,!0);function n(e){if(!e)return`Unknown time`;let t=new Date(e);return Number.isNaN(t.getTime())?e:new Intl.DateTimeFormat(void 0,{month:`short`,day:`numeric`,hour:`2-digit`,minute:`2-digit`}).format(t)}var r=Ua();Y(r,1,`rounded-xl border border-zinc-700/90 bg-zinc-900/90 p-3 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl flex flex-col gap-3`);var i=L(r),a=R(L(i),2),o=L(a,!0);A(a),A(i);var s=R(i,2),c=e=>{K(e,Fa())},l=e=>{var n=Ia(),r=L(n,!0);A(n),z(()=>q(r,t.error)),K(e,n)},u=e=>{K(e,La())},d=e=>{var r=Ha();Gr(r,21,()=>t.queries,Vr,(e,t)=>{var r=Va();Y(r,1,`rounded-lg border border-zinc-700/80 bg-zinc-800/90 flex flex-col gap-2.5 p-2.5`);var i=L(r),a=L(i),o=L(a),s=L(o,!0);A(o);var c=R(o,2),l=e=>{var n=Ra(),r=L(n,!0);A(n),z(()=>q(r,H(t).state)),K(e,n)},u=e=>{var n=za(),r=L(n,!0);A(n),z(()=>q(r,H(t).state)),K(e,n)},d=e=>{var n=Ba(),r=L(n);A(n),z(()=>q(r,`unknown - ${H(t).state??``}`)),K(e,n)};J(c,e=>{H(t).state===`completed`?e(l):H(t).state===`failed`?e(u,1):e(d,-1)});var f=R(c,2),p=L(f,!0);A(f),A(a);var m=R(a,2);Y(m,1,`inline-flex min-h-8 items-center justify-center gap-2 rounded-lg border border-lime-300 bg-lime-200 px-2.5 text-[13px] font-semibold leading-none text-zinc-900 no-underline transition-colors hover:border-lime-300 hover:bg-lime-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-lime-200 min-w-[72px]`),A(i);var h=R(i,2),g=L(h,!0);A(h),A(r),z((e,n)=>{q(s,e),q(p,H(t).target_id),X(m,`href`,n),q(g,H(t).query)},[()=>n(H(t).created_at),()=>Ki(`/servers/${H(t).target_id}/${H(t).id}`)]),K(e,r)}),A(r),K(e,r)};J(s,e=>{t.loading?e(c):t.error?e(l,1):t.queries.length===0?e(u,2):e(d,-1)}),A(r),z(()=>q(o,t.queries.length)),K(e,r),Ge()}var Ga=G(`
Loading servers...
`),Ka=G(`
`),qa=G(`
No servers found.
`),Ja=G(`
`),Ya=G(`
`),Xa=G(`
`),Za=G(`
Exposed tables
`),Qa=G(`
Tables
Fields
`),$a=G(`
`),eo=G(`
Targets

Available servers

Browse approved data sources and inspect the tables exposed for safe querying.

`);function to(e,t){We(t,!0);var n=eo();Y(n,1,`rounded-xl border border-zinc-700/90 bg-zinc-900/90 p-3 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl flex flex-col gap-3`);var r=L(n),i=R(L(r),2),a=L(i,!0);A(i),A(r);var o=R(r,2),s=e=>{K(e,Ga())},c=e=>{var n=Ka(),r=L(n,!0);A(n),z(()=>q(r,t.error)),K(e,n)},l=e=>{K(e,qa())},u=e=>{var n=$a();Gr(n,21,()=>t.servers,Vr,(e,t)=>{var n=Qa(),r=L(n);Y(r,1,`rounded-lg border border-zinc-700/80 bg-zinc-800/90 flex flex-col gap-3 p-3 transition-colors hover:border-zinc-500 hover:bg-zinc-800`);var i=L(r),a=L(i),o=L(a),s=L(o),c=L(s),l=L(c,!0);A(c);var u=R(c,2),d=L(u,!0);A(u),A(s);var f=R(s,2),p=L(f,!0);A(f),A(o),A(a);var m=R(a,2),h=e=>{var n=Ya();Gr(n,21,()=>H(t).Tags??[],Vr,(e,t)=>{var n=Ja(),r=L(n,!0);A(n),z(()=>q(r,H(t).Name)),K(e,n)}),A(n),K(e,n)};J(m,e=>{(H(t).Tags??[]).length>0&&e(h)}),A(i);var g=R(i,2),_=L(g),v=R(L(_),2),y=L(v,!0);A(v),A(_);var b=R(_,2),x=R(L(b),2),S=L(x,!0);A(x),A(b),A(g);var C=R(g,2),w=e=>{var n=Za(),r=R(L(n),2);Gr(r,21,()=>H(t).Tables??[],Vr,(e,t)=>{var n=Xa(),r=L(n),i=L(r,!0);A(r);var a=R(r,2),o=L(a),s=e=>{var n=Pr();z(e=>q(n,e),[()=>H(t).fields.join(`, `)]),K(e,n)},c=e=>{K(e,Pr(`No exposed fields`))};J(o,e=>{(H(t).fields??[]).length>0?e(s):e(c,-1)}),A(a),A(n),z(()=>q(i,H(t).table)),K(e,n)}),A(r),A(n),K(e,n)};J(C,e=>{(H(t).Tables??[]).length>0&&e(w)}),A(r),A(n),z((e,r)=>{X(n,`href`,e),q(l,H(t).ID),q(d,H(t).Type),q(p,H(t).Description||`No description provided.`),q(y,H(t).Tables?.length??0),q(S,r)},[()=>Ki(`/servers/${H(t).ID}`),()=>(H(t).Tables??[]).reduce((e,t)=>e+(t.fields?.length??0),0)]),K(e,n)}),A(n),K(e,n)};J(o,e=>{t.loading?e(s):t.error?e(c,1):t.servers.length===0?e(l,2):e(u,-1)}),A(n),z(()=>q(a,t.servers.length)),K(e,n),Ge()}var no=G(`
Servers
Bookmarks
Schema fields
Recent queries
`);function ro(e,t){We(t,!1);let n=F([]),r=F(``),i=F(!0),a=F([]),o=F(``),s=F(!0),c=F(``),l=F(``),u=F([]),d=F(``),f=F(!0),p=`rounded-lg border border-zinc-700/80 bg-zinc-800/90`;function m(e,t){return`${e} ${t}`}function h(){return H(n).reduce((e,t)=>e+(t.Tables??[]).reduce((e,t)=>e+(t.fields?.length??0),0),0)}function g(e,t,n,r){return e?`Loading ${r}`:t||(n===0?`No ${r} available`:m(n,r))}function _(e){I(r,e),I(o,e),I(d,e),I(i,!1),I(s,!1),I(f,!1)}function v(e,t,n,r,i){e.status===`fulfilled`?t(e.value):n(Q(e.reason,i)),r()}async function y(){let e;try{e=await ji(async e=>Promise.allSettled([Mi(e),Fi(e),Ii(e)]))}catch(e){_(Q(e,`Failed to load dashboard`));return}e&&(v(e[0],e=>{I(n,e.targets??[]),I(r,``)},e=>{I(r,e)},()=>{I(i,!1)},`Failed to load servers`),v(e[1],e=>{I(a,e.bookmarks??[]),I(o,``)},e=>{I(o,e)},()=>{I(s,!1)},`Failed to load bookmarks`),v(e[2],e=>{I(u,e.queries??[]),I(d,``)},e=>{I(d,e)},()=>{I(f,!1)},`Failed to load queries`))}async function b(e){I(c,e.id);try{let t=await ji(t=>Li(t,e.target_id,e.query));if(!t)return;I(o,``),Ji(`/servers/${e.target_id}/${t.query_id}`)}catch(e){I(o,Q(e,`Failed to run bookmark`))}finally{I(c,``)}}async function x(e){I(l,e.id);try{let t=await ji(t=>Ui(t,e.id));if(!t&&t!==void 0)return;I(a,H(a).filter(t=>t.id!==e.id)),I(o,``)}catch(e){I(o,Q(e,`Failed to delete bookmark`))}finally{I(l,``)}}gi(y),mi();var S=no();Zr(`1eem88v`,e=>{kn(()=>{tn.title=`Database Gateway`})});var C=L(S),w=L(C),T=L(w);to(L(T),{get loading(){return H(i)},get error(){return H(r)},get servers(){return H(n)}}),A(T);var ee=R(T,2);Pa(L(ee),{get loading(){return H(s)},get error(){return H(o)},get bookmarks(){return H(a)},get runningBookmarkID(){return H(c)},get deletingBookmarkID(){return H(l)},onRun:b,onDelete:x}),A(ee),A(w);var te=R(w,2);Wa(L(te),{get loading(){return H(f)},get error(){return H(d)},get queries(){return H(u)}}),A(te),A(C);var ne=R(C,2);Y(ne,1,`rounded-xl border border-zinc-700/90 bg-zinc-900/90 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl grid gap-3.5 p-3.5 xl:grid-cols-[minmax(0,1.3fr)_minmax(0,1fr)]`);var re=L(ne),ie=L(re);Y(ie,1,`${p} p-2.5`);var ae=R(L(ie),2),oe=L(ae,!0);A(ae);var se=R(ae,2),ce=L(se,!0);A(se),A(ie);var le=R(ie,2);Y(le,1,`${p} p-2.5`);var ue=R(L(le),2),de=L(ue,!0);A(ue);var fe=R(ue,2),pe=L(fe,!0);A(fe),A(le);var me=R(le,2);Y(me,1,`${p} p-2.5`);var he=R(L(me),2),ge=L(he,!0);A(he);var _e=R(he,2),ve=L(_e);A(_e),A(me);var ye=R(me,2);Y(ye,1,`${p} p-2.5`);var be=R(L(ye),2),xe=L(be,!0);A(be);var Se=R(be,2),Ce=L(Se,!0);A(Se),A(ye),A(re),A(ne),A(S),z((e,t,r,i)=>{q(oe,H(n).length),q(ce,e),q(de,H(a).length),q(pe,t),q(ge,r),q(ve,`Across ${H(n).length??``} configured targets`),q(xe,H(u).length),q(Ce,i)},[()=>g(H(i),H(r),H(n).length,`servers`),()=>g(H(s),H(o),H(a).length,`bookmarks`),()=>h(),()=>g(H(f),H(d),H(u).length,`recent queries`)]),K(e,S),Ge()}var io=G(`
Error 404
Route unavailable

This page does not exist in Database Gateway.

The address you opened is not mapped to an active view. It may be outdated, incomplete, or outside the + routes exposed by this deployment.

Requested path
`);function ao(e,t){We(t,!0);let n=Z(t,`pathname`,3,`/`),r=`rounded-xl border border-zinc-700/90 bg-zinc-900/90 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl`,i=`rounded-lg border border-zinc-700/80 bg-zinc-800/90`;var a=io(),o=L(a),s=L(o);Y(s,1,`${r} p-6 md:p-8`);var c=L(s),l=L(c),u=L(l);Y(L(u),1,`${i} px-2 py-1 text-[11px] font-semibold uppercase tracking-[0.18em] text-zinc-300`),Pe(2),A(u),Pe(2),A(l);var d=R(l,2);Y(d,1,`${i} p-4`);var f=R(L(d),2),p=L(f,!0);A(f),A(d);var m=R(d,2),h=L(m);Y(h,1,`inline-flex min-h-8 items-center justify-center gap-2 rounded-lg border border-lime-300 bg-lime-200 px-2.5 text-[13px] font-semibold leading-none text-zinc-900 no-underline transition-colors hover:border-lime-300 hover:bg-lime-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-lime-200 justify-center`),Pe(2),A(m),A(c),A(s),Y(R(s,2),1,`${r} p-5 md:p-6`),A(o),A(a),z(e=>{q(p,n()),X(h,`href`,e)},[()=>Ki(`/`)]),K(e,a),Ge()}var oo=G(`
Loading query result...
`),so=G(`
`);function co(e,t){We(t,!0);let n=P(null),r=P(``),i=P(!0),a=P(``),o=`rounded-xl border border-zinc-700/90 bg-zinc-900/90 p-3 text-sm shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl`;async function s(){I(i,!0),I(r,``);try{let e=await ji(e=>Bi(e,t.queryID));if(!e)return;I(n,e,!0)}catch(e){I(r,Q(e,`Failed to load query result`),!0)}finally{I(i,!1)}}Tn(()=>{t.queryID&&t.queryID!==H(a)&&(I(a,t.queryID,!0),I(n,null),s())});var c=Fr(),l=un(c),u=e=>{var t=oo();Y(t,1,`${o} text-zinc-400`),K(e,t)},d=e=>{var t=so();Y(t,1,`${o} border-red-500/70 bg-red-950/20 text-red-300`);var n=L(t,!0);A(t),z(()=>q(n,H(r))),K(e,t)},f=e=>{{let r=_t(()=>Ki(`/`));pa(e,{title:`Query results`,subtitle:`Review a previously executed query without returning to the server page.`,get backHref(){return H(r)},backLabel:`Back to dashboard`,get query(){return H(n).query},get createdAt(){return H(n).created_at},get queryID(){return t.queryID},get table(){return H(n).table},get meta(){return H(n).meta},get status(){return H(n).state},get error(){return H(n).error}})}};J(l,e=>{H(i)?e(u):H(r)?e(d,1):H(n)&&e(f,2)}),K(e,c),Ge()}var lo=G(`
Loading server...
`),uo=G(`
`),fo=G(`
`),po=G(`
`),mo=G(`
`),ho=G(`
Saved bookmarks
Show
`),go=G(`
`),_o=G(`
`),vo=G(`
`),yo=G(`
`),bo=G(`
Exposed tables
Show
`);function xo(e,t){We(t,!0);let n=Z(t,`queryID`,3,``),r=Z(t,`initialQuery`,3,``),i=Z(t,`autoRun`,3,!1),a=P(null),o=P($t([])),s=P(``),c=P(``),l=P(null),u=P(!0),d=P(!1),f=P(!1),p=P(``),m=P(``),h=P(``),g=P(``),_=P(``),v=P(``),y=P(``),b=P(``),x=`rounded-xl border border-zinc-700/90 bg-zinc-900/90 shadow-[0_18px_42px_rgb(0_0_0_/_0.28)] backdrop-blur-xl`,S=`rounded-lg border border-zinc-700/80 bg-zinc-800/90`,C=`inline-flex min-h-8 items-center justify-center gap-2 rounded-lg border border-lime-300 bg-lime-200 px-2.5 text-[13px] font-semibold leading-none text-zinc-900 transition-colors hover:border-lime-300 hover:bg-lime-300 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-lime-200 disabled:cursor-not-allowed disabled:opacity-60`,w=`w-full rounded-md border border-zinc-600 bg-zinc-800 px-2 py-1.5 text-zinc-100 placeholder:text-zinc-400 focus:outline-none focus:ring-2 focus:ring-lime-200`;async function T(){let e=await ji(e=>Fi(e,t.serverID));return e?(I(o,e.bookmarks??[],!0),!0):!1}async function ee(){I(u,!0),I(g,``);try{let e=await ji(e=>Promise.all([Ni(e,t.serverID),Fi(e,t.serverID)]));if(!e)return;let[i,c]=e;if(I(a,i.target??null,!0),I(o,c.bookmarks??[],!0),!H(a)){I(g,`Server ${t.serverID} was not found`);return}if(n()){let e=await ji(e=>Bi(e,n()));if(!e)return;I(l,e,!0),I(s,e.query,!0)}else I(l,null),I(s,r()||``,!0)}catch(e){I(g,Q(e,`Failed to load server page`),!0)}finally{I(u,!1)}}function te(e){return`select ${(e.fields??[]).join(`, `)} + from ${e.table} limit 10`}function ne(e){return`select count(*) + from ${e.table}`}async function re(e=H(s)){let t=e.trim();if(!H(a)||!t){I(_,`Query is required`);return}I(d,!0),I(_,``),I(s,t,!0);try{let e=await ji(async e=>Li(e,H(a).ID,t));if(!e)return;Ji(`/servers/${H(a).ID}/${e.query_id}`)}catch(e){I(_,Q(e,`Failed to run query`),!0)}finally{I(d,!1)}}async function ie(){if(!H(a)||!H(s).trim()){I(v,`Run or enter a query before saving a bookmark`);return}I(f,!0),I(v,``);try{await ji(e=>Hi(e,H(a).ID,H(c).trim(),H(s).trim())),I(c,``),await T()}catch(e){I(v,Q(e,`Failed to save bookmark`),!0)}finally{I(f,!1)}}async function ae(e){I(m,e,!0),I(v,``);try{await ji(t=>Ui(t,e)),await T()}catch(e){I(v,Q(e,`Failed to delete bookmark`),!0)}finally{I(m,``)}}async function oe(e){I(p,e.id,!0);try{await re(e.query)}finally{I(p,``)}}function se(e){e.key===`Enter`&&e.shiftKey&&(e.preventDefault(),re())}async function ce(e){if(!(!H(l)?.id||H(h))){I(h,e,!0),I(_,``);try{let t=await ji(t=>Vi(t,H(l).id,e));if(!t?.url)return;window.location.assign(t.url)}catch(t){I(_,Q(t,`Failed to prepare ${e.toUpperCase()} export`),!0)}finally{I(h,``)}}}Tn(()=>{let e=`${t.serverID}:${n()}:${r()}:${i()}`;t.serverID&&e!==H(y)&&(I(y,e),I(a,null),I(o,[],!0),I(s,n()?``:r()||``,!0),I(c,``),I(l,null),I(_,``),I(v,``),I(b,``),ee())}),Tn(()=>{let e=`${t.serverID}:${n()}:${r()}:${i()}`;!n()&&H(a)&&i()&&r().trim()&&H(b)!==e&&!H(d)&&(I(b,e),re(r()))});var le=Fr(),ue=un(le),de=e=>{var t=lo();Y(t,1,`${x} p-3 text-sm text-zinc-400`),K(e,t)},fe=e=>{var t=uo();Y(t,1,`${x} border-red-500/70 bg-red-950/20 p-3 text-sm text-red-300`);var n=L(t,!0);A(t),z(()=>q(n,H(g))),K(e,t)},pe=e=>{var t=bo(),n=L(t);Y(n,1,`${x} w-full p-3`);var r=L(n),i=L(r),u=L(i),g=L(u),y=L(g,!0);A(g);var b=R(g,2),T=L(b,!0);A(b);var ee=R(b,2),le=e=>{var t=fo(),n=L(t,!0);A(t),z(()=>q(n,H(a).Description)),K(e,t)};J(ee,e=>{H(a).Description&&e(le)});var ue=R(ee,2);Gr(ue,21,()=>H(a).Tags??[],Vr,(e,t)=>{var n=po(),r=L(n,!0);A(n),z(()=>q(r,H(t).Name)),K(e,n)}),A(ue),A(u);var de=R(u,2);Y(de,1,`${S} overflow-hidden`);var fe=L(de),pe=L(fe),me=R(L(pe),2),he=L(me);A(me),A(pe),Pe(2),A(fe);var ge=R(fe,2),_e=L(ge);Gr(_e,21,()=>H(a).Tables??[],Vr,(e,t)=>{var n=mo();Y(n,1,`${S} flex flex-wrap items-center justify-between gap-2 p-2.5`);var r=L(n),i=L(r),a=L(i,!0);A(i),Gr(R(i,2),17,()=>H(t).fields??[],Vr,(e,n,r)=>{var i=fo(),a=L(i);A(i),z(()=>q(a,`${H(n)??``}${r<(H(t).fields?.length??0)-1?`,`:``}`)),K(e,i)}),A(r);var o=R(r,2),s=L(o);Y(s,1,`${C} px-2 py-1 text-xs`);var c=R(s,2);Y(c,1,`${C} px-2 py-1 text-xs`),A(o),A(n),z(()=>q(a,H(t).table)),W(`click`,s,()=>re(te(H(t)))),W(`click`,c,()=>re(ne(H(t)))),K(e,n)}),A(_e),A(ge),A(de);var ve=R(de,2),ye=e=>{var t=ho();Y(t,1,`${S} overflow-hidden`);var n=L(t),r=L(n),i=R(L(r),2),a=L(i);A(i),A(r),Pe(2),A(n);var s=R(n,2);Aa(L(s),{get bookmarks(){return H(o)},compact:!0,emptyText:`No bookmarks yet`,get runningBookmarkID(){return H(p)},get deletingBookmarkID(){return H(m)},onRun:oe,onDelete:e=>ae(e.id)}),A(s),A(t),z(()=>q(a,`${H(o).length??``} ${H(o).length===1?`bookmark`:`bookmarks`}`)),K(e,t)};J(ve,e=>{H(o).length>0&&e(ye)}),A(i),A(r),A(n);var be=R(n,2);Y(be,1,`${x} w-full p-3`);var xe=L(be),Se=L(xe);hn(Se),Y(Se,1,`${w} h-24`);var Ce=R(Se,2),we=L(Ce);Y(we,1,ei(C));var Te=L(we,!0);A(we);var Ee=R(we,2);Y(Ee,1,ei(C));var De=L(Ee,!0);A(Ee);var E=R(Ee,2);Y(E,1,ei(C));var Oe=L(E,!0);A(E);var ke=R(E,4);oi(ke),Y(ke,1,`${w} w-full md:ml-auto md:w-[200px] md:min-w-[200px] md:flex-none`);var Ae=R(ke,2);Y(Ae,1,`${C} whitespace-nowrap`);var je=L(Ae,!0);A(Ae),A(Ce);var D=R(Ce,2),Me=e=>{var t=go(),n=L(t,!0);A(t),z(()=>q(n,H(_))),K(e,t)};J(D,e=>{H(_)&&e(Me)});var O=R(D,2),k=e=>{var t=_o(),n=L(t,!0);A(t),z(()=>q(n,H(v))),K(e,t)};J(O,e=>{H(v)&&e(k)}),A(xe),A(be);var Ne=R(be,2),Fe=e=>{var t=yo();Y(t,1,`${x} w-full p-3`);var n=L(t),r=L(n),i=L(r);$(i,{title:`ID`,get value(){return H(l).id}});var a=R(i,2);{let e=_t(()=>H(l).state===`failed`?`red`:`green`);$(a,{title:`Status`,get value(){return H(l).state},get color(){return H(e)}})}var o=R(a,2);$(o,{title:`Created`,get value(){return H(l).created_at}});var s=R(o,2);$(s,{title:`Target`,get value(){return H(l).target_id}});var c=R(s,2);$(c,{title:`User`,get value(){return H(l).user_id}});var u=R(c,2);$(u,{title:`Rows`,get value(){return H(l).meta.rows_count}});var d=R(u,2);$(d,{title:`Columns`,get value(){return H(l).meta.columns_count}});var f=R(d,2);$(f,{title:`Vectors`,get value(){return H(l).meta.vectors_count}}),Gr(R(f,2),17,()=>H(l).meta.trace.records,Vr,(e,t)=>{{let n=_t(()=>H(t).duration/1e6);$(e,{get title(){return`${H(t).name??``} (ms)`},get value(){return H(n)}})}}),A(r);var p=R(r,2),m=e=>{var t=vo(),n=L(t,!0);A(t),z(()=>q(n,H(l).error)),K(e,t)},h=e=>{sa(e,{get table(){return H(l).table}})};J(p,e=>{H(l).error?e(m):e(h,-1)}),A(n),A(t),K(e,t)};J(Ne,e=>{H(l)&&e(Fe)}),A(t),z((e,t)=>{q(y,H(a).Type),q(T,H(a).ID),q(he,`${H(a).Tables?.length??0??``} ${(H(a).Tables?.length??0)===1?`table`:`tables`}`),we.disabled=H(d),q(Te,H(d)?`Running...`:`Run (Shift + Enter)`),Ee.disabled=e,q(De,H(h)===`json`?`Preparing JSON...`:`Get JSON`),E.disabled=t,q(Oe,H(h)===`csv`?`Preparing CSV...`:`Get CSV`),Ae.disabled=H(f),q(je,H(f)?`Saving...`:`Save bookmark`)},[()=>!H(l)||!!H(h),()=>!H(l)||!!H(h)]),W(`keydown`,Se,se),di(Se,()=>H(s),e=>I(s,e)),W(`click`,we,()=>re()),W(`click`,Ee,()=>ce(`json`)),W(`click`,E,()=>ce(`csv`)),di(ke,()=>H(c),e=>I(c,e)),W(`click`,Ae,ie),K(e,t)};J(ue,e=>{H(u)?e(de):H(g)?e(fe,1):H(a)&&e(pe,2)}),K(e,le),Ge()}Dr([`click`,`keydown`]);var So=G(`
`);function Co(e,t){We(t,!1);let n=F(),r=F(),i=F(),a=F(),o=F(),s=F({}),c=F(``),l=F(!0),u=F({pathname:`/`,searchParams:new URLSearchParams});function d(e){let t=e&&typeof e==`object`?e:{},n=t.id??t.ID??``;return{id:n,username:t.username??t.Username??n,role:t.role??t.Role??``}}function f(){I(u,qi())}async function p(){try{let e=await ji(e=>Pi(e));if(!e)return;I(s,d(e)),I(c,``)}catch(e){I(c,Q(e,`Failed to load profile`))}finally{I(l,!1)}}gi(()=>{ki(),f(),p();let e=()=>{f()};return window.addEventListener(`popstate`,e),()=>{window.removeEventListener(`popstate`,e)}}),An(()=>H(u),()=>{I(n,Math.max(1,Number.parseInt(H(u).searchParams.get(`page`)||`1`,10)||1))}),An(()=>H(u),()=>{I(r,Yi(H(u).pathname,`/admin/requests/:requestID`))}),An(()=>H(u),()=>{I(i,Yi(H(u).pathname,`/servers/:serverID/:queryID`))}),An(()=>H(u),()=>{I(a,Yi(H(u).pathname,`/queries/:queryID`))}),An(()=>H(u),()=>{I(o,Yi(H(u).pathname,`/servers/:serverID`))}),jn(),mi();var m=So(),h=R(L(m),2),g=L(h);{let e=vt(()=>(H(u),U(()=>H(u).pathname.startsWith(`/admin/requests`))));na(g,{get loading(){return H(l)},get error(){return H(c)},get profile(){return H(s)},get isAdminRequestsPage(){return H(e)}})}var _=R(g,2),v=L(_),y=e=>{ga(e,{get requestID(){return H(r),U(()=>H(r).requestID)}})},b=e=>{Ta(e,{get page(){return H(n)}})},x=e=>{{let t=vt(()=>(H(u),U(()=>H(u).searchParams.get(`query`)||``))),n=vt(()=>(H(u),U(()=>H(u).searchParams.get(`autorun`)===`1`)));xo(e,{get serverID(){return H(i),U(()=>H(i).serverID)},get queryID(){return H(i),U(()=>H(i).queryID)},get initialQuery(){return H(t)},get autoRun(){return H(n)}})}},S=e=>{{let t=vt(()=>(H(u),U(()=>H(u).searchParams.get(`query`)||``))),n=vt(()=>(H(u),U(()=>H(u).searchParams.get(`autorun`)===`1`)));xo(e,{get serverID(){return H(o),U(()=>H(o).serverID)},get initialQuery(){return H(t)},get autoRun(){return H(n)}})}},C=e=>{co(e,{get queryID(){return H(a),U(()=>H(a).queryID)}})},w=e=>{ro(e,{})},T=e=>{ao(e,{get pathname(){return H(u),U(()=>H(u).pathname)}})};J(v,e=>{H(r)?e(y):(H(u),U(()=>H(u).pathname===`/admin/requests`)?e(b,1):H(i)?e(x,2):H(o)?e(S,3):H(a)?e(C,4):(H(u),U(()=>H(u).pathname===`/`)?e(w,5):e(T,-1)))}),A(_),A(h),A(m),K(e,m),Ge()}Ir(Co,{target:document.getElementById(`app`)}); \ No newline at end of file diff --git a/internal/facade/ui/dist/index.html b/internal/facade/ui/dist/index.html index 3d2a90c..1a482e7 100644 --- a/internal/facade/ui/dist/index.html +++ b/internal/facade/ui/dist/index.html @@ -22,8 +22,8 @@ Database Gateway - - + +
diff --git a/internal/facade/ui/embed.go b/internal/facade/ui/embed.go index 542aa3c..cdd0e2e 100644 --- a/internal/facade/ui/embed.go +++ b/internal/facade/ui/embed.go @@ -18,6 +18,7 @@ package ui import ( "embed" + "fmt" "io/fs" ) @@ -26,10 +27,14 @@ var files embed.FS //nolint:gochecknoglobals var DistFS = func() fs.FS { - fsys, err := fs.Sub(files, "dist") + return subDistFS(files, "dist") +}() + +func subDistFS(source fs.FS, dir string) fs.FS { + fsys, err := fs.Sub(source, dir) if err != nil { - panic(err) + panic(fmt.Errorf("create embedded ui fs from %q: %w", dir, err)) } return fsys -}() +} diff --git a/internal/facade/ui/embed_test.go b/internal/facade/ui/embed_test.go new file mode 100644 index 0000000..27b35ad --- /dev/null +++ b/internal/facade/ui/embed_test.go @@ -0,0 +1,57 @@ +// Database Gateway provides access to servers with ACL for safe and restricted database interactions. +// Copyright (C) 2024 Kirill Zhuravlev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package ui //nolint:testpackage + +import ( + "fmt" + "testing" + "testing/fstest" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestSubDistFSPanicIncludesOperationAndPath(t *testing.T) { + t.Parallel() + + const dir = "../dist" + + panicValue := requirePanic(t, func() { + subDistFS(fstest.MapFS{}, dir) + }) + + message := fmt.Sprint(panicValue) + assert.Contains(t, message, "create embedded ui fs") + assert.Contains(t, message, dir) +} + +func requirePanic(t *testing.T, fn func()) any { + t.Helper() + + var panicValue any + func() { + defer func() { + panicValue = recover() + }() + + fn() + }() + + require.NotNil(t, panicValue) + + return panicValue +} diff --git a/internal/parser/classification_test.go b/internal/parser/classification_test.go new file mode 100644 index 0000000..d239f65 --- /dev/null +++ b/internal/parser/classification_test.go @@ -0,0 +1,113 @@ +// Database Gateway provides access to servers with ACL for safe and restricted database interactions. +// Copyright (C) 2024 Kirill Zhuravlev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package parser_test + +import ( + "testing" + + "github.com/kazhuravlev/database-gateway/internal/parser" + "github.com/stretchr/testify/require" +) + +func TestParseSelectClassifiesClauseColumns(t *testing.T) { + t.Parallel() + + vecs, err := parser.Parse(` +SELECT target_col +FROM clients +WHERE filter_col = 1 +GROUP BY group_col +ORDER BY sort_col +`) + require.NoError(t, err) + require.Len(t, vecs, 1) + + sel, ok := vecs[0].(parser.SelectVec) + require.True(t, ok) + require.Equal(t, "clients", sel.Tbl) + require.Equal(t, []string{"target_col"}, sel.Target) + require.Equal(t, []string{"filter_col"}, sel.Filter) + require.Equal(t, []string{"group_col"}, sel.Group) + require.Equal(t, []string{"sort_col"}, sel.Sort) + require.NotContains(t, sel.Target, "filter_col") + require.NotContains(t, sel.Target, "group_col") + require.NotContains(t, sel.Target, "sort_col") +} + +func TestParseReturningColumnsAreNotClassifiedAsTargets(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + query string + assertions func(t *testing.T, vec parser.Vector) + }{ + { + name: "insert", + query: `INSERT INTO clients(name) VALUES ('alice') RETURNING id`, + assertions: func(t *testing.T, vec parser.Vector) { + t.Helper() + + ins, ok := vec.(parser.InsertVec) + require.True(t, ok) + require.Equal(t, []string{"name"}, ins.Target) + require.Equal(t, []string{"id"}, ins.Returning) + require.NotContains(t, ins.Target, "id") + }, + }, + { + name: "update", + query: `UPDATE clients SET name = 'alice' WHERE email = 'a@example.com' RETURNING id`, + assertions: func(t *testing.T, vec parser.Vector) { + t.Helper() + + upd, ok := vec.(parser.UpdateVec) + require.True(t, ok) + require.Equal(t, []string{"name"}, upd.Target) + require.Equal(t, []string{"email"}, upd.Filter) + require.Equal(t, []string{"id"}, upd.Returning) + require.NotContains(t, upd.Target, "email") + require.NotContains(t, upd.Target, "id") + }, + }, + { + name: "delete", + query: `DELETE FROM clients WHERE email = 'a@example.com' RETURNING id`, + assertions: func(t *testing.T, vec parser.Vector) { + t.Helper() + + del, ok := vec.(parser.DeleteVec) + require.True(t, ok) + require.Empty(t, del.Target) + require.Equal(t, []string{"email"}, del.Filter) + require.Equal(t, []string{"id"}, del.Returning) + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + vecs, err := parser.Parse(tc.query) + require.NoError(t, err) + require.Len(t, vecs, 1) + + tc.assertions(t, vecs[0]) + }) + } +} diff --git a/internal/parser/op_delete.go b/internal/parser/op_delete.go index 976f639..c61b217 100644 --- a/internal/parser/op_delete.go +++ b/internal/parser/op_delete.go @@ -25,33 +25,29 @@ import ( ) type DeleteVec struct { //nolint:recvcheck - Tbl string - Target []string - Filter []string + Tbl string + Target []string + Filter []string + Returning []string } func (s *DeleteVec) Columns() []string { - columns := just.SliceUniq(slices.Concat(s.Target, s.Filter)) + columns := just.SliceUniq(slices.Concat(s.Target, s.Filter, s.Returning)) return columns } func (DeleteVec) isVector() {} -func handleDelete(req *pg.DeleteStmt) ([]Vector, error) { //nolint:gocyclo +func handleDelete(req *pg.DeleteStmt) ([]Vector, error) { if req.UsingClause != nil || req.GetWithClause() != nil { return nil, fmt.Errorf("unknown clause: %w", ErrNotImplemented) } - tables := NewTables("public") - rel := req.GetRelation() - fqTableName, err := tables.Put(rel.GetCatalogname(), rel.GetSchemaname(), rel.GetRelname(), rel.GetAlias().GetAliasname()) + tables, fqTableName, err := deleteTables(req) if err != nil { - return nil, fmt.Errorf("failed to add table: %w", err) - } - if err := tables.Finalize(); err != nil { - return nil, fmt.Errorf("failed to finalize tables: %w", err) + return nil, err } retCols, err := pNodes2Columns(req.GetReturningList(), fqTableName) @@ -64,27 +60,43 @@ func handleDelete(req *pg.DeleteStmt) ([]Vector, error) { //nolint:gocyclo return nil, fmt.Errorf("parse where: %w", err) } - allColumns := slices.Concat(retCols, whereColumns) - - table2target := make(map[string]Columns, len(allColumns)) - table2target[fqTableName] = nil - for _, column := range allColumns { - tbl, ok := tables.Get(column.Table()) - if !ok { - return nil, fmt.Errorf("table not found: %s", column.Table()) //nolint:err113 - } + allTables, err := tables.GetAll() + if err != nil { + return nil, fmt.Errorf("failed to get all tables: %w", err) + } - table2target[tbl] = append(table2target[tbl], column) + table2filter, err := columnsByTable(tables, allTables, whereColumns) + if err != nil { + return nil, err + } + table2returning, err := columnsByTable(tables, allTables, retCols) + if err != nil { + return nil, err } - vectors := make([]Vector, 0, len(table2target)) - for tbl, cols := range table2target { + vectors := make([]Vector, 0, len(allTables)) + for _, tbl := range allTables { vectors = append(vectors, DeleteVec{ - Tbl: tbl, - Target: cols.ListNames(), - Filter: nil, // TODO: impl + Tbl: tbl, + Target: nil, + Filter: table2filter[tbl].ListNames(), + Returning: table2returning[tbl].ListNames(), }) } return vectors, nil } + +func deleteTables(req *pg.DeleteStmt) (*Tables, string, error) { + tables := NewTables("public") + rel := req.GetRelation() + fqTableName, err := tables.Put(rel.GetCatalogname(), rel.GetSchemaname(), rel.GetRelname(), rel.GetAlias().GetAliasname()) + if err != nil { + return nil, "", fmt.Errorf("failed to add table: %w", err) + } + if err := tables.Finalize(); err != nil { + return nil, "", fmt.Errorf("failed to finalize tables: %w", err) + } + + return tables, fqTableName, nil +} diff --git a/internal/parser/op_delete_test.go b/internal/parser/op_delete_test.go index 4bd43f6..deb9cc5 100644 --- a/internal/parser/op_delete_test.go +++ b/internal/parser/op_delete_test.go @@ -82,3 +82,20 @@ func TestParseDeleteInvalid(t *testing.T) { test("DELETE FROM t1 USING t2", "using_clause") test("DELETE FROM t1 WHERE EXISTS (SELECT 1 FROM t2)", "exists_subquery") } + +func TestParseDeleteAliasQualifiedColumns(t *testing.T) { + t.Parallel() + + vecs, err := parser.Parse( + "DELETE FROM clients AS c WHERE c.email = 'a@example.com' RETURNING c.id, c.created_at", + ) + require.NoError(t, err) + require.Len(t, vecs, 1) + + del, ok := vecs[0].(parser.DeleteVec) + require.True(t, ok) + require.Equal(t, "clients", del.Tbl) + require.Empty(t, del.Target) + require.Equal(t, []string{"email"}, del.Filter) + require.Equal(t, []string{"id", "created_at"}, del.Returning) +} diff --git a/internal/parser/op_insert.go b/internal/parser/op_insert.go index cabd77d..345133f 100644 --- a/internal/parser/op_insert.go +++ b/internal/parser/op_insert.go @@ -18,19 +18,19 @@ package parser import ( "fmt" - "slices" "github.com/kazhuravlev/just" pg "github.com/pganalyze/pg_query_go/v6" ) type InsertVec struct { //nolint:recvcheck - Tbl string - Target []string + Tbl string + Target []string + Returning []string } func (s *InsertVec) Columns() []string { - columns := just.SliceUniq(s.Target) + columns := just.SliceUniq(append(append([]string{}, s.Target...), s.Returning...)) return columns } @@ -116,8 +116,6 @@ func handleInsert(req *pg.InsertStmt) ([]Vector, error) { //nolint:gocyclo,gocog return nil, fmt.Errorf("parse returning columns: %w", err) } - allColumns := slices.Concat(targetCols, retCols) - if confl := req.GetOnConflictClause(); confl != nil { switch confl.GetAction() { //nolint:exhaustive default: @@ -136,30 +134,35 @@ func handleInsert(req *pg.InsertStmt) ([]Vector, error) { //nolint:gocyclo,gocog return nil, fmt.Errorf("parse conflict columns: %w", err) } - targetCols, err := pNodes2Columns(confl.GetTargetList(), fqTableName) + conflictTargetCols, err := pNodes2Columns(confl.GetTargetList(), fqTableName) if err != nil { return nil, fmt.Errorf("parse target columns: %w", err) } - allColumns = slices.Concat(allColumns, conflictCols, targetCols) + targetCols = append(targetCols, conflictCols...) + targetCols = append(targetCols, conflictTargetCols...) } - table2target := make(map[string]Columns, len(allColumns)) - table2target[fqTableName] = nil - for _, column := range allColumns { - tbl, ok := tables.Get(column.Table()) - if !ok { - return nil, fmt.Errorf("table not found: %s", column.Table()) //nolint:err113 - } + allTables, err := tables.GetAll() + if err != nil { + return nil, fmt.Errorf("failed to get all tables: %w", err) + } - table2target[tbl] = append(table2target[tbl], column) + table2target, err := columnsByTable(tables, allTables, targetCols) + if err != nil { + return nil, err + } + table2returning, err := columnsByTable(tables, allTables, retCols) + if err != nil { + return nil, err } - vectors := make([]Vector, 0, len(table2target)) - for tbl, cols := range table2target { + vectors := make([]Vector, 0, len(allTables)) + for _, tbl := range allTables { vectors = append(vectors, InsertVec{ - Tbl: tbl, - Target: cols.ListNames(), + Tbl: tbl, + Target: table2target[tbl].ListNames(), + Returning: table2returning[tbl].ListNames(), }) } diff --git a/internal/parser/op_insert_test.go b/internal/parser/op_insert_test.go index c683479..ae733ed 100644 --- a/internal/parser/op_insert_test.go +++ b/internal/parser/op_insert_test.go @@ -69,20 +69,25 @@ func TestParseInsertValid(t *testing.T) { func TestParseInsertInvalid(t *testing.T) { t.Parallel() - test := func(input, name string) { + test := func(input, name, expErr string) { t.Helper() t.Run(name, func(t *testing.T) { t.Helper() _, err := parser.Parse(input) require.Error(t, err) + require.ErrorIs(t, err, parser.ErrNotImplemented) + require.ErrorContains(t, err, expErr) }) } - test("insert into t1(c1) values(1) returning *", "star_returning") - test("insert into t1(c1) select t2.c1 from t2", "insert_from_select") + test("insert into t1(c1) values(1) returning *", "star_returning", "star expressions") + test("insert into t1(c1) select t2.c1 from t2", "insert_from_select", "unknown clause") - test("INSERT INTO t1(c1) VALUES (5 * 10)", "multiplication") - test("INSERT INTO t1(c1) VALUES ('prefix_' || 'suffix')", "concatenation") + test("INSERT INTO t1(c1) VALUES (5 * 10)", "multiplication", "unknown node list node type") + test("INSERT INTO t1(c1) VALUES ('prefix_' || 'suffix')", "concatenation", "unknown node list node type") - test("WITH source AS (SELECT 1 AS id, 'test' AS name) INSERT INTO table1(id, name) SELECT id, name FROM source", "with_clause") + test("WITH source AS (SELECT 1 AS id, 'test' AS name) INSERT INTO table1(id, name) SELECT id, name FROM source", "with_clause", "unknown clause") + test("INSERT INTO t1(id, name) VALUES (1, 'test') ON CONFLICT ON CONSTRAINT t1_id_key DO NOTHING", "on_conflict_constraint", "unknown on-conflict clause") + test("INSERT INTO t1(id, active) VALUES (1, true) ON CONFLICT (id) WHERE active DO NOTHING", "on_conflict_partial_index_where", "unknown on-conflict clause") + test("INSERT INTO t1(email) VALUES ('a@example.com') ON CONFLICT (lower(email)) DO NOTHING", "on_conflict_expression_index_elem", "unknown index elem") } diff --git a/internal/parser/op_select.go b/internal/parser/op_select.go index 851201d..5847117 100644 --- a/internal/parser/op_select.go +++ b/internal/parser/op_select.go @@ -156,6 +156,36 @@ func parseSelectJoinColumns(node *pg.Node) (Columns, error) { } } +func validateSelectLimit(name string, node *pg.Node, allowAll bool) error { + if node == nil { + return nil + } + + switch node := node.GetNode().(type) { + default: + return fmt.Errorf("%s type (%T): %w", name, node, ErrNotImplemented) + case *pg.Node_AConst: + if node.AConst.GetIsnull() { + if allowAll { + return nil + } + + return fmt.Errorf("%s all: %w", name, ErrNotImplemented) + } + + switch val := node.AConst.GetVal().(type) { + default: + return fmt.Errorf("%s value type (%T): %w", name, val, ErrNotImplemented) + case *pg.A_Const_Ival: + if val.Ival.GetIval() < 0 { + return fmt.Errorf("%s cannot be negative", name) //nolint:err113 + } + + return nil + } + } +} + func handleSelect(sel *pg.SelectStmt) ([]Vector, error) { //nolint:gocyclo,gocognit,cyclop,funlen,maintidx if sel.DistinctClause != nil || sel.GetIntoClause() != nil || @@ -186,7 +216,7 @@ func handleSelect(sel *pg.SelectStmt) ([]Vector, error) { //nolint:gocyclo,gocog return nil, fmt.Errorf("failed to finalize tables: %w", err) } - var allColumns Columns + var targetColumns Columns // handle target fields for _, target := range sel.GetTargetList() { switch node := target.GetNode().(type) { @@ -207,7 +237,7 @@ func handleSelect(sel *pg.SelectStmt) ([]Vector, error) { //nolint:gocyclo,gocog return nil, fmt.Errorf("parse column: %w", err) } - allColumns = append(allColumns, column) + targetColumns = append(targetColumns, column) case *pg.Node_FuncCall: funcCall := node.FuncCall if funcCall.GetOver() != nil || funcCall.GetAggFilter() != nil || funcCall.AggOrder != nil { @@ -228,6 +258,10 @@ func handleSelect(sel *pg.SelectStmt) ([]Vector, error) { //nolint:gocyclo,gocog } } + if len(funcCall.GetArgs()) > 1 { + return nil, fmt.Errorf("function arguments: %w", ErrNotImplemented) + } + for _, node := range funcCall.GetArgs() { switch node := node.GetNode().(type) { default: @@ -241,27 +275,29 @@ func handleSelect(sel *pg.SelectStmt) ([]Vector, error) { //nolint:gocyclo,gocog if err != nil { return nil, fmt.Errorf("parse column: %w", err) } - allColumns = append(allColumns, column) + targetColumns = append(targetColumns, column) } } } } } + var filterColumns Columns if sel.GetWhereClause() != nil { whereColumns, err := parseWhereClause(sel.GetWhereClause()) if err != nil { return nil, fmt.Errorf("parse where clause: %w", err) } - allColumns = append(allColumns, whereColumns...) + filterColumns = append(filterColumns, whereColumns...) } joinColumns, err := parseSelectJoinColumns(from) if err != nil { return nil, err } - allColumns = append(allColumns, joinColumns...) + filterColumns = append(filterColumns, joinColumns...) + var sortColumns Columns for _, node := range sel.GetSortClause() { switch node := node.GetNode().(type) { default: @@ -280,15 +316,26 @@ func handleSelect(sel *pg.SelectStmt) ([]Vector, error) { //nolint:gocyclo,gocog return nil, fmt.Errorf("parse column: %w", err) } - allColumns = append(allColumns, column) + sortColumns = append(sortColumns, column) } } } - // NOTE: Implement checking of sel.LimitOffset - // NOTE: Implement checking of sel.LimitCount - // NOTE: Implement checking of sel.LimitOption + switch sel.GetLimitOption() { //nolint:exhaustive + default: + return nil, fmt.Errorf("limit option: %w", ErrNotImplemented) + case pg.LimitOption_LIMIT_OPTION_DEFAULT, pg.LimitOption_LIMIT_OPTION_COUNT: + } + + if err := validateSelectLimit("limit", sel.GetLimitCount(), true); err != nil { + return nil, err + } + + if err := validateSelectLimit("offset", sel.GetLimitOffset(), false); err != nil { + return nil, err + } + var groupColumns Columns for _, node := range sel.GetGroupClause() { switch node := node.GetNode().(type) { default: @@ -299,7 +346,7 @@ func handleSelect(sel *pg.SelectStmt) ([]Vector, error) { //nolint:gocyclo,gocog return nil, fmt.Errorf("parse group by column: %w", err) } - allColumns = append(allColumns, column) + groupColumns = append(groupColumns, column) } } @@ -308,32 +355,31 @@ func handleSelect(sel *pg.SelectStmt) ([]Vector, error) { //nolint:gocyclo,gocog return nil, fmt.Errorf("failed to get all tables: %w", err) } - table2target := make(map[string]Columns, len(allTables)+len(allColumns)) - for _, tableName := range allTables { - table2target[tableName] = nil + table2target, err := columnsByTable(tables, allTables, targetColumns) + if err != nil { + return nil, err } - - for _, column := range allColumns { - if column.Table() == "" && tables.SourcesCount() > 1 { - return nil, fmt.Errorf("ambiguous column reference (%s): %w", column.column, ErrNotImplemented) - } - - tbl, ok := tables.Get(column.Table()) - if !ok { - return nil, fmt.Errorf("table not found: %s", column.Table()) //nolint:err113 - } - - table2target[tbl] = append(table2target[tbl], column) + table2filter, err := columnsByTable(tables, allTables, filterColumns) + if err != nil { + return nil, err + } + table2group, err := columnsByTable(tables, allTables, groupColumns) + if err != nil { + return nil, err + } + table2sort, err := columnsByTable(tables, allTables, sortColumns) + if err != nil { + return nil, err } - vectors := make([]Vector, 0, len(table2target)) - for tbl, cols := range table2target { + vectors := make([]Vector, 0, len(allTables)) + for _, tbl := range allTables { vectors = append(vectors, SelectVec{ Tbl: tbl, - Target: cols.ListNames(), - Filter: nil, // TODO: impl - Group: nil, // TODO: impl - Sort: nil, // TODO: impl + Target: table2target[tbl].ListNames(), + Filter: table2filter[tbl].ListNames(), + Group: table2group[tbl].ListNames(), + Sort: table2sort[tbl].ListNames(), }) } diff --git a/internal/parser/op_select_test.go b/internal/parser/op_select_test.go index 7dce62e..fc031b6 100644 --- a/internal/parser/op_select_test.go +++ b/internal/parser/op_select_test.go @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package parser_test +package parser_test //nolint:exhaustruct import ( "sort" @@ -108,6 +108,144 @@ func TestParseSelectInvalid(t *testing.T) { fInvalid("SELECT id FROM clients UNION SELECT id FROM orders", "union_expression") } +func TestParseSelectLimitOffset(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + query string + wantErr bool + }{ + { + name: "limit", + query: `SELECT id FROM clients LIMIT 10`, + }, + { + name: "offset", + query: `SELECT id FROM clients OFFSET 5`, + }, + { + name: "limit_with_offset", + query: `SELECT id FROM clients LIMIT 10 OFFSET 5`, + }, + { + name: "limit_all", + query: `SELECT id FROM clients LIMIT ALL`, + }, + { + name: "negative_limit", + query: `SELECT id FROM clients LIMIT -1`, + wantErr: true, + }, + { + name: "negative_offset", + query: `SELECT id FROM clients OFFSET -1`, + wantErr: true, + }, + { + name: "param_limit", + query: `SELECT id FROM clients LIMIT $1`, + wantErr: true, + }, + { + name: "expression_limit", + query: `SELECT id FROM clients LIMIT 1 + 1`, + wantErr: true, + }, + { + name: "expression_offset", + query: `SELECT id FROM clients OFFSET 1 + 1`, + wantErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + _, err := parser.Parse(tc.query) + if tc.wantErr { + require.Error(t, err) + + return + } + + require.NoError(t, err) + }) + } +} + +func TestParseSelectFunctionEdgeCases(t *testing.T) { + t.Parallel() + + t.Run("invalid", func(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + query string + }{ + { + name: "disallowed_function", + query: `SELECT avg(total_sales) FROM sales_olap_42`, + }, + { + name: "multiple_arguments", + query: `SELECT sum(total_sales, tax) FROM sales_olap_42`, + }, + { + name: "nested_expression", + query: `SELECT sum(total_sales + tax) FROM sales_olap_42`, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + _, err := parser.Parse(tc.query) + require.Error(t, err) + }) + } + }) + + t.Run("qualified_columns", func(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + query string + }{ + { + name: "table_qualified", + query: `SELECT sum(sales_olap_42.total_sales) FROM sales_olap_42`, + }, + { + name: "schema_qualified", + query: `SELECT sum(public.sales_olap_42.total_sales) FROM public.sales_olap_42`, + }, + { + name: "alias_qualified", + query: `SELECT sum(s.total_sales) FROM public.sales_olap_42 AS s`, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + vecs, err := parser.Parse(tc.query) + require.NoError(t, err) + require.Len(t, vecs, 1) + + sel, ok := vecs[0].(parser.SelectVec) + require.True(t, ok) + require.Equal(t, []string{"total_sales"}, sel.Target) + }) + } + }) +} + func TestParseSelectJoinVectors(t *testing.T) { t.Parallel() diff --git a/internal/parser/op_update.go b/internal/parser/op_update.go index ec2eb99..032ad2c 100644 --- a/internal/parser/op_update.go +++ b/internal/parser/op_update.go @@ -26,13 +26,14 @@ import ( ) type UpdateVec struct { //nolint:recvcheck - Tbl string - Target []string - Filter []string + Tbl string + Target []string + Filter []string + Returning []string } func (s *UpdateVec) Columns() []string { - columns := just.SliceUniq(slices.Concat(s.Target, s.Filter)) + columns := just.SliceUniq(slices.Concat(s.Target, s.Filter, s.Returning)) return columns } @@ -191,24 +192,31 @@ func handleUpdate(req *pg.UpdateStmt) ([]Vector, error) { //nolint:gocyclo,cyclo return nil, fmt.Errorf("parse where clause: %w", err) } - allColumns := slices.Concat(targetCols, retCols, whereColumns) - - table2target := make(map[string]Columns, len(allColumns)) - for _, column := range allColumns { - tbl, ok := tables.Get(column.Table()) - if !ok { - return nil, fmt.Errorf("table not found: %s", column.Table()) //nolint:err113 - } + allTables, err := tables.GetAll() + if err != nil { + return nil, fmt.Errorf("failed to get all tables: %w", err) + } - table2target[tbl] = append(table2target[tbl], column) + table2target, err := columnsByTable(tables, allTables, targetCols) + if err != nil { + return nil, err + } + table2filter, err := columnsByTable(tables, allTables, whereColumns) + if err != nil { + return nil, err + } + table2returning, err := columnsByTable(tables, allTables, retCols) + if err != nil { + return nil, err } - vectors := make([]Vector, 0, len(table2target)) - for tbl, cols := range table2target { + vectors := make([]Vector, 0, len(allTables)) + for _, tbl := range allTables { vectors = append(vectors, UpdateVec{ - Tbl: tbl, - Target: cols.ListNames(), - Filter: nil, // TODO: impl + Tbl: tbl, + Target: table2target[tbl].ListNames(), + Filter: table2filter[tbl].ListNames(), + Returning: table2returning[tbl].ListNames(), }) } diff --git a/internal/parser/op_update_test.go b/internal/parser/op_update_test.go index ea2e8dc..0f88c07 100644 --- a/internal/parser/op_update_test.go +++ b/internal/parser/op_update_test.go @@ -75,3 +75,20 @@ func TestParseUpdateInvalid(t *testing.T) { test("UPDATE t1 SET c1 = (SELECT MAX(c1) FROM t2)", "subquery_in_target_field") test("UPDATE t1 SET c1 = 'value' WHERE id IN (SELECT id FROM t2 WHERE active = true)", "subquery_in_where") } + +func TestParseUpdateAliasQualifiedColumns(t *testing.T) { + t.Parallel() + + vecs, err := parser.Parse( + "UPDATE clients AS c SET name = 'alice' WHERE c.email = 'a@example.com' RETURNING c.id, c.created_at", + ) + require.NoError(t, err) + require.Len(t, vecs, 1) + + upd, ok := vecs[0].(parser.UpdateVec) + require.True(t, ok) + require.Equal(t, "clients", upd.Tbl) + require.Equal(t, []string{"name"}, upd.Target) + require.Equal(t, []string{"email"}, upd.Filter) + require.Equal(t, []string{"id", "created_at"}, upd.Returning) +} diff --git a/internal/parser/parser.go b/internal/parser/parser.go index a019807..0eb223c 100644 --- a/internal/parser/parser.go +++ b/internal/parser/parser.go @@ -229,3 +229,25 @@ func (c Columns) ListNames() []string { return res } + +func columnsByTable(tables *Tables, allTables []string, columns Columns) (map[string]Columns, error) { + res := make(map[string]Columns, len(allTables)) + for _, tableName := range allTables { + res[tableName] = nil + } + + for _, column := range columns { + if column.Table() == "" && tables.SourcesCount() > 1 { + return nil, fmt.Errorf("ambiguous column reference (%s): %w", column.column, ErrNotImplemented) + } + + tbl, ok := tables.Get(column.Table()) + if !ok { + return nil, fmt.Errorf("table not found: %s", column.Table()) //nolint:err113 + } + + res[tbl] = append(res[tbl], column) + } + + return res, nil +} diff --git a/internal/pgdb/pg_helpers.go b/internal/pgdb/pg_helpers.go index e3fa368..ef6389c 100644 --- a/internal/pgdb/pg_helpers.go +++ b/internal/pgdb/pg_helpers.go @@ -19,6 +19,7 @@ package pgdb import ( "context" "database/sql" + "errors" "fmt" "time" @@ -26,6 +27,8 @@ import ( "github.com/kazhuravlev/just" ) +var errNilContext = errors.New("context is nil") + func BuildDBDsn(cfg config.PostgresConfig) string { //nolint:gocritic return fmt.Sprintf( "host=%s port=%d user=%s password=%s dbname=%s sslmode=%s", @@ -38,7 +41,11 @@ func BuildDBDsn(cfg config.PostgresConfig) string { //nolint:gocritic ) } -func ConnectToPg(cfg config.PostgresConfig) (*sql.DB, error) { //nolint:gocritic +func ConnectToPg(ctx context.Context, cfg config.PostgresConfig) (*sql.DB, error) { //nolint:gocritic + if ctx == nil { + return nil, errNilContext + } + postgresDSN := BuildDBDsn(cfg) dbConn, err := sql.Open("postgres", postgresDSN) if err != nil { @@ -49,7 +56,7 @@ func ConnectToPg(cfg config.PostgresConfig) (*sql.DB, error) { //nolint:gocritic dbConn.SetConnMaxLifetime(5 * time.Minute) //nolint:mnd dbConn.SetMaxOpenConns(cfg.MaxPoolSize) - if err := dbConn.PingContext(context.TODO()); err != nil { + if err := dbConn.PingContext(ctx); err != nil { return nil, fmt.Errorf("ping postgres: %w", err) } diff --git a/internal/pgdb/pg_helpers_test.go b/internal/pgdb/pg_helpers_test.go new file mode 100644 index 0000000..94d6b4e --- /dev/null +++ b/internal/pgdb/pg_helpers_test.go @@ -0,0 +1,70 @@ +// Database Gateway provides access to servers with ACL for safe and restricted database interactions. +// Copyright (C) 2024 Kirill Zhuravlev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package pgdb_test + +import ( + "testing" + + "github.com/kazhuravlev/database-gateway/internal/config" + "github.com/kazhuravlev/database-gateway/internal/pgdb" + "github.com/stretchr/testify/assert" +) + +func TestBuildDBDsn(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + cfg config.PostgresConfig + want string + }{ + { + name: "ssl disabled", + cfg: config.PostgresConfig{ + Host: "localhost", + Port: 5432, + Username: "gateway", + Password: "secret", + Database: "metadata", + UseSSL: false, + MaxPoolSize: 5, + }, + want: "host=localhost port=5432 user=gateway password=secret dbname=metadata sslmode=disable", + }, + { + name: "ssl enabled", + cfg: config.PostgresConfig{ + Host: "db.example.com", + Port: 6432, + Username: "readonly", + Password: "password", + Database: "gateway", + UseSSL: true, + MaxPoolSize: 10, + }, + want: "host=db.example.com port=6432 user=readonly password=password dbname=gateway sslmode=prefer", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + assert.Equal(t, tt.want, pgdb.BuildDBDsn(tt.cfg)) + }) + } +} diff --git a/internal/policy/opa/authorizer_test.go b/internal/policy/opa/authorizer_test.go index d1596d9..a7b8fea 100644 --- a/internal/policy/opa/authorizer_test.go +++ b/internal/policy/opa/authorizer_test.go @@ -18,6 +18,8 @@ package opa_test import ( "context" + "os" + "path/filepath" "testing" "github.com/kazhuravlev/database-gateway/internal/policy/opa" @@ -94,3 +96,55 @@ func TestNewFailsForInvalidModule(t *testing.T) { require.Error(t, err) require.Nil(t, authz) } + +func TestLoadModulesFailsForUnreadablePolicyDir(t *testing.T) { + t.Parallel() + + dir := t.TempDir() + path := filepath.Join(dir, "policy-dir") + + modules, err := opa.LoadModules(path) + require.Error(t, err) + require.ErrorContains(t, err, "read policy dir") + require.Nil(t, modules) +} + +func TestLoadModulesFailsForEmptyPolicyDir(t *testing.T) { + t.Parallel() + + dir := t.TempDir() + + modules, err := opa.LoadModules(dir) + require.Error(t, err) + require.ErrorContains(t, err, "no .rego files found") + require.Nil(t, modules) +} + +func TestLoadModulesIgnoresNonRegoFiles(t *testing.T) { + t.Parallel() + + dir := t.TempDir() + require.NoError(t, os.WriteFile(filepath.Join(dir, "README.txt"), []byte("ignored"), 0o600)) + require.NoError(t, os.WriteFile(filepath.Join(dir, "policy.rego"), []byte(ExamplePolicySimple), 0o600)) + require.NoError(t, os.Mkdir(filepath.Join(dir, "nested.rego"), 0o700)) + + modules, err := opa.LoadModules(dir) + require.NoError(t, err) + require.Equal(t, map[string]string{ + "policy.rego": ExamplePolicySimple, + }, modules) +} + +func TestLoadModulesFailsForUnreadableRegoModule(t *testing.T) { + t.Parallel() + + dir := t.TempDir() + filename := filepath.Join(dir, "broken.rego") + require.NoError(t, os.Symlink(filepath.Join(dir, "missing.rego"), filename)) + + modules, err := opa.LoadModules(dir) + require.Error(t, err) + require.ErrorContains(t, err, "read policy module") + require.ErrorContains(t, err, filename) + require.Nil(t, modules) +} diff --git a/internal/storage/adapters.go b/internal/storage/adapters.go index a9a5e29..5673f29 100644 --- a/internal/storage/adapters.go +++ b/internal/storage/adapters.go @@ -16,7 +16,20 @@ package storage -import "github.com/kazhuravlev/database-gateway/internal/storage/jetgen/model" +import ( + "github.com/kazhuravlev/database-gateway/internal/config" + "github.com/kazhuravlev/database-gateway/internal/storage/jetgen/model" + "github.com/kazhuravlev/database-gateway/internal/uuid6" +) + +func adaptQueryResults(items []model.QueryResults) []QueryResult { + out := make([]QueryResult, 0, len(items)) + for i := range items { + out = append(out, adaptQueryResult(&items[i])) + } + + return out +} func adaptQueryResult(item *model.QueryResults) QueryResult { return QueryResult{ @@ -29,3 +42,23 @@ func adaptQueryResult(item *model.QueryResults) QueryResult { Response: item.Response, } } + +func adaptBookmarks(items []model.Bookmarks) []Bookmark { + out := make([]Bookmark, 0, len(items)) + for i := range items { + out = append(out, adaptBookmark(&items[i])) + } + + return out +} + +func adaptBookmark(item *model.Bookmarks) Bookmark { + return Bookmark{ + ID: uuid6.FromUUID(item.ID), + UserID: config.UserID(item.UserID), + TargetID: config.TargetID(item.TargetID), + Title: item.Title, + Query: item.Query, + CreatedAt: item.CreatedAt, + } +} diff --git a/internal/storage/adapters_test.go b/internal/storage/adapters_test.go new file mode 100644 index 0000000..ecc2417 --- /dev/null +++ b/internal/storage/adapters_test.go @@ -0,0 +1,132 @@ +// Database Gateway provides access to servers with ACL for safe and restricted database interactions. +// Copyright (C) 2024 Kirill Zhuravlev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package storage //nolint:testpackage + +import ( + "testing" + "time" + + "github.com/google/uuid" + "github.com/kazhuravlev/database-gateway/internal/config" + "github.com/kazhuravlev/database-gateway/internal/storage/jetgen/model" + "github.com/kazhuravlev/database-gateway/internal/structs" + "github.com/kazhuravlev/database-gateway/internal/uuid6" + "github.com/stretchr/testify/require" +) + +func TestAdaptEmptyListsReturnsNonNilSlices(t *testing.T) { + t.Parallel() + + testCases := []struct { + name string + check func(t *testing.T) + }{ + { + name: "query results", + check: func(t *testing.T) { + t.Helper() + + var items []model.QueryResults + + got := adaptQueryResults(items) + require.NotNil(t, got) + require.Empty(t, got) + }, + }, + { + name: "bookmarks", + check: func(t *testing.T) { + t.Helper() + + var items []model.Bookmarks + + got := adaptBookmarks(items) + require.NotNil(t, got) + require.Empty(t, got) + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + tc.check(t) + }) + } +} + +func TestAdaptQueryResultsMapsNonEmptyRows(t *testing.T) { + t.Parallel() + + id := uuid6.FromStr("01J2Z7K2YJ9G8F7E6D5C4B3A2H") + createdAt := time.Date(2026, time.July, 2, 10, 11, 12, 13, time.UTC) + response := []byte(`{"columns":["id"],"rows":[["1"]]}`) + + got := adaptQueryResults([]model.QueryResults{ + { + ID: id, + UserID: config.UserID("user-1"), + TargetID: config.TargetID("target-1"), + CreatedAt: createdAt, + Query: "select 1", + State: structs.QueryStateCompleted, + Response: response, + }, + }) + + require.Equal(t, []QueryResult{ + { + ID: id, + UserID: config.UserID("user-1"), + TargetID: config.TargetID("target-1"), + CreatedAt: createdAt, + Query: "select 1", + State: structs.QueryStateCompleted, + Response: response, + }, + }, got) +} + +func TestAdaptBookmarksMapsNonEmptyRows(t *testing.T) { + t.Parallel() + + id := uuid.MustParse("018aacd7-8b53-75f2-a46c-8a87f37a7b2f") + createdAt := time.Date(2026, time.July, 2, 13, 14, 15, 16, time.UTC) + + got := adaptBookmarks([]model.Bookmarks{ + { + ID: id, + UserID: "user-1", + TargetID: "target-1", + Title: "Important query", + Query: "select * from users", + CreatedAt: createdAt, + }, + }) + + require.Equal(t, []Bookmark{ + { + ID: uuid6.FromUUID(id), + UserID: config.UserID("user-1"), + TargetID: config.TargetID("target-1"), + Title: "Important query", + Query: "select * from users", + CreatedAt: createdAt, + }, + }, got) +} diff --git a/internal/storage/api.go b/internal/storage/api.go index 3fb33c6..c8f6c66 100644 --- a/internal/storage/api.go +++ b/internal/storage/api.go @@ -122,16 +122,7 @@ func (*Service) ListQueryResultsByUser(conn qrm.DB, uid config.UserID, limit int return nil, err } - if items == nil { - return []QueryResult{}, nil - } - - out := make([]QueryResult, 0, len(items)) - for i := range items { - out = append(out, adaptQueryResult(&items[i])) - } - - return out, nil + return adaptQueryResults(items), nil } func (*Service) ListQueryResults(conn qrm.DB, limit, offset int64) ([]QueryResult, error) { @@ -147,16 +138,7 @@ func (*Service) ListQueryResults(conn qrm.DB, limit, offset int64) ([]QueryResul return nil, err } - if items == nil { - return []QueryResult{}, nil - } - - out := make([]QueryResult, 0, len(items)) - for i := range items { - out = append(out, adaptQueryResult(&items[i])) - } - - return out, nil + return adaptQueryResults(items), nil } type InsertBookmarkReq struct { @@ -220,23 +202,7 @@ func (*Service) ListBookmarks(conn qrm.DB, uid config.UserID, targetID config.Ta return nil, err } - if items == nil { - return []Bookmark{}, nil - } - - out := make([]Bookmark, 0, len(items)) - for _, item := range items { - out = append(out, Bookmark{ - ID: uuid6.FromUUID(item.ID), - UserID: config.UserID(item.UserID), - TargetID: config.TargetID(item.TargetID), - Title: item.Title, - Query: item.Query, - CreatedAt: item.CreatedAt, - }) - } - - return out, nil + return adaptBookmarks(items), nil } func (*Service) ListBookmarksByUser(conn qrm.DB, uid config.UserID) ([]Bookmark, error) { @@ -251,21 +217,5 @@ func (*Service) ListBookmarksByUser(conn qrm.DB, uid config.UserID) ([]Bookmark, return nil, err } - if items == nil { - return []Bookmark{}, nil - } - - out := make([]Bookmark, 0, len(items)) - for _, item := range items { - out = append(out, Bookmark{ - ID: uuid6.FromUUID(item.ID), - UserID: config.UserID(item.UserID), - TargetID: config.TargetID(item.TargetID), - Title: item.Title, - Query: item.Query, - CreatedAt: item.CreatedAt, - }) - } - - return out, nil + return adaptBookmarks(items), nil } diff --git a/internal/storage/migrations/migrations.go b/internal/storage/migrations/migrations.go index dbb66dd..88d1702 100644 --- a/internal/storage/migrations/migrations.go +++ b/internal/storage/migrations/migrations.go @@ -18,6 +18,7 @@ package migrations import ( "embed" + "fmt" "path/filepath" ) @@ -26,13 +27,17 @@ var Migrations embed.FS // AbsMigrationsDir должен указывать на директорию, где хранятся миграции. var AbsMigrationsDir = func() string { //nolint:gochecknoglobals - abs, err := filepath.Abs(absMigrationsDir) + return resolveAbsMigrationsDir(absMigrationsDir, filepath.Abs) +}() + +func resolveAbsMigrationsDir(dir string, absPath func(string) (string, error)) string { + abs, err := absPath(dir) if err != nil { - panic("the sky is falling") + panic(fmt.Errorf("resolve absolute migrations dir %q: %w", dir, err)) } return abs -}() +} const ( absMigrationsDir = "./internal/storage/migrations" diff --git a/internal/storage/migrations/migrations_test.go b/internal/storage/migrations/migrations_test.go new file mode 100644 index 0000000..a0e5357 --- /dev/null +++ b/internal/storage/migrations/migrations_test.go @@ -0,0 +1,63 @@ +// Database Gateway provides access to servers with ACL for safe and restricted database interactions. +// Copyright (C) 2024 Kirill Zhuravlev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package migrations //nolint:testpackage + +import ( + "errors" + "fmt" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +var errCWDUnavailable = errors.New("cwd unavailable") + +func TestResolveAbsMigrationsDirPanicIncludesOperationAndPath(t *testing.T) { + t.Parallel() + + const dir = "./missing/migrations" + cause := errCWDUnavailable + + panicValue := requirePanic(t, func() { + resolveAbsMigrationsDir(dir, func(string) (string, error) { + return "", cause + }) + }) + + message := fmt.Sprint(panicValue) + assert.Contains(t, message, "resolve absolute migrations dir") + assert.Contains(t, message, dir) + assert.Contains(t, message, cause.Error()) +} + +func requirePanic(t *testing.T, fn func()) any { + t.Helper() + + var panicValue any + func() { + defer func() { + panicValue = recover() + }() + + fn() + }() + + require.NotNil(t, panicValue) + + return panicValue +} diff --git a/internal/storage/private_test.go b/internal/storage/private_test.go new file mode 100644 index 0000000..c1b536b --- /dev/null +++ b/internal/storage/private_test.go @@ -0,0 +1,87 @@ +// Database Gateway provides access to servers with ACL for safe and restricted database interactions. +// Copyright (C) 2024 Kirill Zhuravlev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package storage //nolint:exhaustruct,testpackage + +import ( + "database/sql" + "errors" + "testing" + + "github.com/go-jet/jet/v2/qrm" + "github.com/lib/pq" + "github.com/stretchr/testify/require" +) + +var errRowsAffectedFailed = errors.New("rows affected failed") + +type resultStub struct { + rowsAffected int64 + rowsAffectedErr error +} + +func (resultStub) LastInsertId() (int64, error) { + return 0, nil +} + +func (r resultStub) RowsAffected() (int64, error) { + return r.rowsAffected, r.rowsAffectedErr +} + +func TestHandleError(t *testing.T) { + t.Parallel() + + rowsAffectedErr := errRowsAffectedFailed + + testCases := []struct { + name string + err error + res sql.Result + wantErr error + }{ + { + name: "no rows", + err: qrm.ErrNoRows, + wantErr: ErrNotFound, + }, + { + name: "duplicate key", + err: &pq.Error{Code: "23505"}, + wantErr: ErrIntegrityViolation, + }, + { + name: "zero rows affected", + res: resultStub{rowsAffected: 0}, + wantErr: ErrNotFound, + }, + { + name: "rows affected error", + res: resultStub{ + rowsAffectedErr: rowsAffectedErr, + }, + wantErr: rowsAffectedErr, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + err := handleError("test operation", tc.err, tc.res) + require.ErrorIs(t, err, tc.wantErr) + }) + } +} diff --git a/internal/uuid6/uuid_v6_test.go b/internal/uuid6/uuid_v6_test.go new file mode 100644 index 0000000..790f922 --- /dev/null +++ b/internal/uuid6/uuid_v6_test.go @@ -0,0 +1,143 @@ +// Database Gateway provides access to servers with ACL for safe and restricted database interactions. +// Copyright (C) 2024 Kirill Zhuravlev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +package uuid6_test + +import ( + "testing" + + "github.com/google/uuid" + "github.com/kazhuravlev/database-gateway/internal/uuid6" + "github.com/stretchr/testify/require" +) + +func TestNil(t *testing.T) { + t.Parallel() + + id := uuid6.Nil() + + require.True(t, id.IsNil()) + require.Equal(t, "00000000000000000000000000", id.String()) + require.Equal(t, uuid.Nil, id.ToUUID()) +} + +func TestFromUUID(t *testing.T) { + t.Parallel() + + original := uuid.MustParse("018f1f60-7a0a-6c20-b9f2-147c3cb8778a") + + id := uuid6.FromUUID(original) + + require.False(t, id.IsNil()) + require.Equal(t, original, id.ToUUID()) +} + +func TestParseStr(t *testing.T) { + t.Parallel() + + id, err := uuid6.ParseStr("01HZ7N9R8Y9V1B9KZ6ZQVV8A1S") + require.NoError(t, err) + require.Equal(t, "01HZ7N9R8Y9V1B9KZ6ZQVV8A1S", id.String()) +} + +func TestValue(t *testing.T) { + t.Parallel() + + original := uuid.MustParse("018f1f60-7a0a-6c20-b9f2-147c3cb8778a") + id := uuid6.FromUUID(original) + + value, err := id.Value() + require.NoError(t, err) + require.Equal(t, original.String(), value) +} + +func TestScan(t *testing.T) { + t.Parallel() + + original := uuid.MustParse("018f1f60-7a0a-6c20-b9f2-147c3cb8778a") + + tests := []struct { + name string + input any + want uuid6.UUID + }{ + { + name: "string", + input: original.String(), + want: uuid6.FromUUID(original), + }, + { + name: "bytes", + input: []byte(original.String()), + want: uuid6.FromUUID(original), + }, + { + name: "nil uuid bytes", + input: uuid.Nil[:], + want: uuid6.Nil(), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + var got uuid6.UUID + err := got.Scan(tc.input) + require.NoError(t, err) + require.Equal(t, tc.want, got) + }) + } +} + +func TestInvalidInputCases(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + input any + }{ + { + name: "nil scan input", + input: nil, + }, + { + name: "unsupported scan input", + input: 42, + }, + { + name: "invalid scan string", + input: "not-a-uuid", + }, + { + name: "invalid scan bytes", + input: []byte("not-a-uuid"), + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + var got uuid6.UUID + err := got.Scan(tc.input) + require.Error(t, err) + }) + } + + _, err := uuid6.ParseStr("not-a-ulid") + require.Error(t, err) +} diff --git a/internal/validator/validator_test.go b/internal/validator/validator_test.go index 552e80b..d101789 100644 --- a/internal/validator/validator_test.go +++ b/internal/validator/validator_test.go @@ -90,6 +90,33 @@ func TestValidateSchema(t *testing.T) { }) } +func TestValidateSchemaUsesCanonicalDefaultSchemaTable(t *testing.T) { + t.Parallel() + + schema := validator.NewDbSchema("public", []config.TargetTable{ + {Table: "public.clients", Fields: []string{"id"}}, + }) + + err := validator.ValidateSchema([]validator.Vec{ + { + Op: config.OpSelect, + Tbl: "clients", + Cols: []string{"id"}, + }, + }, schema) + require.NoError(t, err) + + err = validator.ValidateSchema([]validator.Vec{ + { + Op: config.OpSelect, + Tbl: "clients", + Cols: []string{"unknown"}, + }, + }, schema) + require.ErrorIs(t, err, validator.ErrAccessDenied) + require.ErrorIs(t, err, validator.ErrUnknownColumn) +} + func TestCanonicalTable(t *testing.T) { t.Parallel() @@ -103,6 +130,41 @@ func TestCanonicalTable(t *testing.T) { require.Equal(t, "missing", schema.CanonicalTable("missing")) } +func TestValidatorRejectsUnknownSchemas(t *testing.T) { + t.Parallel() + + schema := validator.NewDbSchema("public", []config.TargetTable{ + {Table: "public.clients", Fields: []string{"id", "name"}}, + }) + testCases := []struct { + name string + query string + }{ + {name: "select", query: `select id from private.clients`}, + {name: "insert", query: `insert into private.clients(id) values (42)`}, + {name: "update", query: `update private.clients set name='john'`}, + {name: "delete", query: `delete from private.clients where id=42`}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + + accessChecked := false + haveAccess := func(_ validator.Vec) bool { + accessChecked = true + + return true + } + + err := validator.IsAllowed(schema, haveAccess, tc.query) + require.ErrorIs(t, err, validator.ErrAccessDenied) + require.ErrorIs(t, err, validator.ErrUnknownTable) + require.False(t, accessChecked) + }) + } +} + func testTargetTables() []config.TargetTable { return []config.TargetTable{ {