From b1a7ef89ca2f3650e1aa3e28fd253daac462454b Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 17 Aug 2023 18:49:32 +0200 Subject: [PATCH 1/7] feat(backend): add snowflake IDs --- backend/common/generator.go | 65 ++++++++++++++ backend/common/snowflake.go | 83 ++++++++++++++++++ backend/common/snowflake_types.go | 39 +++++++++ backend/db/flags.go | 23 ++--- backend/db/member.go | 6 +- backend/db/user.go | 3 +- backend/routes/v1/auth/routes.go | 3 + backend/routes/v1/member/get_member.go | 19 ++-- backend/routes/v1/member/get_members.go | 3 + backend/routes/v1/user/get_user.go | 5 ++ main.go | 2 + scripts/migrate/021_snowflakes.sql | 13 +++ scripts/snowflakes/main.go | 111 ++++++++++++++++++++++++ 13 files changed, 355 insertions(+), 20 deletions(-) create mode 100644 backend/common/generator.go create mode 100644 backend/common/snowflake.go create mode 100644 backend/common/snowflake_types.go create mode 100644 scripts/migrate/021_snowflakes.sql create mode 100644 scripts/snowflakes/main.go diff --git a/backend/common/generator.go b/backend/common/generator.go new file mode 100644 index 0000000..33385e1 --- /dev/null +++ b/backend/common/generator.go @@ -0,0 +1,65 @@ +package common + +import ( + "math/rand" + "sync/atomic" + "time" +) + +// Generator is a snowflake generator. +// For compatibility with other snowflake implementations, both worker and PID are set, +// but they are randomized for every generator. +type IDGenerator struct { + inc *uint64 + worker, pid uint64 +} + +var defaultGenerator = NewIDGenerator(0, 0) + +// NewIDGenerator creates a new ID generator with the given worker and pid. +// If worker or pid is empty, it will be set to a random number. +func NewIDGenerator(worker, pid uint64) *IDGenerator { + if worker == 0 { + worker = rand.Uint64() + } + if pid == 0 { + pid = rand.Uint64() + } + + g := &IDGenerator{ + inc: new(uint64), + worker: worker % 32, + pid: pid % 32, + } + + return g +} + +// GenerateID generates a new snowflake with the default generator. +// If you need to customize the worker and PID, manually call (*Generator).Generate. +func GenerateID() Snowflake { + return defaultGenerator.Generate() +} + +// GenerateID generates a new snowflake with the given time with the default generator. +// If you need to customize the worker and PID, manually call (*Generator).GenerateWithTime. +func GenerateIDWithTime(t time.Time) Snowflake { + return defaultGenerator.GenerateWithTime(t) +} + +// Generate generates a snowflake with the current time. +func (g *IDGenerator) Generate() Snowflake { + return g.GenerateWithTime(time.Now()) +} + +// GenerateWithTime generates a snowflake with the given time. +// To generate a snowflake for comparison, use the top-level New function instead. +func (g *IDGenerator) GenerateWithTime(t time.Time) Snowflake { + increment := atomic.AddUint64(g.inc, 1) + ts := uint64(t.UnixMilli() - Epoch) + + worker := g.worker << 17 + pid := g.pid << 12 + + return Snowflake(ts<<22 | worker | pid | (increment % 4096)) +} diff --git a/backend/common/snowflake.go b/backend/common/snowflake.go new file mode 100644 index 0000000..694ec24 --- /dev/null +++ b/backend/common/snowflake.go @@ -0,0 +1,83 @@ +package common + +import ( + "strconv" + "strings" + "time" +) + +// Epoch is the pronouns.cc epoch (January 1st 2022 at 00:00:00 UTC) in milliseconds. +const Epoch = 1_640_995_200_000 +const epochDuration = Epoch * time.Millisecond + +const NullSnowflake = ^Snowflake(0) + +// Snowflake is a 64-bit integer used as a unique ID, with an embedded timestamp. +type Snowflake uint64 + +// ID is an alias to Snowflake. +type ID = Snowflake + +// ParseSnowflake parses a snowflake from a string. +func ParseSnowflake(sf string) (Snowflake, error) { + if sf == "null" { + return NullSnowflake, nil + } + + i, err := strconv.ParseUint(sf, 10, 64) + if err != nil { + return 0, err + } + + return Snowflake(i), nil +} + +// NewSnowflake creates a new snowflake from the given time. +func NewSnowflake(t time.Time) Snowflake { + ts := time.Duration(t.UnixNano()) - epochDuration + + return Snowflake((ts / time.Millisecond) << 22) +} + +// String returns the snowflake as a string. +func (s Snowflake) String() string { return strconv.FormatUint(uint64(s), 10) } + +// Time returns the creation time of the snowflake. +func (s Snowflake) Time() time.Time { + ts := time.Duration(s>>22)*time.Millisecond + epochDuration + return time.Unix(0, int64(ts)) +} + +func (s Snowflake) IsValid() bool { + return s != 0 && s != NullSnowflake +} + +func (s Snowflake) MarshalJSON() ([]byte, error) { + if !s.IsValid() { + return []byte("null"), nil + } + + return []byte(`"` + strconv.FormatUint(uint64(s), 10) + `"`), nil +} + +func (s *Snowflake) UnmarshalJSON(src []byte) error { + sf, err := ParseSnowflake(strings.Trim(string(src), `"`)) + if err != nil { + return err + } + + *s = sf + return nil +} + +func (s Snowflake) Worker() uint8 { + return uint8(s & 0x3E0000 >> 17) +} + +func (s Snowflake) PID() uint8 { + return uint8(s & 0x1F000 >> 12) +} + +func (s Snowflake) Increment() uint16 { + return uint16(s & 0xFFF) +} diff --git a/backend/common/snowflake_types.go b/backend/common/snowflake_types.go new file mode 100644 index 0000000..3e9848f --- /dev/null +++ b/backend/common/snowflake_types.go @@ -0,0 +1,39 @@ +package common + +import "time" + +type UserID Snowflake + +func (id UserID) String() string { return Snowflake(id).String() } +func (id UserID) Time() time.Time { return Snowflake(id).Time() } +func (id UserID) IsValid() bool { return Snowflake(id).IsValid() } +func (id UserID) Worker() uint8 { return Snowflake(id).Worker() } +func (id UserID) PID() uint8 { return Snowflake(id).PID() } +func (id UserID) Increment() uint16 { return Snowflake(id).Increment() } + +func (id UserID) MarshalJSON() ([]byte, error) { return Snowflake(id).MarshalJSON() } +func (id *UserID) UnmarshalJSON(src []byte) error { return (*Snowflake)(id).UnmarshalJSON(src) } + +type MemberID Snowflake + +func (id MemberID) String() string { return Snowflake(id).String() } +func (id MemberID) Time() time.Time { return Snowflake(id).Time() } +func (id MemberID) IsValid() bool { return Snowflake(id).IsValid() } +func (id MemberID) Worker() uint8 { return Snowflake(id).Worker() } +func (id MemberID) PID() uint8 { return Snowflake(id).PID() } +func (id MemberID) Increment() uint16 { return Snowflake(id).Increment() } + +func (id MemberID) MarshalJSON() ([]byte, error) { return Snowflake(id).MarshalJSON() } +func (id *MemberID) UnmarshalJSON(src []byte) error { return (*Snowflake)(id).UnmarshalJSON(src) } + +type FlagID Snowflake + +func (id FlagID) String() string { return Snowflake(id).String() } +func (id FlagID) Time() time.Time { return Snowflake(id).Time() } +func (id FlagID) IsValid() bool { return Snowflake(id).IsValid() } +func (id FlagID) Worker() uint8 { return Snowflake(id).Worker() } +func (id FlagID) PID() uint8 { return Snowflake(id).PID() } +func (id FlagID) Increment() uint16 { return Snowflake(id).Increment() } + +func (id FlagID) MarshalJSON() ([]byte, error) { return Snowflake(id).MarshalJSON() } +func (id *FlagID) UnmarshalJSON(src []byte) error { return (*Snowflake)(id).UnmarshalJSON(src) } diff --git a/backend/db/flags.go b/backend/db/flags.go index 151cb92..8187c46 100644 --- a/backend/db/flags.go +++ b/backend/db/flags.go @@ -9,6 +9,7 @@ import ( "io" "strings" + "codeberg.org/pronounscc/pronouns.cc/backend/common" "codeberg.org/pronounscc/pronouns.cc/backend/log" "emperror.dev/errors" "github.com/davidbyttow/govips/v2/vips" @@ -20,11 +21,12 @@ import ( ) type PrideFlag struct { - ID xid.ID `json:"id"` - UserID xid.ID `json:"-"` - Hash string `json:"hash"` - Name string `json:"name"` - Description *string `json:"description"` + ID xid.ID `json:"id"` + SnowflakeID common.FlagID `json:"id_new"` + UserID xid.ID `json:"-"` + Hash string `json:"hash"` + Name string `json:"name"` + Description *string `json:"description"` } type UserFlag struct { @@ -194,11 +196,12 @@ func (db *DB) CreateFlag(ctx context.Context, tx pgx.Tx, userID xid.ID, name, de sql, args, err := sq.Insert("pride_flags"). SetMap(map[string]any{ - "id": xid.New(), - "hash": "", - "user_id": userID.String(), - "name": name, - "description": description, + "id": xid.New(), + "snowflake_id": common.GenerateID(), + "hash": "", + "user_id": userID.String(), + "name": name, + "description": description, }).Suffix("RETURNING *").ToSql() if err != nil { return f, errors.Wrap(err, "building query") diff --git a/backend/db/member.go b/backend/db/member.go index 71b3bd0..5de82aa 100644 --- a/backend/db/member.go +++ b/backend/db/member.go @@ -6,6 +6,7 @@ import ( "strings" "time" + "codeberg.org/pronounscc/pronouns.cc/backend/common" "emperror.dev/errors" "github.com/Masterminds/squirrel" "github.com/georgysavva/scany/v2/pgxscan" @@ -22,6 +23,7 @@ const ( type Member struct { ID xid.ID UserID xid.ID + SnowflakeID common.MemberID SID string `db:"sid"` Name string DisplayName *string @@ -135,8 +137,8 @@ func (db *DB) CreateMember( name string, displayName *string, bio string, links []string, ) (m Member, err error) { sql, args, err := sq.Insert("members"). - Columns("user_id", "id", "sid", "name", "display_name", "bio", "links"). - Values(userID, xid.New(), squirrel.Expr("find_free_member_sid()"), name, displayName, bio, links). + Columns("user_id", "snowflake_id", "id", "sid", "name", "display_name", "bio", "links"). + Values(userID, common.GenerateID(), xid.New(), squirrel.Expr("find_free_member_sid()"), name, displayName, bio, links). Suffix("RETURNING *").ToSql() if err != nil { return m, errors.Wrap(err, "building sql") diff --git a/backend/db/user.go b/backend/db/user.go index 95a3355..8b9a0ca 100644 --- a/backend/db/user.go +++ b/backend/db/user.go @@ -22,6 +22,7 @@ import ( type User struct { ID xid.ID + SnowflakeID common.UserID SID string `db:"sid"` Username string DisplayName *string @@ -206,7 +207,7 @@ func (db *DB) CreateUser(ctx context.Context, tx pgx.Tx, username string) (u Use return u, err } - sql, args, err := sq.Insert("users").Columns("id", "username", "sid").Values(xid.New(), username, squirrel.Expr("find_free_user_sid()")).Suffix("RETURNING *").ToSql() + sql, args, err := sq.Insert("users").Columns("id", "snowflake_id", "username", "sid").Values(xid.New(), common.GenerateID(), username, squirrel.Expr("find_free_user_sid()")).Suffix("RETURNING *").ToSql() if err != nil { return u, errors.Wrap(err, "building sql") } diff --git a/backend/routes/v1/auth/routes.go b/backend/routes/v1/auth/routes.go index 7699084..103d43e 100644 --- a/backend/routes/v1/auth/routes.go +++ b/backend/routes/v1/auth/routes.go @@ -4,6 +4,7 @@ import ( "net/http" "os" + "codeberg.org/pronounscc/pronouns.cc/backend/common" "codeberg.org/pronounscc/pronouns.cc/backend/db" "codeberg.org/pronounscc/pronouns.cc/backend/log" "codeberg.org/pronounscc/pronouns.cc/backend/server" @@ -25,6 +26,7 @@ type Server struct { type userResponse struct { ID xid.ID `json:"id"` + SnowflakeID common.UserID `json:"id_new"` Username string `json:"name"` DisplayName *string `json:"display_name"` Bio *string `json:"bio"` @@ -51,6 +53,7 @@ type userResponse struct { func dbUserToUserResponse(u db.User, fields []db.Field) *userResponse { return &userResponse{ ID: u.ID, + SnowflakeID: u.SnowflakeID, Username: u.Username, DisplayName: u.DisplayName, Bio: u.Bio, diff --git a/backend/routes/v1/member/get_member.go b/backend/routes/v1/member/get_member.go index ddc94d8..f87bbe9 100644 --- a/backend/routes/v1/member/get_member.go +++ b/backend/routes/v1/member/get_member.go @@ -4,6 +4,7 @@ import ( "context" "net/http" + "codeberg.org/pronounscc/pronouns.cc/backend/common" "codeberg.org/pronounscc/pronouns.cc/backend/db" "codeberg.org/pronounscc/pronouns.cc/backend/server" "emperror.dev/errors" @@ -13,13 +14,14 @@ import ( ) type GetMemberResponse struct { - ID xid.ID `json:"id"` - SID string `json:"sid"` - Name string `json:"name"` - DisplayName *string `json:"display_name"` - Bio *string `json:"bio"` - Avatar *string `json:"avatar"` - Links []string `json:"links"` + ID xid.ID `json:"id"` + SnowflakeID common.MemberID `json:"id_new"` + SID string `json:"sid"` + Name string `json:"name"` + DisplayName *string `json:"display_name"` + Bio *string `json:"bio"` + Avatar *string `json:"avatar"` + Links []string `json:"links"` Names []db.FieldEntry `json:"names"` Pronouns []db.PronounEntry `json:"pronouns"` @@ -34,6 +36,7 @@ type GetMemberResponse struct { func dbMemberToMember(u db.User, m db.Member, fields []db.Field, flags []db.MemberFlag, isOwnMember bool) GetMemberResponse { r := GetMemberResponse{ ID: m.ID, + SnowflakeID: m.SnowflakeID, SID: m.SID, Name: m.Name, DisplayName: m.DisplayName, @@ -48,6 +51,7 @@ func dbMemberToMember(u db.User, m db.Member, fields []db.Field, flags []db.Memb User: PartialUser{ ID: u.ID, + SnowflakeID: u.SnowflakeID, Username: u.Username, DisplayName: u.DisplayName, Avatar: u.Avatar, @@ -64,6 +68,7 @@ func dbMemberToMember(u db.User, m db.Member, fields []db.Field, flags []db.Memb type PartialUser struct { ID xid.ID `json:"id"` + SnowflakeID common.UserID `json:"id_new"` Username string `json:"name"` DisplayName *string `json:"display_name"` Avatar *string `json:"avatar"` diff --git a/backend/routes/v1/member/get_members.go b/backend/routes/v1/member/get_members.go index 6b08239..6dba566 100644 --- a/backend/routes/v1/member/get_members.go +++ b/backend/routes/v1/member/get_members.go @@ -3,6 +3,7 @@ package member import ( "net/http" + "codeberg.org/pronounscc/pronouns.cc/backend/common" "codeberg.org/pronounscc/pronouns.cc/backend/db" "codeberg.org/pronounscc/pronouns.cc/backend/server" "github.com/go-chi/chi/v5" @@ -12,6 +13,7 @@ import ( type memberListResponse struct { ID xid.ID `json:"id"` + SnowflakeID common.MemberID `json:"id_new"` SID string `json:"sid"` Name string `json:"name"` DisplayName *string `json:"display_name"` @@ -28,6 +30,7 @@ func membersToMemberList(ms []db.Member, isSelf bool) []memberListResponse { for i := range ms { resps[i] = memberListResponse{ ID: ms[i].ID, + SnowflakeID: ms[i].SnowflakeID, SID: ms[i].SID, Name: ms[i].Name, DisplayName: ms[i].DisplayName, diff --git a/backend/routes/v1/user/get_user.go b/backend/routes/v1/user/get_user.go index d1163a3..4826ed0 100644 --- a/backend/routes/v1/user/get_user.go +++ b/backend/routes/v1/user/get_user.go @@ -4,6 +4,7 @@ import ( "net/http" "time" + "codeberg.org/pronounscc/pronouns.cc/backend/common" "codeberg.org/pronounscc/pronouns.cc/backend/db" "codeberg.org/pronounscc/pronouns.cc/backend/log" "codeberg.org/pronounscc/pronouns.cc/backend/server" @@ -14,6 +15,7 @@ import ( type GetUserResponse struct { ID xid.ID `json:"id"` + SnowflakeID common.UserID `json:"id_new"` SID string `json:"sid"` Username string `json:"name"` DisplayName *string `json:"display_name"` @@ -58,6 +60,7 @@ type GetMeResponse struct { type PartialMember struct { ID xid.ID `json:"id"` + SnowflakeID common.MemberID `json:"id_new"` SID string `json:"sid"` Name string `json:"name"` DisplayName *string `json:"display_name"` @@ -71,6 +74,7 @@ type PartialMember struct { func dbUserToResponse(u db.User, fields []db.Field, members []db.Member, flags []db.UserFlag) GetUserResponse { resp := GetUserResponse{ ID: u.ID, + SnowflakeID: u.SnowflakeID, SID: u.SID, Username: u.Username, DisplayName: u.DisplayName, @@ -97,6 +101,7 @@ func dbUserToResponse(u db.User, fields []db.Field, members []db.Member, flags [ for i := range members { resp.Members[i] = PartialMember{ ID: members[i].ID, + SnowflakeID: members[i].SnowflakeID, SID: members[i].SID, Name: members[i].Name, DisplayName: members[i].DisplayName, diff --git a/main.go b/main.go index 61cac6b..067aafd 100644 --- a/main.go +++ b/main.go @@ -13,6 +13,7 @@ import ( "codeberg.org/pronounscc/pronouns.cc/scripts/genkey" "codeberg.org/pronounscc/pronouns.cc/scripts/migrate" "codeberg.org/pronounscc/pronouns.cc/scripts/seeddb" + "codeberg.org/pronounscc/pronouns.cc/scripts/snowflakes" "github.com/urfave/cli/v2" ) @@ -32,6 +33,7 @@ var app = &cli.App{ migrate.Command, seeddb.Command, cleandb.Command, + snowflakes.Command, }, }, { diff --git a/scripts/migrate/021_snowflakes.sql b/scripts/migrate/021_snowflakes.sql new file mode 100644 index 0000000..76777c0 --- /dev/null +++ b/scripts/migrate/021_snowflakes.sql @@ -0,0 +1,13 @@ +-- 2023-08-17: Add snowflake ID columns + +-- +migrate Up + +alter table users add column snowflake_id bigint unique; +alter table members add column snowflake_id bigint unique; +alter table pride_flags add column snowflake_id bigint unique; + +-- +migrate Down + +alter table users drop column snowflake_id; +alter table members drop column snowflake_id; +alter table pride_flags drop column snowflake_id; diff --git a/scripts/snowflakes/main.go b/scripts/snowflakes/main.go new file mode 100644 index 0000000..382035a --- /dev/null +++ b/scripts/snowflakes/main.go @@ -0,0 +1,111 @@ +package snowflakes + +import ( + "os" + "time" + + "codeberg.org/pronounscc/pronouns.cc/backend/common" + "codeberg.org/pronounscc/pronouns.cc/backend/log" + "github.com/georgysavva/scany/v2/pgxscan" + "github.com/jackc/pgx/v5" + "github.com/joho/godotenv" + "github.com/rs/xid" + "github.com/urfave/cli/v2" +) + +var Command = &cli.Command{ + Name: "create-snowflakes", + Usage: "Give all users, members, and flags snowflake IDs.", + Action: run, +} + +func run(c *cli.Context) error { + err := godotenv.Load() + if err != nil { + log.Error("loading .env file:", err) + return err + } + + conn, err := pgx.Connect(c.Context, os.Getenv("DATABASE_URL")) + if err != nil { + log.Error("opening database:", err) + return err + } + defer conn.Close(c.Context) + log.Info("opened database") + + tx, err := conn.Begin(c.Context) + if err != nil { + log.Error("creating transaction:", err) + return err + } + defer tx.Rollback(c.Context) + + var userIDs []xid.ID + err = pgxscan.Select(c.Context, conn, &userIDs, "SELECT id FROM users WHERE snowflake_id IS NULL") + if err != nil { + log.Error("selecting users without snowflake:", err) + return err + } + + t := time.Now() + for _, userID := range userIDs { + t := userID.Time() + snowflake := common.UserID(common.GenerateIDWithTime(t)) + + _, err = tx.Exec(c.Context, "UPDATE users SET snowflake_id = $1 WHERE id = $2", snowflake, userID) + if err != nil { + log.Errorf("updating user with ID %v: %v", userID, err) + return err + } + } + log.Infof("updated %v users in %v", len(userIDs), time.Since(t)) + + var memberIDs []xid.ID + err = pgxscan.Select(c.Context, conn, &memberIDs, "SELECT id FROM members WHERE snowflake_id IS NULL") + if err != nil { + log.Error("selecting users without snowflake:", err) + return err + } + + t = time.Now() + for _, memberID := range memberIDs { + t := memberID.Time() + snowflake := common.MemberID(common.GenerateIDWithTime(t)) + + _, err = tx.Exec(c.Context, "UPDATE members SET snowflake_id = $1 WHERE id = $2", snowflake, memberID) + if err != nil { + log.Errorf("updating user with ID %v: %v", memberID, err) + return err + } + } + log.Infof("updated %v members in %v", len(memberIDs), time.Since(t)) + + var flagIDs []xid.ID + err = pgxscan.Select(c.Context, conn, &flagIDs, "SELECT id FROM pride_flags WHERE snowflake_id IS NULL") + if err != nil { + log.Error("selecting users without snowflake:", err) + return err + } + + t = time.Now() + for _, flagID := range flagIDs { + t := flagID.Time() + snowflake := common.FlagID(common.GenerateIDWithTime(t)) + + _, err = tx.Exec(c.Context, "UPDATE pride_flags SET snowflake_id = $1 WHERE id = $2", snowflake, flagID) + if err != nil { + log.Errorf("updating user with ID %v: %v", flagID, err) + return err + } + } + log.Infof("updated %v flags in %v", len(flagIDs), time.Since(t)) + + err = tx.Commit(c.Context) + if err != nil { + log.Error("committing transaction:", err) + return err + } + + return nil +} From 1cce0defca6d1a72dbfd81852c80c76ab6ac3591 Mon Sep 17 00:00:00 2001 From: sam Date: Sun, 20 Aug 2023 22:45:14 +0200 Subject: [PATCH 2/7] feat(backend): make snowflake IDs usable in /users/{id}, /users/{id}/members --- backend/db/member.go | 15 ++++++++++++++- backend/db/user.go | 20 ++++++++++++++++++++ backend/routes/v1/member/get_member.go | 12 +++++++++++- backend/routes/v1/user/get_user.go | 9 +++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/backend/db/member.go b/backend/db/member.go index 5de82aa..53b6a9f 100644 --- a/backend/db/member.go +++ b/backend/db/member.go @@ -73,9 +73,22 @@ func (db *DB) Member(ctx context.Context, id xid.ID) (m Member, err error) { return m, nil } +func (db *DB) MemberBySnowflake(ctx context.Context, id common.MemberID) (m Member, err error) { + sql, args, err := sq.Select("*").From("members").Where("snowflake_id = ?", id).ToSql() + if err != nil { + return m, errors.Wrap(err, "building sql") + } + + err = pgxscan.Get(ctx, db, &m, sql, args...) + if err != nil { + return m, errors.Wrap(err, "executing query") + } + return m, nil +} + // UserMember returns a member scoped by user. func (db *DB) UserMember(ctx context.Context, userID xid.ID, memberRef string) (m Member, err error) { - sql, args, err := sq.Select("*").From("members").Where("user_id = ?", userID).Where("(id = ? or name = ?)", memberRef, memberRef).ToSql() + sql, args, err := sq.Select("*").From("members").Where("user_id = ?", userID).Where("(id = ? or name = ? or snowflake_id = ?)", memberRef, memberRef, memberRef).ToSql() if err != nil { return m, errors.Wrap(err, "building sql") } diff --git a/backend/db/user.go b/backend/db/user.go index 8b9a0ca..80d380d 100644 --- a/backend/db/user.go +++ b/backend/db/user.go @@ -495,6 +495,26 @@ func (db *DB) User(ctx context.Context, id xid.ID) (u User, err error) { return u, nil } +// UserBySnowflake gets a user by their snowflake ID. +func (db *DB) UserBySnowflake(ctx context.Context, id common.UserID) (u User, err error) { + sql, args, err := sq.Select("*", "(SELECT instance FROM fediverse_apps WHERE id = users.fediverse_app_id) AS fediverse_instance"). + From("users").Where("snowflake_id = ?", id).ToSql() + if err != nil { + return u, errors.Wrap(err, "building sql") + } + + err = pgxscan.Get(ctx, db, &u, sql, args...) + if err != nil { + if errors.Cause(err) == pgx.ErrNoRows { + return u, ErrUserNotFound + } + + return u, errors.Wrap(err, "getting user from db") + } + + return u, nil +} + // Username gets a user by username. func (db *DB) Username(ctx context.Context, name string) (u User, err error) { sql, args, err := sq.Select("*").From("users").Where("username = ?", name).ToSql() diff --git a/backend/routes/v1/member/get_member.go b/backend/routes/v1/member/get_member.go index f87bbe9..60fa573 100644 --- a/backend/routes/v1/member/get_member.go +++ b/backend/routes/v1/member/get_member.go @@ -191,12 +191,22 @@ func (s *Server) getMeMember(w http.ResponseWriter, r *http.Request) error { } func (s *Server) parseUser(ctx context.Context, userRef string) (u db.User, err error) { - if id, err := xid.FromString(userRef); err != nil { + // check xid first + if id, err := xid.FromString(userRef); err == nil { u, err := s.DB.User(ctx, id) if err == nil { return u, nil } } + // if not an xid, check by snowflake + if id, err := common.ParseSnowflake(userRef); err == nil { + u, err := s.DB.UserBySnowflake(ctx, common.UserID(id)) + if err == nil { + return u, nil + } + } + + // else, use username return s.DB.Username(ctx, userRef) } diff --git a/backend/routes/v1/user/get_user.go b/backend/routes/v1/user/get_user.go index 4826ed0..af43a4b 100644 --- a/backend/routes/v1/user/get_user.go +++ b/backend/routes/v1/user/get_user.go @@ -129,6 +129,15 @@ func (s *Server) getUser(w http.ResponseWriter, r *http.Request) (err error) { } } + if u.ID.IsNil() { + if id, err := common.ParseSnowflake(userRef); err == nil { + u, err = s.DB.UserBySnowflake(ctx, common.UserID(id)) + if err != nil { + log.Errorf("getting user by snowflake: %v", err) + } + } + } + if u.ID.IsNil() { u, err = s.DB.Username(ctx, userRef) if err == db.ErrUserNotFound { From 0171f545922da86e453336ca91f74387bef12f9f Mon Sep 17 00:00:00 2001 From: sam Date: Sat, 2 Sep 2023 16:34:51 +0200 Subject: [PATCH 3/7] add snowflake support to some member routes --- backend/db/member.go | 3 ++- backend/routes/v1/member/get_member.go | 29 ++++++++++++++++++-------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/backend/db/member.go b/backend/db/member.go index 53b6a9f..f301c89 100644 --- a/backend/db/member.go +++ b/backend/db/member.go @@ -88,7 +88,8 @@ func (db *DB) MemberBySnowflake(ctx context.Context, id common.MemberID) (m Memb // UserMember returns a member scoped by user. func (db *DB) UserMember(ctx context.Context, userID xid.ID, memberRef string) (m Member, err error) { - sql, args, err := sq.Select("*").From("members").Where("user_id = ?", userID).Where("(id = ? or name = ? or snowflake_id = ?)", memberRef, memberRef, memberRef).ToSql() + sf, _ := common.ParseSnowflake(memberRef) // error can be ignored as the zero value will never be used as an ID + sql, args, err := sq.Select("*").From("members").Where("user_id = ?", userID).Where("(id = ? or snowflake_id = ? or name = ?)", memberRef, sf, memberRef).ToSql() if err != nil { return m, errors.Wrap(err, "building sql") } diff --git a/backend/routes/v1/member/get_member.go b/backend/routes/v1/member/get_member.go index 60fa573..2a7d5e6 100644 --- a/backend/routes/v1/member/get_member.go +++ b/backend/routes/v1/member/get_member.go @@ -6,6 +6,7 @@ import ( "codeberg.org/pronounscc/pronouns.cc/backend/common" "codeberg.org/pronounscc/pronouns.cc/backend/db" + "codeberg.org/pronounscc/pronouns.cc/backend/log" "codeberg.org/pronounscc/pronouns.cc/backend/server" "emperror.dev/errors" "github.com/go-chi/chi/v5" @@ -75,20 +76,30 @@ type PartialUser struct { CustomPreferences db.CustomPreferences `json:"custom_preferences"` } -func (s *Server) getMember(w http.ResponseWriter, r *http.Request) error { +func (s *Server) getMember(w http.ResponseWriter, r *http.Request) (err error) { ctx := r.Context() - id, err := xid.FromString(chi.URLParam(r, "memberRef")) - if err != nil { - return server.APIError{ - Code: server.ErrMemberNotFound, + var m db.Member + if id, err := xid.FromString(chi.URLParam(r, "memberRef")); err == nil { + m, err = s.DB.Member(ctx, id) + if err != nil { + log.Errorf("getting member by xid: %v", err) } } + // xid was not valid + if !m.SnowflakeID.IsValid() { + id, err := common.ParseSnowflake(chi.URLParam(r, "memberRef")) + if err != nil { + return server.APIError{ + Code: server.ErrMemberNotFound, + } + } - m, err := s.DB.Member(ctx, id) - if err != nil { - return server.APIError{ - Code: server.ErrMemberNotFound, + m, err = s.DB.MemberBySnowflake(ctx, common.MemberID(id)) + if err != nil { + return server.APIError{ + Code: server.ErrMemberNotFound, + } } } From 1b9a5deb78d79fab0bf3ff1a61220e0625399096 Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 7 Sep 2023 01:43:05 +0200 Subject: [PATCH 4/7] make more member routes accept snowflakes + make flag routes accept snowflakes --- .air.toml | 43 + .gitignore | 1 + backend/main.go | 3 +- backend/routes/v1/member/delete_member.go | 30 +- backend/routes/v1/member/patch_member.go | 50 +- backend/routes/v1/user/flags.go | 57 +- package.json | 12 + pnpm-lock.yaml | 2462 +++++++++++++++++++++ pnpm-workspace.yaml | 3 + 9 files changed, 2605 insertions(+), 56 deletions(-) create mode 100644 .air.toml create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml diff --git a/.air.toml b/.air.toml new file mode 100644 index 0000000..1687879 --- /dev/null +++ b/.air.toml @@ -0,0 +1,43 @@ +root = "." +tmp_dir = "tmp" + +[build] + args_bin = ["web"] + bin = "./tmp/main" + cmd = "go build -o ./tmp/main ." + delay = 1000 + exclude_dir = ["docs", "frontend", "prns", "pronounslib", "tmp", "target"] + exclude_file = [] + exclude_regex = ["_test.go"] + exclude_unchanged = false + follow_symlink = false + full_bin = "" + include_dir = [] + include_ext = ["go", "tpl", "tmpl", "html"] + include_file = [] + kill_delay = "0s" + log = "build-errors.log" + poll = false + poll_interval = 0 + rerun = false + rerun_delay = 500 + send_interrupt = false + stop_on_error = false + +[color] + app = "" + build = "yellow" + main = "magenta" + runner = "green" + watcher = "cyan" + +[log] + main_only = false + time = false + +[misc] + clean_on_exit = false + +[screen] + clear_on_rebuild = false + keep_scroll = true diff --git a/.gitignore b/.gitignore index 7cc7eb8..8e5f23c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ package vite.config.js.timestamp-* vite.config.ts.timestamp-* target +tmp diff --git a/backend/main.go b/backend/main.go index 7c9226a..008e80f 100644 --- a/backend/main.go +++ b/backend/main.go @@ -62,9 +62,8 @@ func run(c *cli.Context) error { return nil case err := <-e: log.Fatalf("Error running server: %v", err) + return err } - - return nil } const MaxContentLength = 2 * 1024 * 1024 diff --git a/backend/routes/v1/member/delete_member.go b/backend/routes/v1/member/delete_member.go index 5ce49c0..4c2abde 100644 --- a/backend/routes/v1/member/delete_member.go +++ b/backend/routes/v1/member/delete_member.go @@ -8,12 +8,13 @@ import ( "github.com/go-chi/render" "github.com/rs/xid" + "codeberg.org/pronounscc/pronouns.cc/backend/common" "codeberg.org/pronounscc/pronouns.cc/backend/db" "codeberg.org/pronounscc/pronouns.cc/backend/log" "codeberg.org/pronounscc/pronouns.cc/backend/server" ) -func (s *Server) deleteMember(w http.ResponseWriter, r *http.Request) error { +func (s *Server) deleteMember(w http.ResponseWriter, r *http.Request) (err error) { ctx := r.Context() claims, _ := server.ClaimsFromContext(ctx) @@ -22,18 +23,27 @@ func (s *Server) deleteMember(w http.ResponseWriter, r *http.Request) error { return server.APIError{Code: server.ErrMissingPermissions, Details: "this token is read-only"} } - id, err := xid.FromString(chi.URLParam(r, "memberRef")) - if err != nil { - return server.APIError{Code: server.ErrMemberNotFound} - } + var m db.Member + if id, err := xid.FromString(chi.URLParam(r, "memberRef")); err == nil { + m, err = s.DB.Member(ctx, id) + if err != nil { + if err == db.ErrMemberNotFound { + return server.APIError{Code: server.ErrMemberNotFound} + } - m, err := s.DB.Member(ctx, id) - if err != nil { - if err == db.ErrMemberNotFound { - return server.APIError{Code: server.ErrMemberNotFound} + return errors.Wrap(err, "getting member") } + } else if id, err := common.ParseSnowflake(chi.URLParam(r, "memberRef")); err == nil { + m, err = s.DB.MemberBySnowflake(ctx, common.MemberID(id)) + if err != nil { + if err == db.ErrMemberNotFound { + return server.APIError{Code: server.ErrMemberNotFound} + } - return errors.Wrap(err, "getting member") + return errors.Wrap(err, "getting member") + } + } else { + return server.APIError{Code: server.ErrMemberNotFound} } if m.UserID != claims.UserID { diff --git a/backend/routes/v1/member/patch_member.go b/backend/routes/v1/member/patch_member.go index 5884efe..5391026 100644 --- a/backend/routes/v1/member/patch_member.go +++ b/backend/routes/v1/member/patch_member.go @@ -38,23 +38,41 @@ func (s *Server) patchMember(w http.ResponseWriter, r *http.Request) error { return server.APIError{Code: server.ErrMissingPermissions, Details: "This token is read-only"} } - id, err := xid.FromString(chi.URLParam(r, "memberRef")) - if err != nil { - return server.APIError{Code: server.ErrMemberNotFound} - } - u, err := s.DB.User(ctx, claims.UserID) if err != nil { return errors.Wrap(err, "getting user") } - m, err := s.DB.Member(ctx, id) - if err != nil { - if err == db.ErrMemberNotFound { + var m db.Member + if id, err := xid.FromString(chi.URLParam(r, "memberRef")); err == nil { + log.Debugf("%v/%v is xid", chi.URLParam(r, "memberRef"), id) + + m, err = s.DB.Member(ctx, id) + if err != nil { + if err == db.ErrMemberNotFound { + return server.APIError{Code: server.ErrMemberNotFound} + } + + return errors.Wrap(err, "getting member") + } + } else { + id, err := common.ParseSnowflake(chi.URLParam(r, "memberRef")) + if err != nil { + log.Debugf("%v/%v is not valid snowflake", chi.URLParam(r, "memberRef"), id) + return server.APIError{Code: server.ErrMemberNotFound} } - return errors.Wrap(err, "getting member") + log.Debugf("%v/%v is valid snowflake", chi.URLParam(r, "memberRef"), id) + + m, err = s.DB.MemberBySnowflake(ctx, common.MemberID(id)) + if err != nil { + if err == db.ErrMemberNotFound { + return server.APIError{Code: server.ErrMemberNotFound} + } + + return errors.Wrap(err, "getting member") + } } if m.UserID != claims.UserID { @@ -234,7 +252,7 @@ func (s *Server) patchMember(w http.ResponseWriter, r *http.Request) error { } defer tx.Rollback(ctx) - m, err = s.DB.UpdateMember(ctx, tx, id, req.Name, req.DisplayName, req.Bio, req.Unlisted, req.Links, avatarHash) + m, err = s.DB.UpdateMember(ctx, tx, m.ID, req.Name, req.DisplayName, req.Bio, req.Unlisted, req.Links, avatarHash) if err != nil { switch errors.Cause(err) { case db.ErrNothingToUpdate: @@ -258,9 +276,9 @@ func (s *Server) patchMember(w http.ResponseWriter, r *http.Request) error { pronouns = *req.Pronouns } - err = s.DB.SetMemberNamesPronouns(ctx, tx, id, names, pronouns) + err = s.DB.SetMemberNamesPronouns(ctx, tx, m.ID, names, pronouns) if err != nil { - log.Errorf("setting names for member %v: %v", id, err) + log.Errorf("setting names for member %v: %v", m.ID, err) return err } m.Names = names @@ -269,16 +287,16 @@ func (s *Server) patchMember(w http.ResponseWriter, r *http.Request) error { var fields []db.Field if req.Fields != nil { - err = s.DB.SetMemberFields(ctx, tx, id, *req.Fields) + err = s.DB.SetMemberFields(ctx, tx, m.ID, *req.Fields) if err != nil { - log.Errorf("setting fields for member %v: %v", id, err) + log.Errorf("setting fields for member %v: %v", m.ID, err) return err } fields = *req.Fields } else { - fields, err = s.DB.MemberFields(ctx, id) + fields, err = s.DB.MemberFields(ctx, m.ID) if err != nil { - log.Errorf("getting fields for member %v: %v", id, err) + log.Errorf("getting fields for member %v: %v", m.ID, err) return err } } diff --git a/backend/routes/v1/user/flags.go b/backend/routes/v1/user/flags.go index 899a675..b9c3ced 100644 --- a/backend/routes/v1/user/flags.go +++ b/backend/routes/v1/user/flags.go @@ -1,6 +1,7 @@ package user import ( + "context" "fmt" "net/http" "strings" @@ -121,6 +122,24 @@ type patchUserFlagRequest struct { Description *string `json:"description"` } +func (s *Server) parseFlag(ctx context.Context, flags []db.PrideFlag, flagRef string) (db.PrideFlag, bool) { + if id, err := xid.FromString(flagRef); err == nil { + for _, f := range flags { + if f.ID == id { + return f, true + } + } + } + if id, err := common.ParseSnowflake(flagRef); err == nil { + for _, f := range flags { + if f.SnowflakeID == common.FlagID(id) { + return f, true + } + } + } + return db.PrideFlag{}, false +} + func (s *Server) patchUserFlag(w http.ResponseWriter, r *http.Request) error { ctx := r.Context() claims, _ := server.ClaimsFromContext(ctx) @@ -129,28 +148,13 @@ func (s *Server) patchUserFlag(w http.ResponseWriter, r *http.Request) error { return server.APIError{Code: server.ErrMissingPermissions, Details: "This token is read-only"} } - flagID, err := xid.FromString(chi.URLParam(r, "flagID")) - if err != nil { - return server.APIError{Code: server.ErrNotFound, Details: "Invalid flag ID"} - } - flags, err := s.DB.AccountFlags(ctx, claims.UserID) if err != nil { return errors.Wrap(err, "getting current user flags") } - if len(flags) >= db.MaxPrideFlags { - return server.APIError{ - Code: server.ErrFlagLimitReached, - } - } - var found bool - for _, flag := range flags { - if flag.ID == flagID { - found = true - break - } - } - if !found { + + flag, ok := s.parseFlag(ctx, flags, chi.URLParam(r, "flagID")) + if !ok { return server.APIError{Code: server.ErrNotFound, Details: "No flag with that ID found"} } @@ -190,7 +194,7 @@ func (s *Server) patchUserFlag(w http.ResponseWriter, r *http.Request) error { } defer tx.Rollback(ctx) - flag, err := s.DB.EditFlag(ctx, tx, flagID, req.Name, req.Description, nil) + flag, err = s.DB.EditFlag(ctx, tx, flag.ID, req.Name, req.Description, nil) if err != nil { return errors.Wrap(err, "updating flag") } @@ -212,19 +216,16 @@ func (s *Server) deleteUserFlag(w http.ResponseWriter, r *http.Request) error { return server.APIError{Code: server.ErrMissingPermissions, Details: "This token is read-only"} } - flagID, err := xid.FromString(chi.URLParam(r, "flagID")) + flags, err := s.DB.AccountFlags(ctx, claims.UserID) if err != nil { - return server.APIError{Code: server.ErrNotFound, Details: "Invalid flag ID"} + return errors.Wrap(err, "getting current user flags") } - flag, err := s.DB.UserFlag(ctx, flagID) - if err != nil { - if err == db.ErrFlagNotFound { - return server.APIError{Code: server.ErrNotFound, Details: "Flag not found"} - } - - return errors.Wrap(err, "getting flag object") + flag, ok := s.parseFlag(ctx, flags, chi.URLParam(r, "flagID")) + if !ok { + return server.APIError{Code: server.ErrNotFound, Details: "No flag with that ID found"} } + if flag.UserID != claims.UserID { return server.APIError{Code: server.ErrNotFound, Details: "Flag not found"} } diff --git a/package.json b/package.json new file mode 100644 index 0000000..fadb53c --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "pronouns", + "private": true, + "scripts": { + "dev": "concurrently \"air\" \"make dev\"" + }, + "author": "sam ", + "license": "AGPL-3.0-only", + "devDependencies": { + "concurrently": "^8.2.1" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..23ad836 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2462 @@ +lockfileVersion: '6.0' + +importers: + + .: + devDependencies: + concurrently: + specifier: ^8.2.1 + version: 8.2.1 + + frontend: + dependencies: + '@fontsource/firago': + specifier: ^4.5.3 + version: 4.5.3 + '@popperjs/core': + specifier: ^2.11.7 + version: 2.11.7 + '@sentry/node': + specifier: ^7.46.0 + version: 7.46.0 + base64-arraybuffer: + specifier: ^1.0.2 + version: 1.0.2 + bootstrap: + specifier: 5.3.0-alpha1 + version: 5.3.0-alpha1(@popperjs/core@2.11.7) + bootstrap-icons: + specifier: ^1.10.4 + version: 1.10.4 + jose: + specifier: ^4.13.1 + version: 4.13.1 + luxon: + specifier: ^3.3.0 + version: 3.3.0 + markdown-it: + specifier: ^13.0.1 + version: 13.0.1 + pretty-bytes: + specifier: ^6.1.0 + version: 6.1.0 + sanitize-html: + specifier: ^2.10.0 + version: 2.10.0 + devDependencies: + '@sveltejs/adapter-auto': + specifier: ^2.0.0 + version: 2.0.0(@sveltejs/kit@1.15.0) + '@sveltejs/adapter-node': + specifier: ^1.2.3 + version: 1.2.3(@sveltejs/kit@1.15.0) + '@sveltejs/kit': + specifier: ^1.15.0 + version: 1.15.0(svelte@3.58.0)(vite@4.2.1) + '@types/luxon': + specifier: ^3.2.2 + version: 3.2.2 + '@types/markdown-it': + specifier: ^12.2.3 + version: 12.2.3 + '@types/node': + specifier: ^18.15.11 + version: 18.15.11 + '@types/sanitize-html': + specifier: ^2.9.0 + version: 2.9.0 + '@typescript-eslint/eslint-plugin': + specifier: ^5.57.1 + version: 5.57.1(@typescript-eslint/parser@5.57.1)(eslint@8.37.0)(typescript@4.9.5) + '@typescript-eslint/parser': + specifier: ^5.57.1 + version: 5.57.1(eslint@8.37.0)(typescript@4.9.5) + eslint: + specifier: ^8.37.0 + version: 8.37.0 + eslint-config-prettier: + specifier: ^8.8.0 + version: 8.8.0(eslint@8.37.0) + eslint-plugin-svelte3: + specifier: ^4.0.0 + version: 4.0.0(eslint@8.37.0)(svelte@3.58.0) + prettier: + specifier: ^2.8.7 + version: 2.8.7 + prettier-plugin-svelte: + specifier: ^2.10.0 + version: 2.10.0(prettier@2.8.7)(svelte@3.58.0) + svelte: + specifier: ^3.58.0 + version: 3.58.0 + svelte-check: + specifier: ^3.1.4 + version: 3.1.4(svelte@3.58.0) + svelte-hcaptcha: + specifier: ^0.1.1 + version: 0.1.1 + sveltestrap: + specifier: ^5.10.0 + version: 5.10.0(svelte@3.58.0) + tslib: + specifier: ^2.5.0 + version: 2.5.0 + typescript: + specifier: ^4.9.5 + version: 4.9.5 + vite: + specifier: ^4.2.1 + version: 4.2.1(@types/node@18.15.11) + vite-plugin-markdown: + specifier: ^2.1.0 + version: 2.1.0(vite@4.2.1) + +packages: + + /@aashutoshrathi/word-wrap@1.2.6: + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} + dev: true + + /@babel/runtime@7.22.15: + resolution: {integrity: sha512-T0O+aa+4w0u06iNmapipJXMV4HoUir03hpx3/YqXXhu9xim3w+dVphjFWl1OH8NbZHw5Lbm9k45drDkgq2VNNA==} + engines: {node: '>=6.9.0'} + dependencies: + regenerator-runtime: 0.14.0 + dev: true + + /@esbuild/android-arm64@0.17.19: + resolution: {integrity: sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.17.19: + resolution: {integrity: sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.17.19: + resolution: {integrity: sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.17.19: + resolution: {integrity: sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.17.19: + resolution: {integrity: sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.17.19: + resolution: {integrity: sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.17.19: + resolution: {integrity: sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.17.19: + resolution: {integrity: sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.17.19: + resolution: {integrity: sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.17.19: + resolution: {integrity: sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.17.19: + resolution: {integrity: sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.17.19: + resolution: {integrity: sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.17.19: + resolution: {integrity: sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.17.19: + resolution: {integrity: sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.17.19: + resolution: {integrity: sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.17.19: + resolution: {integrity: sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.17.19: + resolution: {integrity: sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.17.19: + resolution: {integrity: sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.17.19: + resolution: {integrity: sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.17.19: + resolution: {integrity: sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.17.19: + resolution: {integrity: sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.17.19: + resolution: {integrity: sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@eslint-community/eslint-utils@4.4.0(eslint@8.37.0): + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + dependencies: + eslint: 8.37.0 + eslint-visitor-keys: 3.4.3 + dev: true + + /@eslint-community/regexpp@4.8.0: + resolution: {integrity: sha512-JylOEEzDiOryeUnFbQz+oViCXS0KsvR1mvHkoMiu5+UiBvy+RYX7tzlIIIEstF/gVa2tj9AQXk3dgnxv6KxhFg==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + dev: true + + /@eslint/eslintrc@2.1.2: + resolution: {integrity: sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + ajv: 6.12.6 + debug: 4.3.4 + espree: 9.6.1 + globals: 13.21.0 + ignore: 5.2.4 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + dev: true + + /@eslint/js@8.37.0: + resolution: {integrity: sha512-x5vzdtOOGgFVDCUs81QRB2+liax8rFg3+7hqM+QhBG0/G3F1ZsoYl97UrqgHgQ9KKT7G6c4V+aTUCgu/n22v1A==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@fontsource/firago@4.5.3: + resolution: {integrity: sha512-Kn6FBi6MGNPYwzVKUtgyWGUj0zV18El263ieZi8A7TM2LPKsfzON5ENeKfjyTKfYErkjg9ktwiNP3SLsEOSBNQ==} + dev: false + + /@humanwhocodes/config-array@0.11.11: + resolution: {integrity: sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA==} + engines: {node: '>=10.10.0'} + dependencies: + '@humanwhocodes/object-schema': 1.2.1 + debug: 4.3.4 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + dev: true + + /@humanwhocodes/module-importer@1.0.1: + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + dev: true + + /@humanwhocodes/object-schema@1.2.1: + resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==} + dev: true + + /@jridgewell/resolve-uri@3.1.1: + resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} + engines: {node: '>=6.0.0'} + dev: true + + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + + /@jridgewell/trace-mapping@0.3.19: + resolution: {integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==} + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /@nodelib/fs.scandir@2.1.5: + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + dev: true + + /@nodelib/fs.stat@2.0.5: + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + dev: true + + /@nodelib/fs.walk@1.2.8: + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.15.0 + dev: true + + /@polka/url@1.0.0-next.23: + resolution: {integrity: sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==} + dev: true + + /@popperjs/core@2.11.7: + resolution: {integrity: sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==} + + /@rollup/plugin-commonjs@24.1.0(rollup@3.29.0): + resolution: {integrity: sha512-eSL45hjhCWI0jCCXcNtLVqM5N1JlBGvlFfY0m6oOYnLCJ6N0qEXoZql4sY2MOUArzhH4SA/qBpTxvvZp2Sc+DQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.68.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.0.4(rollup@3.29.0) + commondir: 1.0.1 + estree-walker: 2.0.2 + glob: 8.1.0 + is-reference: 1.2.1 + magic-string: 0.27.0 + rollup: 3.29.0 + dev: true + + /@rollup/plugin-json@6.0.0(rollup@3.29.0): + resolution: {integrity: sha512-i/4C5Jrdr1XUarRhVu27EEwjt4GObltD7c+MkCIpO2QIbojw8MUs+CCTqOphQi3Qtg1FLmYt+l+6YeoIf51J7w==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.0.4(rollup@3.29.0) + rollup: 3.29.0 + dev: true + + /@rollup/plugin-node-resolve@15.2.1(rollup@3.29.0): + resolution: {integrity: sha512-nsbUg588+GDSu8/NS8T4UAshO6xeaOfINNuXeVHcKV02LJtoRaM1SiOacClw4kws1SFiNhdLGxlbMY9ga/zs/w==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^2.78.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@rollup/pluginutils': 5.0.4(rollup@3.29.0) + '@types/resolve': 1.20.2 + deepmerge: 4.3.1 + is-builtin-module: 3.2.1 + is-module: 1.0.0 + resolve: 1.22.4 + rollup: 3.29.0 + dev: true + + /@rollup/pluginutils@5.0.4(rollup@3.29.0): + resolution: {integrity: sha512-0KJnIoRI8A+a1dqOYLxH8vBf8bphDmty5QvIm2hqm7oFCFYKCAZWWd2hXgMibaPsNDhI0AtpYfQZJG47pt/k4g==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + dependencies: + '@types/estree': 1.0.1 + estree-walker: 2.0.2 + picomatch: 2.3.1 + rollup: 3.29.0 + dev: true + + /@sentry-internal/tracing@7.46.0: + resolution: {integrity: sha512-KYoppa7PPL8Er7bdPoxTNUfIY804JL7hhOEomQHYD22rLynwQ4AaLm3YEY75QWwcGb0B7ZDMV+tSumW7Rxuwuw==} + engines: {node: '>=8'} + dependencies: + '@sentry/core': 7.46.0 + '@sentry/types': 7.46.0 + '@sentry/utils': 7.46.0 + tslib: 1.14.1 + dev: false + + /@sentry/core@7.46.0: + resolution: {integrity: sha512-BnNHGh/ZTztqQedFko7vb2u6yLs/kWesOQNivav32ZbsEpVCjcmG1gOJXh2YmGIvj3jXOC9a4xfIuh+lYFcA6A==} + engines: {node: '>=8'} + dependencies: + '@sentry/types': 7.46.0 + '@sentry/utils': 7.46.0 + tslib: 1.14.1 + dev: false + + /@sentry/node@7.46.0: + resolution: {integrity: sha512-+GrgJMCye2WXGarRiU5IJHCK27xg7xbPc2XjGojBKbBoZfqxVAWbXEK4bnBQgRGP1pCmrU/M6ZhVgR3dP580xA==} + engines: {node: '>=8'} + dependencies: + '@sentry-internal/tracing': 7.46.0 + '@sentry/core': 7.46.0 + '@sentry/types': 7.46.0 + '@sentry/utils': 7.46.0 + cookie: 0.4.2 + https-proxy-agent: 5.0.1 + lru_map: 0.3.3 + tslib: 1.14.1 + transitivePeerDependencies: + - supports-color + dev: false + + /@sentry/types@7.46.0: + resolution: {integrity: sha512-2FMEMgt2h6u7AoELhNhu9L54GAh67KKfK2pJ1kEXJHmWxM9FSCkizjLs/t+49xtY7jEXr8qYq8bV967VfDPQ9g==} + engines: {node: '>=8'} + dev: false + + /@sentry/utils@7.46.0: + resolution: {integrity: sha512-elRezDAF84guMG0OVIIZEWm6wUpgbda4HGks98CFnPsrnMm3N1bdBI9XdlxYLtf+ir5KsGR5YlEIf/a0kRUwAQ==} + engines: {node: '>=8'} + dependencies: + '@sentry/types': 7.46.0 + tslib: 1.14.1 + dev: false + + /@sveltejs/adapter-auto@2.0.0(@sveltejs/kit@1.15.0): + resolution: {integrity: sha512-b+gkHFZgD771kgV3aO4avHFd7y1zhmMYy9i6xOK7m/rwmwaRO8gnF5zBc0Rgca80B2PMU1bKNxyBTHA14OzUAQ==} + peerDependencies: + '@sveltejs/kit': ^1.0.0 + dependencies: + '@sveltejs/kit': 1.15.0(svelte@3.58.0)(vite@4.2.1) + import-meta-resolve: 2.2.2 + dev: true + + /@sveltejs/adapter-node@1.2.3(@sveltejs/kit@1.15.0): + resolution: {integrity: sha512-Fv6NyVpVWYA63KRaV6dDjcU8ytcWFiUr0siJOoHl+oWy5WHNEuRiJOUdiZzYbZo8MmvFaCoxHkTgPrVQhpqaRA==} + peerDependencies: + '@sveltejs/kit': ^1.0.0 + dependencies: + '@rollup/plugin-commonjs': 24.1.0(rollup@3.29.0) + '@rollup/plugin-json': 6.0.0(rollup@3.29.0) + '@rollup/plugin-node-resolve': 15.2.1(rollup@3.29.0) + '@sveltejs/kit': 1.15.0(svelte@3.58.0)(vite@4.2.1) + rollup: 3.29.0 + dev: true + + /@sveltejs/kit@1.15.0(svelte@3.58.0)(vite@4.2.1): + resolution: {integrity: sha512-fvDsW9msxWjDU/j9wwLlxEZ6cpXQYcmcQHq7neJMqibMEl39gI1ztVymGnYqM8KLqZXwNmhKtLu8EPheukKtXQ==} + engines: {node: ^16.14 || >=18} + hasBin: true + requiresBuild: true + peerDependencies: + svelte: ^3.54.0 + vite: ^4.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte': 2.4.5(svelte@3.58.0)(vite@4.2.1) + '@types/cookie': 0.5.2 + cookie: 0.5.0 + devalue: 4.3.2 + esm-env: 1.0.0 + kleur: 4.1.5 + magic-string: 0.30.3 + mime: 3.0.0 + sade: 1.8.1 + set-cookie-parser: 2.6.0 + sirv: 2.0.3 + svelte: 3.58.0 + tiny-glob: 0.2.9 + undici: 5.21.0 + vite: 4.2.1(@types/node@18.15.11) + transitivePeerDependencies: + - supports-color + dev: true + + /@sveltejs/vite-plugin-svelte-inspector@1.0.4(@sveltejs/vite-plugin-svelte@2.4.5)(svelte@3.58.0)(vite@4.2.1): + resolution: {integrity: sha512-zjiuZ3yydBtwpF3bj0kQNV0YXe+iKE545QGZVTaylW3eAzFr+pJ/cwK8lZEaRp4JtaJXhD5DyWAV4AxLh6DgaQ==} + engines: {node: ^14.18.0 || >= 16} + peerDependencies: + '@sveltejs/vite-plugin-svelte': ^2.2.0 + svelte: ^3.54.0 || ^4.0.0 + vite: ^4.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte': 2.4.5(svelte@3.58.0)(vite@4.2.1) + debug: 4.3.4 + svelte: 3.58.0 + vite: 4.2.1(@types/node@18.15.11) + transitivePeerDependencies: + - supports-color + dev: true + + /@sveltejs/vite-plugin-svelte@2.4.5(svelte@3.58.0)(vite@4.2.1): + resolution: {integrity: sha512-UJKsFNwhzCVuiZd06jM/psscyNJNDwjQC+qIeb7GBJK9iWeQCcIyfcPWDvbCudfcJggY9jtxJeeaZH7uny93FQ==} + engines: {node: ^14.18.0 || >= 16} + peerDependencies: + svelte: ^3.54.0 || ^4.0.0 + vite: ^4.0.0 + dependencies: + '@sveltejs/vite-plugin-svelte-inspector': 1.0.4(@sveltejs/vite-plugin-svelte@2.4.5)(svelte@3.58.0)(vite@4.2.1) + debug: 4.3.4 + deepmerge: 4.3.1 + kleur: 4.1.5 + magic-string: 0.30.3 + svelte: 3.58.0 + svelte-hmr: 0.15.3(svelte@3.58.0) + vite: 4.2.1(@types/node@18.15.11) + vitefu: 0.2.4(vite@4.2.1) + transitivePeerDependencies: + - supports-color + dev: true + + /@types/cookie@0.5.2: + resolution: {integrity: sha512-DBpRoJGKJZn7RY92dPrgoMew8xCWc2P71beqsjyhEI/Ds9mOyVmBwtekyfhpwFIVt1WrxTonFifiOZ62V8CnNA==} + dev: true + + /@types/estree@1.0.1: + resolution: {integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA==} + dev: true + + /@types/json-schema@7.0.12: + resolution: {integrity: sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==} + dev: true + + /@types/linkify-it@3.0.3: + resolution: {integrity: sha512-pTjcqY9E4nOI55Wgpz7eiI8+LzdYnw3qxXCfHyBDdPbYvbyLgWLJGh8EdPvqawwMK1Uo1794AUkkR38Fr0g+2g==} + dev: true + + /@types/luxon@3.2.2: + resolution: {integrity: sha512-CuF9hIlsxGpJO4EztrW23/q1L9ctQfb5JM9mnLCJhhA8z81K2b4LTVjQYySXWhFV5SMyUsPYH/IcvvXDCKwa2g==} + dev: true + + /@types/markdown-it@12.2.3: + resolution: {integrity: sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==} + dependencies: + '@types/linkify-it': 3.0.3 + '@types/mdurl': 1.0.2 + dev: true + + /@types/mdurl@1.0.2: + resolution: {integrity: sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==} + dev: true + + /@types/node@18.15.11: + resolution: {integrity: sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==} + dev: true + + /@types/pug@2.0.6: + resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==} + dev: true + + /@types/resolve@1.20.2: + resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} + dev: true + + /@types/sanitize-html@2.9.0: + resolution: {integrity: sha512-4fP/kEcKNj2u39IzrxWYuf/FnCCwwQCpif6wwY6ROUS1EPRIfWJjGkY3HIowY1EX/VbX5e86yq8AAE7UPMgATg==} + dependencies: + htmlparser2: 8.0.2 + dev: true + + /@types/semver@7.5.1: + resolution: {integrity: sha512-cJRQXpObxfNKkFAZbJl2yjWtJCqELQIdShsogr1d2MilP8dKD9TE/nEKHkJgUNHdGKCQaf9HbIynuV2csLGVLg==} + dev: true + + /@typescript-eslint/eslint-plugin@5.57.1(@typescript-eslint/parser@5.57.1)(eslint@8.37.0)(typescript@4.9.5): + resolution: {integrity: sha512-1MeobQkQ9tztuleT3v72XmY0XuKXVXusAhryoLuU5YZ+mXoYKZP9SQ7Flulh1NX4DTjpGTc2b/eMu4u7M7dhnQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + '@typescript-eslint/parser': ^5.0.0 + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@eslint-community/regexpp': 4.8.0 + '@typescript-eslint/parser': 5.57.1(eslint@8.37.0)(typescript@4.9.5) + '@typescript-eslint/scope-manager': 5.57.1 + '@typescript-eslint/type-utils': 5.57.1(eslint@8.37.0)(typescript@4.9.5) + '@typescript-eslint/utils': 5.57.1(eslint@8.37.0)(typescript@4.9.5) + debug: 4.3.4 + eslint: 8.37.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.4 + natural-compare-lite: 1.4.0 + semver: 7.5.4 + tsutils: 3.21.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/parser@5.57.1(eslint@8.37.0)(typescript@4.9.5): + resolution: {integrity: sha512-hlA0BLeVSA/wBPKdPGxoVr9Pp6GutGoY380FEhbVi0Ph4WNe8kLvqIRx76RSQt1lynZKfrXKs0/XeEk4zZycuA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/scope-manager': 5.57.1 + '@typescript-eslint/types': 5.57.1 + '@typescript-eslint/typescript-estree': 5.57.1(typescript@4.9.5) + debug: 4.3.4 + eslint: 8.37.0 + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/scope-manager@5.57.1: + resolution: {integrity: sha512-N/RrBwEUKMIYxSKl0oDK5sFVHd6VI7p9K5MyUlVYAY6dyNb/wHUqndkTd3XhpGlXgnQsBkRZuu4f9kAHghvgPw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.57.1 + '@typescript-eslint/visitor-keys': 5.57.1 + dev: true + + /@typescript-eslint/type-utils@5.57.1(eslint@8.37.0)(typescript@4.9.5): + resolution: {integrity: sha512-/RIPQyx60Pt6ga86hKXesXkJ2WOS4UemFrmmq/7eOyiYjYv/MUSHPlkhU6k9T9W1ytnTJueqASW+wOmW4KrViw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '*' + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/typescript-estree': 5.57.1(typescript@4.9.5) + '@typescript-eslint/utils': 5.57.1(eslint@8.37.0)(typescript@4.9.5) + debug: 4.3.4 + eslint: 8.37.0 + tsutils: 3.21.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/types@5.57.1: + resolution: {integrity: sha512-bSs4LOgyV3bJ08F5RDqO2KXqg3WAdwHCu06zOqcQ6vqbTJizyBhuh1o1ImC69X4bV2g1OJxbH71PJqiO7Y1RuA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /@typescript-eslint/typescript-estree@5.57.1(typescript@4.9.5): + resolution: {integrity: sha512-A2MZqD8gNT0qHKbk2wRspg7cHbCDCk2tcqt6ScCFLr5Ru8cn+TCfM786DjPhqwseiS+PrYwcXht5ztpEQ6TFTw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + dependencies: + '@typescript-eslint/types': 5.57.1 + '@typescript-eslint/visitor-keys': 5.57.1 + debug: 4.3.4 + globby: 11.1.0 + is-glob: 4.0.3 + semver: 7.5.4 + tsutils: 3.21.0(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - supports-color + dev: true + + /@typescript-eslint/utils@5.57.1(eslint@8.37.0)(typescript@4.9.5): + resolution: {integrity: sha512-kN6vzzf9NkEtawECqze6v99LtmDiUJCVpvieTFA1uL7/jDghiJGubGZ5csicYHU1Xoqb3oH/R5cN5df6W41Nfg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.37.0) + '@types/json-schema': 7.0.12 + '@types/semver': 7.5.1 + '@typescript-eslint/scope-manager': 5.57.1 + '@typescript-eslint/types': 5.57.1 + '@typescript-eslint/typescript-estree': 5.57.1(typescript@4.9.5) + eslint: 8.37.0 + eslint-scope: 5.1.1 + semver: 7.5.4 + transitivePeerDependencies: + - supports-color + - typescript + dev: true + + /@typescript-eslint/visitor-keys@5.57.1: + resolution: {integrity: sha512-RjQrAniDU0CEk5r7iphkm731zKlFiUjvcBS2yHAg8WWqFMCaCrD0rKEVOMUyMMcbGPZ0bPp56srkGWrgfZqLRA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + '@typescript-eslint/types': 5.57.1 + eslint-visitor-keys: 3.4.3 + dev: true + + /acorn-jsx@5.3.2(acorn@8.10.0): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.10.0 + dev: true + + /acorn@8.10.0: + resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} + engines: {node: '>=0.4.0'} + hasBin: true + dev: true + + /agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: false + + /ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + dev: true + + /ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + dev: true + + /ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + dependencies: + color-convert: 2.0.1 + dev: true + + /anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + dev: true + + /argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + dependencies: + sprintf-js: 1.0.3 + dev: true + + /argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + /array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + dev: true + + /balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /base64-arraybuffer@1.0.2: + resolution: {integrity: sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==} + engines: {node: '>= 0.6.0'} + dev: false + + /binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + dev: true + + /bootstrap-icons@1.10.4: + resolution: {integrity: sha512-eI3HyIUmpGKRiRv15FCZccV+2sreGE2NnmH8mtxV/nPOzQVu0sPEj8HhF1MwjJ31IhjF0rgMvtYOX5VqIzcb/A==} + dev: false + + /bootstrap@5.3.0-alpha1(@popperjs/core@2.11.7): + resolution: {integrity: sha512-ABZpKK4ObS3kKlIqH+ZVDqoy5t/bhFG0oHTAzByUdon7YIom0lpCeTqRniDzJmbtcWkNe800VVPBiJgxSYTYew==} + peerDependencies: + '@popperjs/core': ^2.11.6 + dependencies: + '@popperjs/core': 2.11.7 + dev: false + + /brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + + /brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + dependencies: + balanced-match: 1.0.2 + dev: true + + /braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + dependencies: + fill-range: 7.0.1 + dev: true + + /buffer-crc32@0.2.13: + resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} + dev: true + + /builtin-modules@3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + dev: true + + /busboy@1.6.0: + resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==} + engines: {node: '>=10.16.0'} + dependencies: + streamsearch: 1.1.0 + dev: true + + /callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + dev: true + + /chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + dev: true + + /chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + dependencies: + anymatch: 3.1.3 + braces: 3.0.2 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + dev: true + + /color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + dependencies: + color-name: 1.1.4 + dev: true + + /color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + dev: true + + /commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + dev: true + + /concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + dev: true + + /concurrently@8.2.1: + resolution: {integrity: sha512-nVraf3aXOpIcNud5pB9M82p1tynmZkrSGQ1p6X/VY8cJ+2LMVqAgXsJxYYefACSHbTYlm92O1xuhdGTjwoEvbQ==} + engines: {node: ^14.13.0 || >=16.0.0} + hasBin: true + dependencies: + chalk: 4.1.2 + date-fns: 2.30.0 + lodash: 4.17.21 + rxjs: 7.8.1 + shell-quote: 1.8.1 + spawn-command: 0.0.2 + supports-color: 8.1.1 + tree-kill: 1.2.2 + yargs: 17.7.2 + dev: true + + /cookie@0.4.2: + resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==} + engines: {node: '>= 0.6'} + dev: false + + /cookie@0.5.0: + resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} + engines: {node: '>= 0.6'} + dev: true + + /cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + dev: true + + /date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + dependencies: + '@babel/runtime': 7.22.15 + dev: true + + /debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + dependencies: + ms: 2.1.2 + + /deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + dev: true + + /deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + /detect-indent@6.1.0: + resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} + engines: {node: '>=8'} + dev: true + + /devalue@4.3.2: + resolution: {integrity: sha512-KqFl6pOgOW+Y6wJgu80rHpo2/3H07vr8ntR9rkkFIRETewbf5GaYYcakYfiKz89K+sLsuPkQIZaXDMjUObZwWg==} + dev: true + + /dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + dependencies: + path-type: 4.0.0 + dev: true + + /doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + dependencies: + esutils: 2.0.3 + dev: true + + /dom-serializer@1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + dev: true + + /dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + entities: 4.5.0 + + /domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + /domhandler@4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 + dev: true + + /domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + dependencies: + domelementtype: 2.3.0 + + /domutils@2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + dev: true + + /domutils@3.1.0: + resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==} + dependencies: + dom-serializer: 2.0.0 + domelementtype: 2.3.0 + domhandler: 5.0.3 + + /emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + dev: true + + /entities@2.1.0: + resolution: {integrity: sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==} + dev: true + + /entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + dev: true + + /entities@3.0.1: + resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==} + engines: {node: '>=0.12'} + dev: false + + /entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + /es6-promise@3.3.1: + resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} + dev: true + + /esbuild@0.17.19: + resolution: {integrity: sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.17.19 + '@esbuild/android-arm64': 0.17.19 + '@esbuild/android-x64': 0.17.19 + '@esbuild/darwin-arm64': 0.17.19 + '@esbuild/darwin-x64': 0.17.19 + '@esbuild/freebsd-arm64': 0.17.19 + '@esbuild/freebsd-x64': 0.17.19 + '@esbuild/linux-arm': 0.17.19 + '@esbuild/linux-arm64': 0.17.19 + '@esbuild/linux-ia32': 0.17.19 + '@esbuild/linux-loong64': 0.17.19 + '@esbuild/linux-mips64el': 0.17.19 + '@esbuild/linux-ppc64': 0.17.19 + '@esbuild/linux-riscv64': 0.17.19 + '@esbuild/linux-s390x': 0.17.19 + '@esbuild/linux-x64': 0.17.19 + '@esbuild/netbsd-x64': 0.17.19 + '@esbuild/openbsd-x64': 0.17.19 + '@esbuild/sunos-x64': 0.17.19 + '@esbuild/win32-arm64': 0.17.19 + '@esbuild/win32-ia32': 0.17.19 + '@esbuild/win32-x64': 0.17.19 + dev: true + + /escalade@3.1.1: + resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} + engines: {node: '>=6'} + dev: true + + /escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + /eslint-config-prettier@8.8.0(eslint@8.37.0): + resolution: {integrity: sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + dependencies: + eslint: 8.37.0 + dev: true + + /eslint-plugin-svelte3@4.0.0(eslint@8.37.0)(svelte@3.58.0): + resolution: {integrity: sha512-OIx9lgaNzD02+MDFNLw0GEUbuovNcglg+wnd/UY0fbZmlQSz7GlQiQ1f+yX0XvC07XPcDOnFcichqI3xCwp71g==} + peerDependencies: + eslint: '>=8.0.0' + svelte: ^3.2.0 + dependencies: + eslint: 8.37.0 + svelte: 3.58.0 + dev: true + + /eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + dependencies: + esrecurse: 4.3.0 + estraverse: 4.3.0 + dev: true + + /eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + dev: true + + /eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dev: true + + /eslint@8.37.0: + resolution: {integrity: sha512-NU3Ps9nI05GUoVMxcZx1J8CNR6xOvUT4jAUMH5+z8lpp3aEdPVCImKw6PWG4PY+Vfkpr+jvMpxs/qoE7wq0sPw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + hasBin: true + dependencies: + '@eslint-community/eslint-utils': 4.4.0(eslint@8.37.0) + '@eslint-community/regexpp': 4.8.0 + '@eslint/eslintrc': 2.1.2 + '@eslint/js': 8.37.0 + '@humanwhocodes/config-array': 0.11.11 + '@humanwhocodes/module-importer': 1.0.1 + '@nodelib/fs.walk': 1.2.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.3 + debug: 4.3.4 + doctrine: 3.0.0 + escape-string-regexp: 4.0.0 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.5.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 6.0.1 + find-up: 5.0.0 + glob-parent: 6.0.2 + globals: 13.21.0 + grapheme-splitter: 1.0.4 + ignore: 5.2.4 + import-fresh: 3.3.0 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + is-path-inside: 3.0.3 + js-sdsl: 4.4.2 + js-yaml: 4.1.0 + json-stable-stringify-without-jsonify: 1.0.1 + levn: 0.4.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.3 + strip-ansi: 6.0.1 + strip-json-comments: 3.1.1 + text-table: 0.2.0 + transitivePeerDependencies: + - supports-color + dev: true + + /esm-env@1.0.0: + resolution: {integrity: sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==} + dev: true + + /espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + dependencies: + acorn: 8.10.0 + acorn-jsx: 5.3.2(acorn@8.10.0) + eslint-visitor-keys: 3.4.3 + dev: true + + /esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + dev: true + + /esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + dependencies: + estraverse: 5.3.0 + dev: true + + /esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + dependencies: + estraverse: 5.3.0 + dev: true + + /estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + dev: true + + /estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + dev: true + + /estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + dev: true + + /esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + dev: true + + /fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + dev: true + + /fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.5 + dev: true + + /fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + dev: true + + /fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + dev: true + + /fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + dependencies: + reusify: 1.0.4 + dev: true + + /file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + dependencies: + flat-cache: 3.1.0 + dev: true + + /fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + dependencies: + to-regex-range: 5.0.1 + dev: true + + /find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + dev: true + + /flat-cache@3.1.0: + resolution: {integrity: sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==} + engines: {node: '>=12.0.0'} + dependencies: + flatted: 3.2.7 + keyv: 4.5.3 + rimraf: 3.0.2 + dev: true + + /flatted@3.2.7: + resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==} + dev: true + + /front-matter@4.0.2: + resolution: {integrity: sha512-I8ZuJ/qG92NWX8i5x1Y8qyj3vizhXS31OxjKDu3LKP+7/qBgfIKValiZIEwoVoJKUHlhWtYrktkxV1XsX+pPlg==} + dependencies: + js-yaml: 3.14.1 + dev: true + + /fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + dev: true + + /fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /function-bind@1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + dev: true + + /get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: true + + /glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + dependencies: + is-glob: 4.0.3 + dev: true + + /glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + dev: true + + /globals@13.21.0: + resolution: {integrity: sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==} + engines: {node: '>=8'} + dependencies: + type-fest: 0.20.2 + dev: true + + /globalyzer@0.1.0: + resolution: {integrity: sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==} + dev: true + + /globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.1 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + dev: true + + /globrex@0.1.2: + resolution: {integrity: sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==} + dev: true + + /graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + dev: true + + /grapheme-splitter@1.0.4: + resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==} + dev: true + + /has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + dev: true + + /has@1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + dev: true + + /htmlparser2@6.1.0: + resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils: 2.8.0 + entities: 2.2.0 + dev: true + + /htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + dependencies: + domelementtype: 2.3.0 + domhandler: 5.0.3 + domutils: 3.1.0 + entities: 4.5.0 + + /https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + dependencies: + agent-base: 6.0.2 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: false + + /ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + dev: true + + /import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + dev: true + + /import-meta-resolve@2.2.2: + resolution: {integrity: sha512-f8KcQ1D80V7RnqVm+/lirO9zkOxjGxhaTC1IPrBGd3MEfNgmNG67tSUO9gTi2F3Blr2Az6g1vocaxzkVnWl9MA==} + dev: true + + /imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + dev: true + + /inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + + /inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + dev: true + + /is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + dependencies: + binary-extensions: 2.2.0 + dev: true + + /is-builtin-module@3.2.1: + resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} + engines: {node: '>=6'} + dependencies: + builtin-modules: 3.3.0 + dev: true + + /is-core-module@2.13.0: + resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} + dependencies: + has: 1.0.3 + dev: true + + /is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + dev: true + + /is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + dev: true + + /is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + dependencies: + is-extglob: 2.1.1 + dev: true + + /is-module@1.0.0: + resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==} + dev: true + + /is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + dev: true + + /is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + dev: true + + /is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + dev: false + + /is-reference@1.2.1: + resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==} + dependencies: + '@types/estree': 1.0.1 + dev: true + + /isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + dev: true + + /jose@4.13.1: + resolution: {integrity: sha512-MSJQC5vXco5Br38mzaQKiq9mwt7lwj2eXpgpRyQYNHYt2lq1PjkWa7DLXX0WVcQLE9HhMh3jPiufS7fhJf+CLQ==} + dev: false + + /js-sdsl@4.4.2: + resolution: {integrity: sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w==} + dev: true + + /js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + dependencies: + argparse: 1.0.10 + esprima: 4.0.1 + dev: true + + /js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + dependencies: + argparse: 2.0.1 + dev: true + + /json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + dev: true + + /json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + dev: true + + /json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + dev: true + + /keyv@4.5.3: + resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} + dependencies: + json-buffer: 3.0.1 + dev: true + + /kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + dev: true + + /levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /linkify-it@3.0.3: + resolution: {integrity: sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==} + dependencies: + uc.micro: 1.0.6 + dev: true + + /linkify-it@4.0.1: + resolution: {integrity: sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==} + dependencies: + uc.micro: 1.0.6 + dev: false + + /locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + dependencies: + p-locate: 5.0.0 + dev: true + + /lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + dev: true + + /lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + dev: true + + /lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + dependencies: + yallist: 4.0.0 + dev: true + + /lru_map@0.3.3: + resolution: {integrity: sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==} + dev: false + + /luxon@3.3.0: + resolution: {integrity: sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==} + engines: {node: '>=12'} + dev: false + + /magic-string@0.27.0: + resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /magic-string@0.30.3: + resolution: {integrity: sha512-B7xGbll2fG/VjP+SWg4sX3JynwIU0mjoTc6MPpKNuIvftk6u6vqhDnk1R80b8C2GBR6ywqy+1DcKBrevBg+bmw==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + + /markdown-it@12.3.2: + resolution: {integrity: sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==} + hasBin: true + dependencies: + argparse: 2.0.1 + entities: 2.1.0 + linkify-it: 3.0.3 + mdurl: 1.0.1 + uc.micro: 1.0.6 + dev: true + + /markdown-it@13.0.1: + resolution: {integrity: sha512-lTlxriVoy2criHP0JKRhO2VDG9c2ypWCsT237eDiLqi09rmbKoUetyGHq2uOIRoRS//kfoJckS0eUzzkDR+k2Q==} + hasBin: true + dependencies: + argparse: 2.0.1 + entities: 3.0.1 + linkify-it: 4.0.1 + mdurl: 1.0.1 + uc.micro: 1.0.6 + dev: false + + /mdurl@1.0.1: + resolution: {integrity: sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==} + + /merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + dev: true + + /micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + dependencies: + braces: 3.0.2 + picomatch: 2.3.1 + dev: true + + /mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + dev: true + + /min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + dev: true + + /minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + dependencies: + brace-expansion: 1.1.11 + dev: true + + /minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + dependencies: + brace-expansion: 2.0.1 + dev: true + + /minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + dev: true + + /mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + dependencies: + minimist: 1.2.8 + dev: true + + /mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + dev: true + + /mrmime@1.0.1: + resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} + engines: {node: '>=10'} + dev: true + + /ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + /nanoid@3.3.6: + resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + /natural-compare-lite@1.4.0: + resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==} + dev: true + + /natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + dev: true + + /normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + dev: true + + /once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + dependencies: + wrappy: 1.0.2 + dev: true + + /optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + dependencies: + '@aashutoshrathi/word-wrap': 1.2.6 + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + dev: true + + /p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + dependencies: + yocto-queue: 0.1.0 + dev: true + + /p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + dependencies: + p-limit: 3.1.0 + dev: true + + /parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + dependencies: + callsites: 3.1.0 + dev: true + + /parse-srcset@1.0.2: + resolution: {integrity: sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==} + dev: false + + /path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + dev: true + + /path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + dev: true + + /path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + dev: true + + /path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + + /path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + dev: true + + /picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + + /picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + dev: true + + /postcss@8.4.29: + resolution: {integrity: sha512-cbI+jaqIeu/VGqXEarWkRCCffhjgXc0qjBtXpqJhTBohMUjUQnbBr0xqX3vEKudc4iviTewcJo5ajcec5+wdJw==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + + /prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + dev: true + + /prettier-plugin-svelte@2.10.0(prettier@2.8.7)(svelte@3.58.0): + resolution: {integrity: sha512-GXMY6t86thctyCvQq+jqElO+MKdB09BkL3hexyGP3Oi8XLKRFaJP1ud/xlWCZ9ZIa2BxHka32zhHfcuU+XsRQg==} + peerDependencies: + prettier: ^1.16.4 || ^2.0.0 + svelte: ^3.2.0 + dependencies: + prettier: 2.8.7 + svelte: 3.58.0 + dev: true + + /prettier@2.8.7: + resolution: {integrity: sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==} + engines: {node: '>=10.13.0'} + hasBin: true + dev: true + + /pretty-bytes@6.1.0: + resolution: {integrity: sha512-Rk753HI8f4uivXi4ZCIYdhmG1V+WKzvRMg/X+M42a6t7D07RcmopXJMDNk6N++7Bl75URRGsb40ruvg7Hcp2wQ==} + engines: {node: ^14.13.1 || >=16.0.0} + dev: false + + /punycode@2.3.0: + resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} + engines: {node: '>=6'} + dev: true + + /queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + dev: true + + /readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + dependencies: + picomatch: 2.3.1 + dev: true + + /regenerator-runtime@0.14.0: + resolution: {integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==} + dev: true + + /require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + dev: true + + /resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + dev: true + + /resolve@1.22.4: + resolution: {integrity: sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==} + hasBin: true + dependencies: + is-core-module: 2.13.0 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + dev: true + + /reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + dev: true + + /rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + hasBin: true + dependencies: + glob: 7.2.3 + dev: true + + /rollup@3.29.0: + resolution: {integrity: sha512-nszM8DINnx1vSS+TpbWKMkxem0CDWk3cSit/WWCBVs9/JZ1I/XLwOsiUglYuYReaeWWSsW9kge5zE5NZtf/a4w==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + dependencies: + queue-microtask: 1.2.3 + dev: true + + /rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + dependencies: + tslib: 2.5.0 + dev: true + + /sade@1.8.1: + resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==} + engines: {node: '>=6'} + dependencies: + mri: 1.2.0 + dev: true + + /sander@0.5.1: + resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==} + dependencies: + es6-promise: 3.3.1 + graceful-fs: 4.2.11 + mkdirp: 0.5.6 + rimraf: 2.7.1 + dev: true + + /sanitize-html@2.10.0: + resolution: {integrity: sha512-JqdovUd81dG4k87vZt6uA6YhDfWkUGruUu/aPmXLxXi45gZExnt9Bnw/qeQU8oGf82vPyaE0vO4aH0PbobB9JQ==} + dependencies: + deepmerge: 4.3.1 + escape-string-regexp: 4.0.0 + htmlparser2: 8.0.2 + is-plain-object: 5.0.0 + parse-srcset: 1.0.2 + postcss: 8.4.29 + dev: false + + /semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + dependencies: + lru-cache: 6.0.0 + dev: true + + /set-cookie-parser@2.6.0: + resolution: {integrity: sha512-RVnVQxTXuerk653XfuliOxBP81Sf0+qfQE73LIYKcyMYHG94AuH0kgrQpRDuTZnSmjpysHmzxJXKNfa6PjFhyQ==} + dev: true + + /shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + dependencies: + shebang-regex: 3.0.0 + dev: true + + /shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + dev: true + + /shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + dev: true + + /sirv@2.0.3: + resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==} + engines: {node: '>= 10'} + dependencies: + '@polka/url': 1.0.0-next.23 + mrmime: 1.0.1 + totalist: 3.0.1 + dev: true + + /slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + dev: true + + /sorcery@0.11.0: + resolution: {integrity: sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==} + hasBin: true + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + buffer-crc32: 0.2.13 + minimist: 1.2.8 + sander: 0.5.1 + dev: true + + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + + /spawn-command@0.0.2: + resolution: {integrity: sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==} + dev: true + + /sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + dev: true + + /streamsearch@1.1.0: + resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==} + engines: {node: '>=10.0.0'} + dev: true + + /string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + dev: true + + /strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + dependencies: + ansi-regex: 5.0.1 + dev: true + + /strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + dependencies: + min-indent: 1.0.1 + dev: true + + /strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + dev: true + + /supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + dependencies: + has-flag: 4.0.0 + dev: true + + /supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + dev: true + + /svelte-check@3.1.4(svelte@3.58.0): + resolution: {integrity: sha512-25Lb46ZS4IK/XpBMe4IBMrtYf23V8alqBX+szXoccb7uM0D2Wqq5rMRzYBONZnFVuU1bQG3R50lyIT5eRewv2g==} + hasBin: true + peerDependencies: + svelte: ^3.55.0 + dependencies: + '@jridgewell/trace-mapping': 0.3.19 + chokidar: 3.5.3 + fast-glob: 3.3.1 + import-fresh: 3.3.0 + picocolors: 1.0.0 + sade: 1.8.1 + svelte: 3.58.0 + svelte-preprocess: 5.0.4(svelte@3.58.0)(typescript@4.9.5) + typescript: 4.9.5 + transitivePeerDependencies: + - '@babel/core' + - coffeescript + - less + - postcss + - postcss-load-config + - pug + - sass + - stylus + - sugarss + dev: true + + /svelte-hcaptcha@0.1.1: + resolution: {integrity: sha512-iFF3HwfrCRciJnDs4Y9/rpP/BM2U/5zt+vh+9d4tALPAHVkcANiJIKqYuS835pIaTm6gt+xOzjfFI3cgiRI29A==} + dev: true + + /svelte-hmr@0.15.3(svelte@3.58.0): + resolution: {integrity: sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==} + engines: {node: ^12.20 || ^14.13.1 || >= 16} + peerDependencies: + svelte: ^3.19.0 || ^4.0.0 + dependencies: + svelte: 3.58.0 + dev: true + + /svelte-preprocess@5.0.4(svelte@3.58.0)(typescript@4.9.5): + resolution: {integrity: sha512-ABia2QegosxOGsVlsSBJvoWeXy1wUKSfF7SWJdTjLAbx/Y3SrVevvvbFNQqrSJw89+lNSsM58SipmZJ5SRi5iw==} + engines: {node: '>= 14.10.0'} + requiresBuild: true + peerDependencies: + '@babel/core': ^7.10.2 + coffeescript: ^2.5.1 + less: ^3.11.3 || ^4.0.0 + postcss: ^7 || ^8 + postcss-load-config: ^2.1.0 || ^3.0.0 || ^4.0.0 + pug: ^3.0.0 + sass: ^1.26.8 + stylus: ^0.55.0 + sugarss: ^2.0.0 || ^3.0.0 || ^4.0.0 + svelte: ^3.23.0 || ^4.0.0-next.0 || ^4.0.0 + typescript: '>=3.9.5 || ^4.0.0 || ^5.0.0' + peerDependenciesMeta: + '@babel/core': + optional: true + coffeescript: + optional: true + less: + optional: true + postcss: + optional: true + postcss-load-config: + optional: true + pug: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + typescript: + optional: true + dependencies: + '@types/pug': 2.0.6 + detect-indent: 6.1.0 + magic-string: 0.27.0 + sorcery: 0.11.0 + strip-indent: 3.0.0 + svelte: 3.58.0 + typescript: 4.9.5 + dev: true + + /svelte@3.58.0: + resolution: {integrity: sha512-brIBNNB76mXFmU/Kerm4wFnkskBbluBDCjx/8TcpYRb298Yh2dztS2kQ6bhtjMcvUhd5ynClfwpz5h2gnzdQ1A==} + engines: {node: '>= 8'} + dev: true + + /sveltestrap@5.10.0(svelte@3.58.0): + resolution: {integrity: sha512-k6Ob+6G2AMYvBidXHBKM9W28fJqFHbmosqCe/NC8pv6TV7K+v47Yw+zmnLWkjqCzzmjkSLkL48SrHZrlWc9mYQ==} + peerDependencies: + svelte: ^3.29.0 + dependencies: + '@popperjs/core': 2.11.7 + svelte: 3.58.0 + dev: true + + /text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + dev: true + + /tiny-glob@0.2.9: + resolution: {integrity: sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==} + dependencies: + globalyzer: 0.1.0 + globrex: 0.1.2 + dev: true + + /to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + dependencies: + is-number: 7.0.0 + dev: true + + /totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + dev: true + + /tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + dev: true + + /tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + /tslib@2.5.0: + resolution: {integrity: sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==} + dev: true + + /tsutils@3.21.0(typescript@4.9.5): + resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==} + engines: {node: '>= 6'} + peerDependencies: + typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta' + dependencies: + tslib: 1.14.1 + typescript: 4.9.5 + dev: true + + /type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + dependencies: + prelude-ls: 1.2.1 + dev: true + + /type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + dev: true + + /typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + dev: true + + /uc.micro@1.0.6: + resolution: {integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==} + + /undici@5.21.0: + resolution: {integrity: sha512-HOjK8l6a57b2ZGXOcUsI5NLfoTrfmbOl90ixJDl0AEFG4wgHNDQxtZy15/ZQp7HhjkpaGlp/eneMgtsu1dIlUA==} + engines: {node: '>=12.18'} + dependencies: + busboy: 1.6.0 + dev: true + + /uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + dependencies: + punycode: 2.3.0 + dev: true + + /vite-plugin-markdown@2.1.0(vite@4.2.1): + resolution: {integrity: sha512-eWLlrWzYZXEX3/HaXZo/KLjRpO72IUhbgaoFrbwB07ueXi6QfwqrgdZQfUcXTSofJCkN7GhErMC1K1RTAE0gGQ==} + peerDependencies: + vite: ^2.0.0 || ^3.0.0 + dependencies: + front-matter: 4.0.2 + htmlparser2: 6.1.0 + markdown-it: 12.3.2 + vite: 4.2.1(@types/node@18.15.11) + dev: true + + /vite@4.2.1(@types/node@18.15.11): + resolution: {integrity: sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 18.15.11 + esbuild: 0.17.19 + postcss: 8.4.29 + resolve: 1.22.4 + rollup: 3.29.0 + optionalDependencies: + fsevents: 2.3.3 + dev: true + + /vitefu@0.2.4(vite@4.2.1): + resolution: {integrity: sha512-fanAXjSaf9xXtOOeno8wZXIhgia+CZury481LsDaV++lSvcU2R9Ch2bPh3PYFyoHW+w9LqAeYRISVQjUIew14g==} + peerDependencies: + vite: ^3.0.0 || ^4.0.0 + peerDependenciesMeta: + vite: + optional: true + dependencies: + vite: 4.2.1(@types/node@18.15.11) + dev: true + + /which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + dependencies: + isexe: 2.0.0 + dev: true + + /wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + dev: true + + /wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + dev: true + + /y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + dev: true + + /yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + dev: true + + /yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + dev: true + + /yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + dependencies: + cliui: 8.0.1 + escalade: 3.1.1 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + dev: true + + /yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + dev: true diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..2bac25d --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +packages: + - docs + - frontend From 04db0507ba2b65585fcb4d3bd0d3feb294a0fab0 Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 7 Sep 2023 16:53:58 +0200 Subject: [PATCH 5/7] add snowflake support to report routes --- backend/routes/v1/mod/create_report.go | 67 ++++++++++++++++++-------- 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/backend/routes/v1/mod/create_report.go b/backend/routes/v1/mod/create_report.go index c9b6377..30969a4 100644 --- a/backend/routes/v1/mod/create_report.go +++ b/backend/routes/v1/mod/create_report.go @@ -3,6 +3,7 @@ package mod import ( "net/http" + "codeberg.org/pronounscc/pronouns.cc/backend/common" "codeberg.org/pronounscc/pronouns.cc/backend/db" "codeberg.org/pronounscc/pronouns.cc/backend/log" "codeberg.org/pronounscc/pronouns.cc/backend/server" @@ -18,7 +19,7 @@ type CreateReportRequest struct { Reason string `json:"reason"` } -func (s *Server) createUserReport(w http.ResponseWriter, r *http.Request) error { +func (s *Server) createUserReport(w http.ResponseWriter, r *http.Request) (err error) { ctx := r.Context() claims, _ := server.ClaimsFromContext(ctx) @@ -26,19 +27,32 @@ func (s *Server) createUserReport(w http.ResponseWriter, r *http.Request) error return server.APIError{Code: server.ErrMissingPermissions, Details: "This token is read-only"} } - userID, err := xid.FromString(chi.URLParam(r, "id")) - if err != nil { - return server.APIError{Code: server.ErrBadRequest, Details: "Invalid user ID"} - } + var u db.User + if id, err := xid.FromString(chi.URLParam(r, "id")); err == nil { + u, err = s.DB.User(ctx, id) + if err != nil { + if err == db.ErrUserNotFound { + return server.APIError{Code: server.ErrUserNotFound} + } - u, err := s.DB.User(ctx, userID) - if err != nil { - if err == db.ErrUserNotFound { + log.Errorf("getting user %v: %v", id, err) + return errors.Wrap(err, "getting user") + } + } else { + id, err := common.ParseSnowflake(chi.URLParam(r, "id")) + if err != nil { return server.APIError{Code: server.ErrUserNotFound} } - log.Errorf("getting user %v: %v", userID, err) - return errors.Wrap(err, "getting user") + u, err = s.DB.UserBySnowflake(ctx, common.UserID(id)) + if err != nil { + if err == db.ErrUserNotFound { + return server.APIError{Code: server.ErrUserNotFound} + } + + log.Errorf("getting user %v: %v", id, err) + return errors.Wrap(err, "getting user") + } } if u.DeletedAt != nil { @@ -73,19 +87,32 @@ func (s *Server) createMemberReport(w http.ResponseWriter, r *http.Request) erro return server.APIError{Code: server.ErrMissingPermissions, Details: "This token is read-only"} } - memberID, err := xid.FromString(chi.URLParam(r, "id")) - if err != nil { - return server.APIError{Code: server.ErrBadRequest, Details: "Invalid member ID"} - } + var m db.Member + if id, err := xid.FromString(chi.URLParam(r, "id")); err == nil { + m, err = s.DB.Member(ctx, id) + if err != nil { + if err == db.ErrMemberNotFound { + return server.APIError{Code: server.ErrMemberNotFound} + } - m, err := s.DB.Member(ctx, memberID) - if err != nil { - if err == db.ErrMemberNotFound { - return server.APIError{Code: server.ErrMemberNotFound} + log.Errorf("getting user %v: %v", id, err) + return errors.Wrap(err, "getting user") + } + } else { + id, err := common.ParseSnowflake(chi.URLParam(r, "id")) + if err != nil { + return server.APIError{Code: server.ErrUserNotFound} } - log.Errorf("getting member %v: %v", memberID, err) - return errors.Wrap(err, "getting member") + m, err = s.DB.MemberBySnowflake(ctx, common.MemberID(id)) + if err != nil { + if err == db.ErrMemberNotFound { + return server.APIError{Code: server.ErrMemberNotFound} + } + + log.Errorf("getting user %v: %v", id, err) + return errors.Wrap(err, "getting user") + } } u, err := s.DB.User(ctx, m.UserID) From 41f5d46891f2e3d6d8d0cf745181a769f5eddbaf Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 7 Sep 2023 17:01:31 +0200 Subject: [PATCH 6/7] add snowflake support to member reroll route --- backend/routes/v1/member/patch_member.go | 40 ++++++++++++++++-------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/backend/routes/v1/member/patch_member.go b/backend/routes/v1/member/patch_member.go index 5391026..956eb13 100644 --- a/backend/routes/v1/member/patch_member.go +++ b/backend/routes/v1/member/patch_member.go @@ -339,7 +339,7 @@ func (s *Server) patchMember(w http.ResponseWriter, r *http.Request) error { return nil } -func (s *Server) rerollMemberSID(w http.ResponseWriter, r *http.Request) error { +func (s *Server) rerollMemberSID(w http.ResponseWriter, r *http.Request) (err error) { ctx := r.Context() claims, _ := server.ClaimsFromContext(ctx) @@ -348,9 +348,32 @@ func (s *Server) rerollMemberSID(w http.ResponseWriter, r *http.Request) error { return server.APIError{Code: server.ErrMissingPermissions, Details: "This token is read-only"} } - id, err := xid.FromString(chi.URLParam(r, "memberRef")) - if err != nil { - return server.APIError{Code: server.ErrMemberNotFound} + var m db.Member + if id, err := xid.FromString(chi.URLParam(r, "memberRef")); err == nil { + m, err = s.DB.Member(ctx, id) + if err != nil { + if err == db.ErrMemberNotFound { + return server.APIError{Code: server.ErrMemberNotFound} + } + + log.Errorf("getting user %v: %v", id, err) + return errors.Wrap(err, "getting user") + } + } else { + id, err := common.ParseSnowflake(chi.URLParam(r, "memberRef")) + if err != nil { + return server.APIError{Code: server.ErrMemberNotFound} + } + + m, err = s.DB.MemberBySnowflake(ctx, common.MemberID(id)) + if err != nil { + if err == db.ErrMemberNotFound { + return server.APIError{Code: server.ErrMemberNotFound} + } + + log.Errorf("getting user %v: %v", id, err) + return errors.Wrap(err, "getting user") + } } u, err := s.DB.User(ctx, claims.UserID) @@ -358,15 +381,6 @@ func (s *Server) rerollMemberSID(w http.ResponseWriter, r *http.Request) error { return errors.Wrap(err, "getting user") } - m, err := s.DB.Member(ctx, id) - if err != nil { - if err == db.ErrMemberNotFound { - return server.APIError{Code: server.ErrMemberNotFound} - } - - return errors.Wrap(err, "getting member") - } - if m.UserID != claims.UserID { return server.APIError{Code: server.ErrNotOwnMember} } From b6cc5bb130bdcd923ab89a1a92cca0bca39eac63 Mon Sep 17 00:00:00 2001 From: sam Date: Thu, 7 Sep 2023 17:04:18 +0200 Subject: [PATCH 7/7] change frontend API calls to use snowflake IDs --- frontend/src/lib/api/entities.ts | 4 ++++ frontend/src/routes/@[username]/+page.svelte | 2 +- frontend/src/routes/@[username]/[memberName]/+page.svelte | 2 +- .../src/routes/@[username]/[memberName]/edit/+layout.svelte | 4 ++-- .../routes/@[username]/[memberName]/edit/other/+page.svelte | 2 +- frontend/src/routes/reports/+page.ts | 3 +-- frontend/src/routes/settings/+page.svelte | 6 +++++- frontend/src/routes/settings/flags/+page.svelte | 2 +- frontend/src/routes/settings/flags/Flag.svelte | 4 ++-- 9 files changed, 18 insertions(+), 11 deletions(-) diff --git a/frontend/src/lib/api/entities.ts b/frontend/src/lib/api/entities.ts index b635c8b..1b3a776 100644 --- a/frontend/src/lib/api/entities.ts +++ b/frontend/src/lib/api/entities.ts @@ -7,6 +7,7 @@ export const MAX_FLAGS = 500; export interface User { id: string; + id_new: string; sid: string; name: string; display_name: string | null; @@ -79,6 +80,7 @@ export interface Pronoun { export interface PartialMember { id: string; + id_new: string; sid: string; name: string; display_name: string | null; @@ -99,6 +101,7 @@ export interface Member extends PartialMember { export interface MemberPartialUser { id: string; + id_new: string; name: string; display_name: string | null; avatar: string | null; @@ -107,6 +110,7 @@ export interface MemberPartialUser { export interface PrideFlag { id: string; + id_new: string; hash: string; name: string; description: string | null; diff --git a/frontend/src/routes/@[username]/+page.svelte b/frontend/src/routes/@[username]/+page.svelte index 76da6de..56542fc 100644 --- a/frontend/src/routes/@[username]/+page.svelte +++ b/frontend/src/routes/@[username]/+page.svelte @@ -274,7 +274,7 @@ /> {/if} {#if $userStore && $userStore.id !== data.id} - + {/if} diff --git a/frontend/src/routes/@[username]/[memberName]/+page.svelte b/frontend/src/routes/@[username]/[memberName]/+page.svelte index 9089959..cef3271 100644 --- a/frontend/src/routes/@[username]/[memberName]/+page.svelte +++ b/frontend/src/routes/@[username]/[memberName]/+page.svelte @@ -170,7 +170,7 @@ /> {/if} {#if $userStore && $userStore.id !== data.user.id} - + {/if} diff --git a/frontend/src/routes/@[username]/[memberName]/edit/+layout.svelte b/frontend/src/routes/@[username]/[memberName]/edit/+layout.svelte index b5e98a3..df02160 100644 --- a/frontend/src/routes/@[username]/[memberName]/edit/+layout.svelte +++ b/frontend/src/routes/@[username]/[memberName]/edit/+layout.svelte @@ -34,7 +34,7 @@ const deleteMember = async () => { try { - await fastFetchClient(`/members/${data.member.id}`, "DELETE"); + await fastFetchClient(`/members/${data.member.id_new}`, "DELETE"); toggleDeleteOpen(); addToast({ @@ -68,7 +68,7 @@ }); try { - const resp = await apiFetchClient(`/members/${data.member.id}`, "PATCH", { + const resp = await apiFetchClient(`/members/${data.member.id_new}`, "PATCH", { name: $member.name, display_name: $member.display_name, avatar: $member.avatar, diff --git a/frontend/src/routes/@[username]/[memberName]/edit/other/+page.svelte b/frontend/src/routes/@[username]/[memberName]/edit/other/+page.svelte index ca29a26..9f3f157 100644 --- a/frontend/src/routes/@[username]/[memberName]/edit/other/+page.svelte +++ b/frontend/src/routes/@[username]/[memberName]/edit/other/+page.svelte @@ -25,7 +25,7 @@ const rerollSid = async () => { try { - const resp = await apiFetchClient(`/members/${data.member.id}/reroll`); + const resp = await apiFetchClient(`/members/${data.member.id_new}/reroll`); addToast({ header: "Success", body: "Rerolled short ID!" }); error = null; $member.sid = resp.sid; diff --git a/frontend/src/routes/reports/+page.ts b/frontend/src/routes/reports/+page.ts index ee1743e..fdeef02 100644 --- a/frontend/src/routes/reports/+page.ts +++ b/frontend/src/routes/reports/+page.ts @@ -1,6 +1,5 @@ -import { ErrorCode, type APIError, type Report } from "$lib/api/entities"; +import type { Report } from "$lib/api/entities"; import { apiFetchClient } from "$lib/api/fetch"; -import { error } from "@sveltejs/kit"; export const load = async ({ url }) => { const { searchParams } = url; diff --git a/frontend/src/routes/settings/+page.svelte b/frontend/src/routes/settings/+page.svelte index ee1f0a3..163698d 100644 --- a/frontend/src/routes/settings/+page.svelte +++ b/frontend/src/routes/settings/+page.svelte @@ -194,7 +194,11 @@ ID - {data.user.id} + {data.user.id_new} + + + xid (deprecated) + {data.user.id} Account created at diff --git a/frontend/src/routes/settings/flags/+page.svelte b/frontend/src/routes/settings/flags/+page.svelte index 4f69c8f..add8fa0 100644 --- a/frontend/src/routes/settings/flags/+page.svelte +++ b/frontend/src/routes/settings/flags/+page.svelte @@ -92,7 +92,7 @@ error = null; addToast({ header: "Deleted flag", body: "Successfully deleted flag!" }); - data.flags = data.flags.filter((entry) => entry.id !== id); + data.flags = data.flags.filter((entry) => entry.id_new !== id); } catch (e) { error = e as APIError; } diff --git a/frontend/src/routes/settings/flags/Flag.svelte b/frontend/src/routes/settings/flags/Flag.svelte index eab228f..a7ae53d 100644 --- a/frontend/src/routes/settings/flags/Flag.svelte +++ b/frontend/src/routes/settings/flags/Flag.svelte @@ -20,7 +20,7 @@ const updateFlag = async () => { try { - const resp = await apiFetchClient(`/users/@me/flags/${flag.id}`, "PATCH", { + const resp = await apiFetchClient(`/users/@me/flags/${flag.id_new}`, "PATCH", { name, description: description || null, }); @@ -65,7 +65,7 @@ Are you sure you want to delete the {flag.name} flag? This cannot be undone! - +