Compare changes

Choose any two refs to compare.

+4263 -4767
+2
api/tangled/actorprofile.go
··· 18 // RECORDTYPE: ActorProfile 19 type ActorProfile struct { 20 LexiconTypeID string `json:"$type,const=sh.tangled.actor.profile" cborgen:"$type,const=sh.tangled.actor.profile"` 21 // bluesky: Include link to this account on Bluesky. 22 Bluesky bool `json:"bluesky" cborgen:"bluesky"` 23 // description: Free-form profile description text.
··· 18 // RECORDTYPE: ActorProfile 19 type ActorProfile struct { 20 LexiconTypeID string `json:"$type,const=sh.tangled.actor.profile" cborgen:"$type,const=sh.tangled.actor.profile"` 21 + // avatar: Small image to be displayed next to posts from account. AKA, 'profile picture' 22 + Avatar *util.LexBlob `json:"avatar,omitempty" cborgen:"avatar,omitempty"` 23 // bluesky: Include link to this account on Bluesky. 24 Bluesky bool `json:"bluesky" cborgen:"bluesky"` 25 // description: Free-form profile description text.
+44 -1
api/tangled/cbor_gen.go
··· 26 } 27 28 cw := cbg.NewCborWriter(w) 29 - fieldCount := 8 30 31 if t.Description == nil { 32 fieldCount-- ··· 144 return err 145 } 146 147 } 148 } 149 ··· 428 } 429 430 } 431 } 432 // t.Bluesky (bool) (bool) 433 case "bluesky":
··· 26 } 27 28 cw := cbg.NewCborWriter(w) 29 + fieldCount := 9 30 + 31 + if t.Avatar == nil { 32 + fieldCount-- 33 + } 34 35 if t.Description == nil { 36 fieldCount-- ··· 148 return err 149 } 150 151 + } 152 + } 153 + 154 + // t.Avatar (util.LexBlob) (struct) 155 + if t.Avatar != nil { 156 + 157 + if len("avatar") > 1000000 { 158 + return xerrors.Errorf("Value in field \"avatar\" was too long") 159 + } 160 + 161 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("avatar"))); err != nil { 162 + return err 163 + } 164 + if _, err := cw.WriteString(string("avatar")); err != nil { 165 + return err 166 + } 167 + 168 + if err := t.Avatar.MarshalCBOR(cw); err != nil { 169 + return err 170 } 171 } 172 ··· 451 } 452 453 } 454 + } 455 + // t.Avatar (util.LexBlob) (struct) 456 + case "avatar": 457 + 458 + { 459 + 460 + b, err := cr.ReadByte() 461 + if err != nil { 462 + return err 463 + } 464 + if b != cbg.CborNull[0] { 465 + if err := cr.UnreadByte(); err != nil { 466 + return err 467 + } 468 + t.Avatar = new(util.LexBlob) 469 + if err := t.Avatar.UnmarshalCBOR(cr); err != nil { 470 + return xerrors.Errorf("unmarshaling t.Avatar pointer: %w", err) 471 + } 472 + } 473 + 474 } 475 // t.Bluesky (bool) (bool) 476 case "bluesky":
-39
api/tangled/gitkeepCommit.go
··· 1 - // Code generated by cmd/lexgen (see Makefile's lexgen); DO NOT EDIT. 2 - 3 - package tangled 4 - 5 - // schema: sh.tangled.git.keepCommit 6 - 7 - import ( 8 - "context" 9 - 10 - "github.com/bluesky-social/indigo/lex/util" 11 - ) 12 - 13 - const ( 14 - GitKeepCommitNSID = "sh.tangled.git.keepCommit" 15 - ) 16 - 17 - // GitKeepCommit_Input is the input argument to a sh.tangled.git.keepCommit call. 18 - type GitKeepCommit_Input struct { 19 - // ref: ref to keep 20 - Ref string `json:"ref" cborgen:"ref"` 21 - // repo: AT-URI of the repository 22 - Repo string `json:"repo" cborgen:"repo"` 23 - } 24 - 25 - // GitKeepCommit_Output is the output of a sh.tangled.git.keepCommit call. 26 - type GitKeepCommit_Output struct { 27 - // commitId: Keeped commit hash 28 - CommitId string `json:"commitId" cborgen:"commitId"` 29 - } 30 - 31 - // GitKeepCommit calls the XRPC method "sh.tangled.git.keepCommit". 32 - func GitKeepCommit(ctx context.Context, c util.LexClient, input *GitKeepCommit_Input) (*GitKeepCommit_Output, error) { 33 - var out GitKeepCommit_Output 34 - if err := c.LexDo(ctx, util.Procedure, "application/json", "sh.tangled.git.keepCommit", nil, input, &out); err != nil { 35 - return nil, err 36 - } 37 - 38 - return &out, nil 39 - }
···
+3 -2
appview/db/artifact.go
··· 8 "github.com/go-git/go-git/v5/plumbing" 9 "github.com/ipfs/go-cid" 10 "tangled.org/core/appview/models" 11 ) 12 13 func AddArtifact(e Execer, artifact models.Artifact) error { ··· 37 return err 38 } 39 40 - func GetArtifact(e Execer, filters ...filter) ([]models.Artifact, error) { 41 var artifacts []models.Artifact 42 43 var conditions []string ··· 109 return artifacts, nil 110 } 111 112 - func DeleteArtifact(e Execer, filters ...filter) error { 113 var conditions []string 114 var args []any 115 for _, filter := range filters {
··· 8 "github.com/go-git/go-git/v5/plumbing" 9 "github.com/ipfs/go-cid" 10 "tangled.org/core/appview/models" 11 + "tangled.org/core/orm" 12 ) 13 14 func AddArtifact(e Execer, artifact models.Artifact) error { ··· 38 return err 39 } 40 41 + func GetArtifact(e Execer, filters ...orm.Filter) ([]models.Artifact, error) { 42 var artifacts []models.Artifact 43 44 var conditions []string ··· 110 return artifacts, nil 111 } 112 113 + func DeleteArtifact(e Execer, filters ...orm.Filter) error { 114 var conditions []string 115 var args []any 116 for _, filter := range filters {
+4 -3
appview/db/collaborators.go
··· 6 "time" 7 8 "tangled.org/core/appview/models" 9 ) 10 11 func AddCollaborator(e Execer, c models.Collaborator) error { ··· 16 return err 17 } 18 19 - func DeleteCollaborator(e Execer, filters ...filter) error { 20 var conditions []string 21 var args []any 22 for _, filter := range filters { ··· 58 return nil, nil 59 } 60 61 - return GetRepos(e, 0, FilterIn("at_uri", repoAts)) 62 } 63 64 - func GetCollaborators(e Execer, filters ...filter) ([]models.Collaborator, error) { 65 var collaborators []models.Collaborator 66 var conditions []string 67 var args []any
··· 6 "time" 7 8 "tangled.org/core/appview/models" 9 + "tangled.org/core/orm" 10 ) 11 12 func AddCollaborator(e Execer, c models.Collaborator) error { ··· 17 return err 18 } 19 20 + func DeleteCollaborator(e Execer, filters ...orm.Filter) error { 21 var conditions []string 22 var args []any 23 for _, filter := range filters { ··· 59 return nil, nil 60 } 61 62 + return GetRepos(e, 0, orm.FilterIn("at_uri", repoAts)) 63 } 64 65 + func GetCollaborators(e Execer, filters ...orm.Filter) ([]models.Collaborator, error) { 66 var collaborators []models.Collaborator 67 var conditions []string 68 var args []any
+33 -138
appview/db/db.go
··· 3 import ( 4 "context" 5 "database/sql" 6 - "fmt" 7 "log/slog" 8 - "reflect" 9 "strings" 10 11 _ "github.com/mattn/go-sqlite3" 12 "tangled.org/core/log" 13 ) 14 15 type DB struct { ··· 261 did text not null, 262 263 -- data 264 description text not null, 265 include_bluesky integer not null default 0, 266 location text, ··· 584 } 585 586 // run migrations 587 - runMigration(conn, logger, "add-description-to-repos", func(tx *sql.Tx) error { 588 tx.Exec(` 589 alter table repos add column description text check (length(description) <= 200); 590 `) 591 return nil 592 }) 593 594 - runMigration(conn, logger, "add-rkey-to-pubkeys", func(tx *sql.Tx) error { 595 // add unconstrained column 596 _, err := tx.Exec(` 597 alter table public_keys ··· 614 return nil 615 }) 616 617 - runMigration(conn, logger, "add-rkey-to-comments", func(tx *sql.Tx) error { 618 _, err := tx.Exec(` 619 alter table comments drop column comment_at; 620 alter table comments add column rkey text; ··· 622 return err 623 }) 624 625 - runMigration(conn, logger, "add-deleted-and-edited-to-issue-comments", func(tx *sql.Tx) error { 626 _, err := tx.Exec(` 627 alter table comments add column deleted text; -- timestamp 628 alter table comments add column edited text; -- timestamp ··· 630 return err 631 }) 632 633 - runMigration(conn, logger, "add-source-info-to-pulls-and-submissions", func(tx *sql.Tx) error { 634 _, err := tx.Exec(` 635 alter table pulls add column source_branch text; 636 alter table pulls add column source_repo_at text; ··· 639 return err 640 }) 641 642 - runMigration(conn, logger, "add-source-to-repos", func(tx *sql.Tx) error { 643 _, err := tx.Exec(` 644 alter table repos add column source text; 645 `) ··· 651 // 652 // [0]: https://sqlite.org/pragma.html#pragma_foreign_keys 653 conn.ExecContext(ctx, "pragma foreign_keys = off;") 654 - runMigration(conn, logger, "recreate-pulls-column-for-stacking-support", func(tx *sql.Tx) error { 655 _, err := tx.Exec(` 656 create table pulls_new ( 657 -- identifiers ··· 708 }) 709 conn.ExecContext(ctx, "pragma foreign_keys = on;") 710 711 - runMigration(conn, logger, "add-spindle-to-repos", func(tx *sql.Tx) error { 712 tx.Exec(` 713 alter table repos add column spindle text; 714 `) ··· 718 // drop all knot secrets, add unique constraint to knots 719 // 720 // knots will henceforth use service auth for signed requests 721 - runMigration(conn, logger, "no-more-secrets", func(tx *sql.Tx) error { 722 _, err := tx.Exec(` 723 create table registrations_new ( 724 id integer primary key autoincrement, ··· 741 }) 742 743 // recreate and add rkey + created columns with default constraint 744 - runMigration(conn, logger, "rework-collaborators-table", func(tx *sql.Tx) error { 745 // create new table 746 // - repo_at instead of repo integer 747 // - rkey field ··· 795 return err 796 }) 797 798 - runMigration(conn, logger, "add-rkey-to-issues", func(tx *sql.Tx) error { 799 _, err := tx.Exec(` 800 alter table issues add column rkey text not null default ''; 801 ··· 807 }) 808 809 // repurpose the read-only column to "needs-upgrade" 810 - runMigration(conn, logger, "rename-registrations-read-only-to-needs-upgrade", func(tx *sql.Tx) error { 811 _, err := tx.Exec(` 812 alter table registrations rename column read_only to needs_upgrade; 813 `) ··· 815 }) 816 817 // require all knots to upgrade after the release of total xrpc 818 - runMigration(conn, logger, "migrate-knots-to-total-xrpc", func(tx *sql.Tx) error { 819 _, err := tx.Exec(` 820 update registrations set needs_upgrade = 1; 821 `) ··· 823 }) 824 825 // require all knots to upgrade after the release of total xrpc 826 - runMigration(conn, logger, "migrate-spindles-to-xrpc-owner", func(tx *sql.Tx) error { 827 _, err := tx.Exec(` 828 alter table spindles add column needs_upgrade integer not null default 0; 829 `) ··· 841 // 842 // disable foreign-keys for the next migration 843 conn.ExecContext(ctx, "pragma foreign_keys = off;") 844 - runMigration(conn, logger, "remove-issue-at-from-issues", func(tx *sql.Tx) error { 845 _, err := tx.Exec(` 846 create table if not exists issues_new ( 847 -- identifiers ··· 911 // - new columns 912 // * column "reply_to" which can be any other comment 913 // * column "at-uri" which is a generated column 914 - runMigration(conn, logger, "rework-issue-comments", func(tx *sql.Tx) error { 915 _, err := tx.Exec(` 916 create table if not exists issue_comments ( 917 -- identifiers ··· 971 // 972 // disable foreign-keys for the next migration 973 conn.ExecContext(ctx, "pragma foreign_keys = off;") 974 - runMigration(conn, logger, "add-at-uri-to-pulls", func(tx *sql.Tx) error { 975 _, err := tx.Exec(` 976 create table if not exists pulls_new ( 977 -- identifiers ··· 1052 // 1053 // disable foreign-keys for the next migration 1054 conn.ExecContext(ctx, "pragma foreign_keys = off;") 1055 - runMigration(conn, logger, "remove-repo-at-pull-id-from-pull-submissions", func(tx *sql.Tx) error { 1056 _, err := tx.Exec(` 1057 create table if not exists pull_submissions_new ( 1058 -- identifiers ··· 1079 // transfer data, constructing pull_at from pulls table 1080 _, err = tx.Exec(` 1081 insert into pull_submissions_new (id, pull_at, round_number, patch, created) 1082 - select 1083 ps.id, 1084 'at://' || p.owner_did || '/sh.tangled.repo.pull/' || p.rkey, 1085 ps.round_number, ··· 1106 1107 // knots may report the combined patch for a comparison, we can store that on the appview side 1108 // (but not on the pds record), because calculating the combined patch requires a git index 1109 - runMigration(conn, logger, "add-combined-column-submissions", func(tx *sql.Tx) error { 1110 _, err := tx.Exec(` 1111 alter table pull_submissions add column combined text; 1112 `) 1113 return err 1114 }) 1115 1116 - runMigration(conn, logger, "add-pronouns-profile", func(tx *sql.Tx) error { 1117 _, err := tx.Exec(` 1118 alter table profile add column pronouns text; 1119 `) 1120 return err 1121 }) 1122 1123 - runMigration(conn, logger, "add-meta-column-repos", func(tx *sql.Tx) error { 1124 _, err := tx.Exec(` 1125 alter table repos add column website text; 1126 alter table repos add column topics text; ··· 1128 return err 1129 }) 1130 1131 - runMigration(conn, logger, "add-usermentioned-preference", func(tx *sql.Tx) error { 1132 _, err := tx.Exec(` 1133 alter table notification_preferences add column user_mentioned integer not null default 1; 1134 `) ··· 1136 }) 1137 1138 // remove the foreign key constraints from stars. 1139 - runMigration(conn, logger, "generalize-stars-subject", func(tx *sql.Tx) error { 1140 _, err := tx.Exec(` 1141 create table stars_new ( 1142 id integer primary key autoincrement, ··· 1174 return err 1175 }) 1176 1177 return &DB{ 1178 db, 1179 logger, 1180 }, nil 1181 } 1182 1183 - type migrationFn = func(*sql.Tx) error 1184 - 1185 - func runMigration(c *sql.Conn, logger *slog.Logger, name string, migrationFn migrationFn) error { 1186 - logger = logger.With("migration", name) 1187 - 1188 - tx, err := c.BeginTx(context.Background(), nil) 1189 - if err != nil { 1190 - return err 1191 - } 1192 - defer tx.Rollback() 1193 - 1194 - var exists bool 1195 - err = tx.QueryRow("select exists (select 1 from migrations where name = ?)", name).Scan(&exists) 1196 - if err != nil { 1197 - return err 1198 - } 1199 - 1200 - if !exists { 1201 - // run migration 1202 - err = migrationFn(tx) 1203 - if err != nil { 1204 - logger.Error("failed to run migration", "err", err) 1205 - return err 1206 - } 1207 - 1208 - // mark migration as complete 1209 - _, err = tx.Exec("insert into migrations (name) values (?)", name) 1210 - if err != nil { 1211 - logger.Error("failed to mark migration as complete", "err", err) 1212 - return err 1213 - } 1214 - 1215 - // commit the transaction 1216 - if err := tx.Commit(); err != nil { 1217 - return err 1218 - } 1219 - 1220 - logger.Info("migration applied successfully") 1221 - } else { 1222 - logger.Warn("skipped migration, already applied") 1223 - } 1224 - 1225 - return nil 1226 - } 1227 - 1228 func (d *DB) Close() error { 1229 return d.DB.Close() 1230 } 1231 - 1232 - type filter struct { 1233 - key string 1234 - arg any 1235 - cmp string 1236 - } 1237 - 1238 - func newFilter(key, cmp string, arg any) filter { 1239 - return filter{ 1240 - key: key, 1241 - arg: arg, 1242 - cmp: cmp, 1243 - } 1244 - } 1245 - 1246 - func FilterEq(key string, arg any) filter { return newFilter(key, "=", arg) } 1247 - func FilterNotEq(key string, arg any) filter { return newFilter(key, "<>", arg) } 1248 - func FilterGte(key string, arg any) filter { return newFilter(key, ">=", arg) } 1249 - func FilterLte(key string, arg any) filter { return newFilter(key, "<=", arg) } 1250 - func FilterIs(key string, arg any) filter { return newFilter(key, "is", arg) } 1251 - func FilterIsNot(key string, arg any) filter { return newFilter(key, "is not", arg) } 1252 - func FilterIn(key string, arg any) filter { return newFilter(key, "in", arg) } 1253 - func FilterLike(key string, arg any) filter { return newFilter(key, "like", arg) } 1254 - func FilterNotLike(key string, arg any) filter { return newFilter(key, "not like", arg) } 1255 - func FilterContains(key string, arg any) filter { 1256 - return newFilter(key, "like", fmt.Sprintf("%%%v%%", arg)) 1257 - } 1258 - 1259 - func (f filter) Condition() string { 1260 - rv := reflect.ValueOf(f.arg) 1261 - kind := rv.Kind() 1262 - 1263 - // if we have `FilterIn(k, [1, 2, 3])`, compile it down to `k in (?, ?, ?)` 1264 - if (kind == reflect.Slice && rv.Type().Elem().Kind() != reflect.Uint8) || kind == reflect.Array { 1265 - if rv.Len() == 0 { 1266 - // always false 1267 - return "1 = 0" 1268 - } 1269 - 1270 - placeholders := make([]string, rv.Len()) 1271 - for i := range placeholders { 1272 - placeholders[i] = "?" 1273 - } 1274 - 1275 - return fmt.Sprintf("%s %s (%s)", f.key, f.cmp, strings.Join(placeholders, ", ")) 1276 - } 1277 - 1278 - return fmt.Sprintf("%s %s ?", f.key, f.cmp) 1279 - } 1280 - 1281 - func (f filter) Arg() []any { 1282 - rv := reflect.ValueOf(f.arg) 1283 - kind := rv.Kind() 1284 - if (kind == reflect.Slice && rv.Type().Elem().Kind() != reflect.Uint8) || kind == reflect.Array { 1285 - if rv.Len() == 0 { 1286 - return nil 1287 - } 1288 - 1289 - out := make([]any, rv.Len()) 1290 - for i := range rv.Len() { 1291 - out[i] = rv.Index(i).Interface() 1292 - } 1293 - return out 1294 - } 1295 - 1296 - return []any{f.arg} 1297 - }
··· 3 import ( 4 "context" 5 "database/sql" 6 "log/slog" 7 "strings" 8 9 _ "github.com/mattn/go-sqlite3" 10 "tangled.org/core/log" 11 + "tangled.org/core/orm" 12 ) 13 14 type DB struct { ··· 260 did text not null, 261 262 -- data 263 + avatar text, 264 description text not null, 265 include_bluesky integer not null default 0, 266 location text, ··· 584 } 585 586 // run migrations 587 + orm.RunMigration(conn, logger, "add-description-to-repos", func(tx *sql.Tx) error { 588 tx.Exec(` 589 alter table repos add column description text check (length(description) <= 200); 590 `) 591 return nil 592 }) 593 594 + orm.RunMigration(conn, logger, "add-rkey-to-pubkeys", func(tx *sql.Tx) error { 595 // add unconstrained column 596 _, err := tx.Exec(` 597 alter table public_keys ··· 614 return nil 615 }) 616 617 + orm.RunMigration(conn, logger, "add-rkey-to-comments", func(tx *sql.Tx) error { 618 _, err := tx.Exec(` 619 alter table comments drop column comment_at; 620 alter table comments add column rkey text; ··· 622 return err 623 }) 624 625 + orm.RunMigration(conn, logger, "add-deleted-and-edited-to-issue-comments", func(tx *sql.Tx) error { 626 _, err := tx.Exec(` 627 alter table comments add column deleted text; -- timestamp 628 alter table comments add column edited text; -- timestamp ··· 630 return err 631 }) 632 633 + orm.RunMigration(conn, logger, "add-source-info-to-pulls-and-submissions", func(tx *sql.Tx) error { 634 _, err := tx.Exec(` 635 alter table pulls add column source_branch text; 636 alter table pulls add column source_repo_at text; ··· 639 return err 640 }) 641 642 + orm.RunMigration(conn, logger, "add-source-to-repos", func(tx *sql.Tx) error { 643 _, err := tx.Exec(` 644 alter table repos add column source text; 645 `) ··· 651 // 652 // [0]: https://sqlite.org/pragma.html#pragma_foreign_keys 653 conn.ExecContext(ctx, "pragma foreign_keys = off;") 654 + orm.RunMigration(conn, logger, "recreate-pulls-column-for-stacking-support", func(tx *sql.Tx) error { 655 _, err := tx.Exec(` 656 create table pulls_new ( 657 -- identifiers ··· 708 }) 709 conn.ExecContext(ctx, "pragma foreign_keys = on;") 710 711 + orm.RunMigration(conn, logger, "add-spindle-to-repos", func(tx *sql.Tx) error { 712 tx.Exec(` 713 alter table repos add column spindle text; 714 `) ··· 718 // drop all knot secrets, add unique constraint to knots 719 // 720 // knots will henceforth use service auth for signed requests 721 + orm.RunMigration(conn, logger, "no-more-secrets", func(tx *sql.Tx) error { 722 _, err := tx.Exec(` 723 create table registrations_new ( 724 id integer primary key autoincrement, ··· 741 }) 742 743 // recreate and add rkey + created columns with default constraint 744 + orm.RunMigration(conn, logger, "rework-collaborators-table", func(tx *sql.Tx) error { 745 // create new table 746 // - repo_at instead of repo integer 747 // - rkey field ··· 795 return err 796 }) 797 798 + orm.RunMigration(conn, logger, "add-rkey-to-issues", func(tx *sql.Tx) error { 799 _, err := tx.Exec(` 800 alter table issues add column rkey text not null default ''; 801 ··· 807 }) 808 809 // repurpose the read-only column to "needs-upgrade" 810 + orm.RunMigration(conn, logger, "rename-registrations-read-only-to-needs-upgrade", func(tx *sql.Tx) error { 811 _, err := tx.Exec(` 812 alter table registrations rename column read_only to needs_upgrade; 813 `) ··· 815 }) 816 817 // require all knots to upgrade after the release of total xrpc 818 + orm.RunMigration(conn, logger, "migrate-knots-to-total-xrpc", func(tx *sql.Tx) error { 819 _, err := tx.Exec(` 820 update registrations set needs_upgrade = 1; 821 `) ··· 823 }) 824 825 // require all knots to upgrade after the release of total xrpc 826 + orm.RunMigration(conn, logger, "migrate-spindles-to-xrpc-owner", func(tx *sql.Tx) error { 827 _, err := tx.Exec(` 828 alter table spindles add column needs_upgrade integer not null default 0; 829 `) ··· 841 // 842 // disable foreign-keys for the next migration 843 conn.ExecContext(ctx, "pragma foreign_keys = off;") 844 + orm.RunMigration(conn, logger, "remove-issue-at-from-issues", func(tx *sql.Tx) error { 845 _, err := tx.Exec(` 846 create table if not exists issues_new ( 847 -- identifiers ··· 911 // - new columns 912 // * column "reply_to" which can be any other comment 913 // * column "at-uri" which is a generated column 914 + orm.RunMigration(conn, logger, "rework-issue-comments", func(tx *sql.Tx) error { 915 _, err := tx.Exec(` 916 create table if not exists issue_comments ( 917 -- identifiers ··· 971 // 972 // disable foreign-keys for the next migration 973 conn.ExecContext(ctx, "pragma foreign_keys = off;") 974 + orm.RunMigration(conn, logger, "add-at-uri-to-pulls", func(tx *sql.Tx) error { 975 _, err := tx.Exec(` 976 create table if not exists pulls_new ( 977 -- identifiers ··· 1052 // 1053 // disable foreign-keys for the next migration 1054 conn.ExecContext(ctx, "pragma foreign_keys = off;") 1055 + orm.RunMigration(conn, logger, "remove-repo-at-pull-id-from-pull-submissions", func(tx *sql.Tx) error { 1056 _, err := tx.Exec(` 1057 create table if not exists pull_submissions_new ( 1058 -- identifiers ··· 1079 // transfer data, constructing pull_at from pulls table 1080 _, err = tx.Exec(` 1081 insert into pull_submissions_new (id, pull_at, round_number, patch, created) 1082 + select 1083 ps.id, 1084 'at://' || p.owner_did || '/sh.tangled.repo.pull/' || p.rkey, 1085 ps.round_number, ··· 1106 1107 // knots may report the combined patch for a comparison, we can store that on the appview side 1108 // (but not on the pds record), because calculating the combined patch requires a git index 1109 + orm.RunMigration(conn, logger, "add-combined-column-submissions", func(tx *sql.Tx) error { 1110 _, err := tx.Exec(` 1111 alter table pull_submissions add column combined text; 1112 `) 1113 return err 1114 }) 1115 1116 + orm.RunMigration(conn, logger, "add-pronouns-profile", func(tx *sql.Tx) error { 1117 _, err := tx.Exec(` 1118 alter table profile add column pronouns text; 1119 `) 1120 return err 1121 }) 1122 1123 + orm.RunMigration(conn, logger, "add-meta-column-repos", func(tx *sql.Tx) error { 1124 _, err := tx.Exec(` 1125 alter table repos add column website text; 1126 alter table repos add column topics text; ··· 1128 return err 1129 }) 1130 1131 + orm.RunMigration(conn, logger, "add-usermentioned-preference", func(tx *sql.Tx) error { 1132 _, err := tx.Exec(` 1133 alter table notification_preferences add column user_mentioned integer not null default 1; 1134 `) ··· 1136 }) 1137 1138 // remove the foreign key constraints from stars. 1139 + orm.RunMigration(conn, logger, "generalize-stars-subject", func(tx *sql.Tx) error { 1140 _, err := tx.Exec(` 1141 create table stars_new ( 1142 id integer primary key autoincrement, ··· 1174 return err 1175 }) 1176 1177 + orm.RunMigration(conn, logger, "add-avatar-to-profile", func(tx *sql.Tx) error { 1178 + _, err := tx.Exec(` 1179 + alter table profile add column avatar text; 1180 + `) 1181 + return err 1182 + }) 1183 + 1184 return &DB{ 1185 db, 1186 logger, 1187 }, nil 1188 } 1189 1190 func (d *DB) Close() error { 1191 return d.DB.Close() 1192 }
+4 -3
appview/db/follow.go
··· 7 "time" 8 9 "tangled.org/core/appview/models" 10 ) 11 12 func AddFollow(e Execer, follow *models.Follow) error { ··· 134 return result, nil 135 } 136 137 - func GetFollows(e Execer, limit int, filters ...filter) ([]models.Follow, error) { 138 var follows []models.Follow 139 140 var conditions []string ··· 191 } 192 193 func GetFollowers(e Execer, did string) ([]models.Follow, error) { 194 - return GetFollows(e, 0, FilterEq("subject_did", did)) 195 } 196 197 func GetFollowing(e Execer, did string) ([]models.Follow, error) { 198 - return GetFollows(e, 0, FilterEq("user_did", did)) 199 } 200 201 func getFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]models.FollowStatus, error) {
··· 7 "time" 8 9 "tangled.org/core/appview/models" 10 + "tangled.org/core/orm" 11 ) 12 13 func AddFollow(e Execer, follow *models.Follow) error { ··· 135 return result, nil 136 } 137 138 + func GetFollows(e Execer, limit int, filters ...orm.Filter) ([]models.Follow, error) { 139 var follows []models.Follow 140 141 var conditions []string ··· 192 } 193 194 func GetFollowers(e Execer, did string) ([]models.Follow, error) { 195 + return GetFollows(e, 0, orm.FilterEq("subject_did", did)) 196 } 197 198 func GetFollowing(e Execer, did string) ([]models.Follow, error) { 199 + return GetFollows(e, 0, orm.FilterEq("user_did", did)) 200 } 201 202 func getFollowStatuses(e Execer, userDid string, subjectDids []string) (map[string]models.FollowStatus, error) {
+21 -20
appview/db/issues.go
··· 13 "tangled.org/core/api/tangled" 14 "tangled.org/core/appview/models" 15 "tangled.org/core/appview/pagination" 16 ) 17 18 func PutIssue(tx *sql.Tx, issue *models.Issue) error { ··· 27 28 issues, err := GetIssues( 29 tx, 30 - FilterEq("did", issue.Did), 31 - FilterEq("rkey", issue.Rkey), 32 ) 33 switch { 34 case err != nil: ··· 98 return nil 99 } 100 101 - func GetIssuesPaginated(e Execer, page pagination.Page, filters ...filter) ([]models.Issue, error) { 102 issueMap := make(map[string]*models.Issue) // at-uri -> issue 103 104 var conditions []string ··· 114 whereClause = " where " + strings.Join(conditions, " and ") 115 } 116 117 - pLower := FilterGte("row_num", page.Offset+1) 118 - pUpper := FilterLte("row_num", page.Offset+page.Limit) 119 120 pageClause := "" 121 if page.Limit > 0 { ··· 205 repoAts = append(repoAts, string(issue.RepoAt)) 206 } 207 208 - repos, err := GetRepos(e, 0, FilterIn("at_uri", repoAts)) 209 if err != nil { 210 return nil, fmt.Errorf("failed to build repo mappings: %w", err) 211 } ··· 228 // collect comments 229 issueAts := slices.Collect(maps.Keys(issueMap)) 230 231 - comments, err := GetIssueComments(e, FilterIn("issue_at", issueAts)) 232 if err != nil { 233 return nil, fmt.Errorf("failed to query comments: %w", err) 234 } ··· 240 } 241 242 // collect allLabels for each issue 243 - allLabels, err := GetLabels(e, FilterIn("subject", issueAts)) 244 if err != nil { 245 return nil, fmt.Errorf("failed to query labels: %w", err) 246 } ··· 251 } 252 253 // collect references for each issue 254 - allReferencs, err := GetReferencesAll(e, FilterIn("from_at", issueAts)) 255 if err != nil { 256 return nil, fmt.Errorf("failed to query reference_links: %w", err) 257 } ··· 277 issues, err := GetIssuesPaginated( 278 e, 279 pagination.Page{}, 280 - FilterEq("repo_at", repoAt), 281 - FilterEq("issue_id", issueId), 282 ) 283 if err != nil { 284 return nil, err ··· 290 return &issues[0], nil 291 } 292 293 - func GetIssues(e Execer, filters ...filter) ([]models.Issue, error) { 294 return GetIssuesPaginated(e, pagination.Page{}, filters...) 295 } 296 ··· 298 func GetIssueIDs(e Execer, opts models.IssueSearchOptions) ([]int64, error) { 299 var ids []int64 300 301 - var filters []filter 302 openValue := 0 303 if opts.IsOpen { 304 openValue = 1 305 } 306 - filters = append(filters, FilterEq("open", openValue)) 307 if opts.RepoAt != "" { 308 - filters = append(filters, FilterEq("repo_at", opts.RepoAt)) 309 } 310 311 var conditions []string ··· 397 return id, nil 398 } 399 400 - func DeleteIssueComments(e Execer, filters ...filter) error { 401 var conditions []string 402 var args []any 403 for _, filter := range filters { ··· 416 return err 417 } 418 419 - func GetIssueComments(e Execer, filters ...filter) ([]models.IssueComment, error) { 420 commentMap := make(map[string]*models.IssueComment) 421 422 var conditions []string ··· 506 507 // collect references for each comments 508 commentAts := slices.Collect(maps.Keys(commentMap)) 509 - allReferencs, err := GetReferencesAll(e, FilterIn("from_at", commentAts)) 510 if err != nil { 511 return nil, fmt.Errorf("failed to query reference_links: %w", err) 512 } ··· 548 return nil 549 } 550 551 - func CloseIssues(e Execer, filters ...filter) error { 552 var conditions []string 553 var args []any 554 for _, filter := range filters { ··· 566 return err 567 } 568 569 - func ReopenIssues(e Execer, filters ...filter) error { 570 var conditions []string 571 var args []any 572 for _, filter := range filters {
··· 13 "tangled.org/core/api/tangled" 14 "tangled.org/core/appview/models" 15 "tangled.org/core/appview/pagination" 16 + "tangled.org/core/orm" 17 ) 18 19 func PutIssue(tx *sql.Tx, issue *models.Issue) error { ··· 28 29 issues, err := GetIssues( 30 tx, 31 + orm.FilterEq("did", issue.Did), 32 + orm.FilterEq("rkey", issue.Rkey), 33 ) 34 switch { 35 case err != nil: ··· 99 return nil 100 } 101 102 + func GetIssuesPaginated(e Execer, page pagination.Page, filters ...orm.Filter) ([]models.Issue, error) { 103 issueMap := make(map[string]*models.Issue) // at-uri -> issue 104 105 var conditions []string ··· 115 whereClause = " where " + strings.Join(conditions, " and ") 116 } 117 118 + pLower := orm.FilterGte("row_num", page.Offset+1) 119 + pUpper := orm.FilterLte("row_num", page.Offset+page.Limit) 120 121 pageClause := "" 122 if page.Limit > 0 { ··· 206 repoAts = append(repoAts, string(issue.RepoAt)) 207 } 208 209 + repos, err := GetRepos(e, 0, orm.FilterIn("at_uri", repoAts)) 210 if err != nil { 211 return nil, fmt.Errorf("failed to build repo mappings: %w", err) 212 } ··· 229 // collect comments 230 issueAts := slices.Collect(maps.Keys(issueMap)) 231 232 + comments, err := GetIssueComments(e, orm.FilterIn("issue_at", issueAts)) 233 if err != nil { 234 return nil, fmt.Errorf("failed to query comments: %w", err) 235 } ··· 241 } 242 243 // collect allLabels for each issue 244 + allLabels, err := GetLabels(e, orm.FilterIn("subject", issueAts)) 245 if err != nil { 246 return nil, fmt.Errorf("failed to query labels: %w", err) 247 } ··· 252 } 253 254 // collect references for each issue 255 + allReferencs, err := GetReferencesAll(e, orm.FilterIn("from_at", issueAts)) 256 if err != nil { 257 return nil, fmt.Errorf("failed to query reference_links: %w", err) 258 } ··· 278 issues, err := GetIssuesPaginated( 279 e, 280 pagination.Page{}, 281 + orm.FilterEq("repo_at", repoAt), 282 + orm.FilterEq("issue_id", issueId), 283 ) 284 if err != nil { 285 return nil, err ··· 291 return &issues[0], nil 292 } 293 294 + func GetIssues(e Execer, filters ...orm.Filter) ([]models.Issue, error) { 295 return GetIssuesPaginated(e, pagination.Page{}, filters...) 296 } 297 ··· 299 func GetIssueIDs(e Execer, opts models.IssueSearchOptions) ([]int64, error) { 300 var ids []int64 301 302 + var filters []orm.Filter 303 openValue := 0 304 if opts.IsOpen { 305 openValue = 1 306 } 307 + filters = append(filters, orm.FilterEq("open", openValue)) 308 if opts.RepoAt != "" { 309 + filters = append(filters, orm.FilterEq("repo_at", opts.RepoAt)) 310 } 311 312 var conditions []string ··· 398 return id, nil 399 } 400 401 + func DeleteIssueComments(e Execer, filters ...orm.Filter) error { 402 var conditions []string 403 var args []any 404 for _, filter := range filters { ··· 417 return err 418 } 419 420 + func GetIssueComments(e Execer, filters ...orm.Filter) ([]models.IssueComment, error) { 421 commentMap := make(map[string]*models.IssueComment) 422 423 var conditions []string ··· 507 508 // collect references for each comments 509 commentAts := slices.Collect(maps.Keys(commentMap)) 510 + allReferencs, err := GetReferencesAll(e, orm.FilterIn("from_at", commentAts)) 511 if err != nil { 512 return nil, fmt.Errorf("failed to query reference_links: %w", err) 513 } ··· 549 return nil 550 } 551 552 + func CloseIssues(e Execer, filters ...orm.Filter) error { 553 var conditions []string 554 var args []any 555 for _, filter := range filters { ··· 567 return err 568 } 569 570 + func ReopenIssues(e Execer, filters ...orm.Filter) error { 571 var conditions []string 572 var args []any 573 for _, filter := range filters {
+8 -7
appview/db/label.go
··· 10 11 "github.com/bluesky-social/indigo/atproto/syntax" 12 "tangled.org/core/appview/models" 13 ) 14 15 // no updating type for now ··· 59 return id, nil 60 } 61 62 - func DeleteLabelDefinition(e Execer, filters ...filter) error { 63 var conditions []string 64 var args []any 65 for _, filter := range filters { ··· 75 return err 76 } 77 78 - func GetLabelDefinitions(e Execer, filters ...filter) ([]models.LabelDefinition, error) { 79 var labelDefinitions []models.LabelDefinition 80 var conditions []string 81 var args []any ··· 167 } 168 169 // helper to get exactly one label def 170 - func GetLabelDefinition(e Execer, filters ...filter) (*models.LabelDefinition, error) { 171 labels, err := GetLabelDefinitions(e, filters...) 172 if err != nil { 173 return nil, err ··· 227 return id, nil 228 } 229 230 - func GetLabelOps(e Execer, filters ...filter) ([]models.LabelOp, error) { 231 var labelOps []models.LabelOp 232 var conditions []string 233 var args []any ··· 302 } 303 304 // get labels for a given list of subject URIs 305 - func GetLabels(e Execer, filters ...filter) (map[syntax.ATURI]models.LabelState, error) { 306 ops, err := GetLabelOps(e, filters...) 307 if err != nil { 308 return nil, err ··· 322 } 323 labelAts := slices.Collect(maps.Keys(labelAtSet)) 324 325 - actx, err := NewLabelApplicationCtx(e, FilterIn("at_uri", labelAts)) 326 if err != nil { 327 return nil, err 328 } ··· 338 return results, nil 339 } 340 341 - func NewLabelApplicationCtx(e Execer, filters ...filter) (*models.LabelApplicationCtx, error) { 342 labels, err := GetLabelDefinitions(e, filters...) 343 if err != nil { 344 return nil, err
··· 10 11 "github.com/bluesky-social/indigo/atproto/syntax" 12 "tangled.org/core/appview/models" 13 + "tangled.org/core/orm" 14 ) 15 16 // no updating type for now ··· 60 return id, nil 61 } 62 63 + func DeleteLabelDefinition(e Execer, filters ...orm.Filter) error { 64 var conditions []string 65 var args []any 66 for _, filter := range filters { ··· 76 return err 77 } 78 79 + func GetLabelDefinitions(e Execer, filters ...orm.Filter) ([]models.LabelDefinition, error) { 80 var labelDefinitions []models.LabelDefinition 81 var conditions []string 82 var args []any ··· 168 } 169 170 // helper to get exactly one label def 171 + func GetLabelDefinition(e Execer, filters ...orm.Filter) (*models.LabelDefinition, error) { 172 labels, err := GetLabelDefinitions(e, filters...) 173 if err != nil { 174 return nil, err ··· 228 return id, nil 229 } 230 231 + func GetLabelOps(e Execer, filters ...orm.Filter) ([]models.LabelOp, error) { 232 var labelOps []models.LabelOp 233 var conditions []string 234 var args []any ··· 303 } 304 305 // get labels for a given list of subject URIs 306 + func GetLabels(e Execer, filters ...orm.Filter) (map[syntax.ATURI]models.LabelState, error) { 307 ops, err := GetLabelOps(e, filters...) 308 if err != nil { 309 return nil, err ··· 323 } 324 labelAts := slices.Collect(maps.Keys(labelAtSet)) 325 326 + actx, err := NewLabelApplicationCtx(e, orm.FilterIn("at_uri", labelAts)) 327 if err != nil { 328 return nil, err 329 } ··· 339 return results, nil 340 } 341 342 + func NewLabelApplicationCtx(e Execer, filters ...orm.Filter) (*models.LabelApplicationCtx, error) { 343 labels, err := GetLabelDefinitions(e, filters...) 344 if err != nil { 345 return nil, err
+5 -4
appview/db/language.go
··· 7 8 "github.com/bluesky-social/indigo/atproto/syntax" 9 "tangled.org/core/appview/models" 10 ) 11 12 - func GetRepoLanguages(e Execer, filters ...filter) ([]models.RepoLanguage, error) { 13 var conditions []string 14 var args []any 15 for _, filter := range filters { ··· 85 return nil 86 } 87 88 - func DeleteRepoLanguages(e Execer, filters ...filter) error { 89 var conditions []string 90 var args []any 91 for _, filter := range filters { ··· 107 func UpdateRepoLanguages(tx *sql.Tx, repoAt syntax.ATURI, ref string, langs []models.RepoLanguage) error { 108 err := DeleteRepoLanguages( 109 tx, 110 - FilterEq("repo_at", repoAt), 111 - FilterEq("ref", ref), 112 ) 113 if err != nil { 114 return fmt.Errorf("failed to delete existing languages: %w", err)
··· 7 8 "github.com/bluesky-social/indigo/atproto/syntax" 9 "tangled.org/core/appview/models" 10 + "tangled.org/core/orm" 11 ) 12 13 + func GetRepoLanguages(e Execer, filters ...orm.Filter) ([]models.RepoLanguage, error) { 14 var conditions []string 15 var args []any 16 for _, filter := range filters { ··· 86 return nil 87 } 88 89 + func DeleteRepoLanguages(e Execer, filters ...orm.Filter) error { 90 var conditions []string 91 var args []any 92 for _, filter := range filters { ··· 108 func UpdateRepoLanguages(tx *sql.Tx, repoAt syntax.ATURI, ref string, langs []models.RepoLanguage) error { 109 err := DeleteRepoLanguages( 110 tx, 111 + orm.FilterEq("repo_at", repoAt), 112 + orm.FilterEq("ref", ref), 113 ) 114 if err != nil { 115 return fmt.Errorf("failed to delete existing languages: %w", err)
+14 -13
appview/db/notifications.go
··· 11 "github.com/bluesky-social/indigo/atproto/syntax" 12 "tangled.org/core/appview/models" 13 "tangled.org/core/appview/pagination" 14 ) 15 16 func CreateNotification(e Execer, notification *models.Notification) error { ··· 44 } 45 46 // GetNotificationsPaginated retrieves notifications with filters and pagination 47 - func GetNotificationsPaginated(e Execer, page pagination.Page, filters ...filter) ([]*models.Notification, error) { 48 var conditions []string 49 var args []any 50 ··· 113 } 114 115 // GetNotificationsWithEntities retrieves notifications with their related entities 116 - func GetNotificationsWithEntities(e Execer, page pagination.Page, filters ...filter) ([]*models.NotificationWithEntity, error) { 117 var conditions []string 118 var args []any 119 ··· 256 } 257 258 // GetNotifications retrieves notifications with filters 259 - func GetNotifications(e Execer, filters ...filter) ([]*models.Notification, error) { 260 return GetNotificationsPaginated(e, pagination.FirstPage(), filters...) 261 } 262 263 - func CountNotifications(e Execer, filters ...filter) (int64, error) { 264 var conditions []string 265 var args []any 266 for _, filter := range filters { ··· 285 } 286 287 func MarkNotificationRead(e Execer, notificationID int64, userDID string) error { 288 - idFilter := FilterEq("id", notificationID) 289 - recipientFilter := FilterEq("recipient_did", userDID) 290 291 query := fmt.Sprintf(` 292 UPDATE notifications ··· 314 } 315 316 func MarkAllNotificationsRead(e Execer, userDID string) error { 317 - recipientFilter := FilterEq("recipient_did", userDID) 318 - readFilter := FilterEq("read", 0) 319 320 query := fmt.Sprintf(` 321 UPDATE notifications ··· 334 } 335 336 func DeleteNotification(e Execer, notificationID int64, userDID string) error { 337 - idFilter := FilterEq("id", notificationID) 338 - recipientFilter := FilterEq("recipient_did", userDID) 339 340 query := fmt.Sprintf(` 341 DELETE FROM notifications ··· 362 } 363 364 func GetNotificationPreference(e Execer, userDid string) (*models.NotificationPreferences, error) { 365 - prefs, err := GetNotificationPreferences(e, FilterEq("user_did", userDid)) 366 if err != nil { 367 return nil, err 368 } ··· 375 return p, nil 376 } 377 378 - func GetNotificationPreferences(e Execer, filters ...filter) (map[syntax.DID]*models.NotificationPreferences, error) { 379 prefsMap := make(map[syntax.DID]*models.NotificationPreferences) 380 381 var conditions []string ··· 483 484 func (d *DB) ClearOldNotifications(ctx context.Context, olderThan time.Duration) error { 485 cutoff := time.Now().Add(-olderThan) 486 - createdFilter := FilterLte("created", cutoff) 487 488 query := fmt.Sprintf(` 489 DELETE FROM notifications
··· 11 "github.com/bluesky-social/indigo/atproto/syntax" 12 "tangled.org/core/appview/models" 13 "tangled.org/core/appview/pagination" 14 + "tangled.org/core/orm" 15 ) 16 17 func CreateNotification(e Execer, notification *models.Notification) error { ··· 45 } 46 47 // GetNotificationsPaginated retrieves notifications with filters and pagination 48 + func GetNotificationsPaginated(e Execer, page pagination.Page, filters ...orm.Filter) ([]*models.Notification, error) { 49 var conditions []string 50 var args []any 51 ··· 114 } 115 116 // GetNotificationsWithEntities retrieves notifications with their related entities 117 + func GetNotificationsWithEntities(e Execer, page pagination.Page, filters ...orm.Filter) ([]*models.NotificationWithEntity, error) { 118 var conditions []string 119 var args []any 120 ··· 257 } 258 259 // GetNotifications retrieves notifications with filters 260 + func GetNotifications(e Execer, filters ...orm.Filter) ([]*models.Notification, error) { 261 return GetNotificationsPaginated(e, pagination.FirstPage(), filters...) 262 } 263 264 + func CountNotifications(e Execer, filters ...orm.Filter) (int64, error) { 265 var conditions []string 266 var args []any 267 for _, filter := range filters { ··· 286 } 287 288 func MarkNotificationRead(e Execer, notificationID int64, userDID string) error { 289 + idFilter := orm.FilterEq("id", notificationID) 290 + recipientFilter := orm.FilterEq("recipient_did", userDID) 291 292 query := fmt.Sprintf(` 293 UPDATE notifications ··· 315 } 316 317 func MarkAllNotificationsRead(e Execer, userDID string) error { 318 + recipientFilter := orm.FilterEq("recipient_did", userDID) 319 + readFilter := orm.FilterEq("read", 0) 320 321 query := fmt.Sprintf(` 322 UPDATE notifications ··· 335 } 336 337 func DeleteNotification(e Execer, notificationID int64, userDID string) error { 338 + idFilter := orm.FilterEq("id", notificationID) 339 + recipientFilter := orm.FilterEq("recipient_did", userDID) 340 341 query := fmt.Sprintf(` 342 DELETE FROM notifications ··· 363 } 364 365 func GetNotificationPreference(e Execer, userDid string) (*models.NotificationPreferences, error) { 366 + prefs, err := GetNotificationPreferences(e, orm.FilterEq("user_did", userDid)) 367 if err != nil { 368 return nil, err 369 } ··· 376 return p, nil 377 } 378 379 + func GetNotificationPreferences(e Execer, filters ...orm.Filter) (map[syntax.DID]*models.NotificationPreferences, error) { 380 prefsMap := make(map[syntax.DID]*models.NotificationPreferences) 381 382 var conditions []string ··· 484 485 func (d *DB) ClearOldNotifications(ctx context.Context, olderThan time.Duration) error { 486 cutoff := time.Now().Add(-olderThan) 487 + createdFilter := orm.FilterLte("created", cutoff) 488 489 query := fmt.Sprintf(` 490 DELETE FROM notifications
+6 -5
appview/db/pipeline.go
··· 7 "time" 8 9 "tangled.org/core/appview/models" 10 ) 11 12 - func GetPipelines(e Execer, filters ...filter) ([]models.Pipeline, error) { 13 var pipelines []models.Pipeline 14 15 var conditions []string ··· 168 169 // this is a mega query, but the most useful one: 170 // get N pipelines, for each one get the latest status of its N workflows 171 - func GetPipelineStatuses(e Execer, limit int, filters ...filter) ([]models.Pipeline, error) { 172 var conditions []string 173 var args []any 174 for _, filter := range filters { 175 - filter.key = "p." + filter.key // the table is aliased in the query to `p` 176 conditions = append(conditions, filter.Condition()) 177 args = append(args, filter.Arg()...) 178 } ··· 264 conditions = nil 265 args = nil 266 for _, p := range pipelines { 267 - knotFilter := FilterEq("pipeline_knot", p.Knot) 268 - rkeyFilter := FilterEq("pipeline_rkey", p.Rkey) 269 conditions = append(conditions, fmt.Sprintf("(%s and %s)", knotFilter.Condition(), rkeyFilter.Condition())) 270 args = append(args, p.Knot) 271 args = append(args, p.Rkey)
··· 7 "time" 8 9 "tangled.org/core/appview/models" 10 + "tangled.org/core/orm" 11 ) 12 13 + func GetPipelines(e Execer, filters ...orm.Filter) ([]models.Pipeline, error) { 14 var pipelines []models.Pipeline 15 16 var conditions []string ··· 169 170 // this is a mega query, but the most useful one: 171 // get N pipelines, for each one get the latest status of its N workflows 172 + func GetPipelineStatuses(e Execer, limit int, filters ...orm.Filter) ([]models.Pipeline, error) { 173 var conditions []string 174 var args []any 175 for _, filter := range filters { 176 + filter.Key = "p." + filter.Key // the table is aliased in the query to `p` 177 conditions = append(conditions, filter.Condition()) 178 args = append(args, filter.Arg()...) 179 } ··· 265 conditions = nil 266 args = nil 267 for _, p := range pipelines { 268 + knotFilter := orm.FilterEq("pipeline_knot", p.Knot) 269 + rkeyFilter := orm.FilterEq("pipeline_rkey", p.Rkey) 270 conditions = append(conditions, fmt.Sprintf("(%s and %s)", knotFilter.Condition(), rkeyFilter.Condition())) 271 args = append(args, p.Knot) 272 args = append(args, p.Rkey)
+16 -8
appview/db/profile.go
··· 11 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 "tangled.org/core/appview/models" 14 ) 15 16 const TimeframeMonths = 7 ··· 44 45 issues, err := GetIssues( 46 e, 47 - FilterEq("did", forDid), 48 - FilterGte("created", time.Now().AddDate(0, -TimeframeMonths, 0)), 49 ) 50 if err != nil { 51 return nil, fmt.Errorf("error getting issues by owner did: %w", err) ··· 65 *items = append(*items, &issue) 66 } 67 68 - repos, err := GetRepos(e, 0, FilterEq("did", forDid)) 69 if err != nil { 70 return nil, fmt.Errorf("error getting all repos by did: %w", err) 71 } ··· 127 _, err = tx.Exec( 128 `insert or replace into profile ( 129 did, 130 description, 131 include_bluesky, 132 location, 133 pronouns 134 ) 135 - values (?, ?, ?, ?, ?)`, 136 profile.Did, 137 profile.Description, 138 includeBskyValue, 139 profile.Location, ··· 199 return tx.Commit() 200 } 201 202 - func GetProfiles(e Execer, filters ...filter) (map[string]*models.Profile, error) { 203 var conditions []string 204 var args []any 205 for _, filter := range filters { ··· 311 func GetProfile(e Execer, did string) (*models.Profile, error) { 312 var profile models.Profile 313 var pronouns sql.Null[string] 314 315 profile.Did = did 316 317 includeBluesky := 0 318 319 err := e.QueryRow( 320 - `select description, include_bluesky, location, pronouns from profile where did = ?`, 321 did, 322 - ).Scan(&profile.Description, &includeBluesky, &profile.Location, &pronouns) 323 if err == sql.ErrNoRows { 324 profile := models.Profile{} 325 profile.Did = did ··· 338 profile.Pronouns = pronouns.V 339 } 340 341 rows, err := e.Query(`select link from profile_links where did = ?`, did) 342 if err != nil { 343 return nil, err ··· 441 } 442 443 // ensure all pinned repos are either own repos or collaborating repos 444 - repos, err := GetRepos(e, 0, FilterEq("did", profile.Did)) 445 if err != nil { 446 log.Printf("getting repos for %s: %s", profile.Did, err) 447 }
··· 11 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 "tangled.org/core/appview/models" 14 + "tangled.org/core/orm" 15 ) 16 17 const TimeframeMonths = 7 ··· 45 46 issues, err := GetIssues( 47 e, 48 + orm.FilterEq("did", forDid), 49 + orm.FilterGte("created", time.Now().AddDate(0, -TimeframeMonths, 0)), 50 ) 51 if err != nil { 52 return nil, fmt.Errorf("error getting issues by owner did: %w", err) ··· 66 *items = append(*items, &issue) 67 } 68 69 + repos, err := GetRepos(e, 0, orm.FilterEq("did", forDid)) 70 if err != nil { 71 return nil, fmt.Errorf("error getting all repos by did: %w", err) 72 } ··· 128 _, err = tx.Exec( 129 `insert or replace into profile ( 130 did, 131 + avatar, 132 description, 133 include_bluesky, 134 location, 135 pronouns 136 ) 137 + values (?, ?, ?, ?, ?, ?)`, 138 profile.Did, 139 + profile.Avatar, 140 profile.Description, 141 includeBskyValue, 142 profile.Location, ··· 202 return tx.Commit() 203 } 204 205 + func GetProfiles(e Execer, filters ...orm.Filter) (map[string]*models.Profile, error) { 206 var conditions []string 207 var args []any 208 for _, filter := range filters { ··· 314 func GetProfile(e Execer, did string) (*models.Profile, error) { 315 var profile models.Profile 316 var pronouns sql.Null[string] 317 + var avatar sql.Null[string] 318 319 profile.Did = did 320 321 includeBluesky := 0 322 323 err := e.QueryRow( 324 + `select avatar, description, include_bluesky, location, pronouns from profile where did = ?`, 325 did, 326 + ).Scan(&avatar, &profile.Description, &includeBluesky, &profile.Location, &pronouns) 327 if err == sql.ErrNoRows { 328 profile := models.Profile{} 329 profile.Did = did ··· 342 profile.Pronouns = pronouns.V 343 } 344 345 + if avatar.Valid { 346 + profile.Avatar = avatar.V 347 + } 348 + 349 rows, err := e.Query(`select link from profile_links where did = ?`, did) 350 if err != nil { 351 return nil, err ··· 449 } 450 451 // ensure all pinned repos are either own repos or collaborating repos 452 + repos, err := GetRepos(e, 0, orm.FilterEq("did", profile.Did)) 453 if err != nil { 454 log.Printf("getting repos for %s: %s", profile.Did, err) 455 }
+21 -20
appview/db/pulls.go
··· 13 14 "github.com/bluesky-social/indigo/atproto/syntax" 15 "tangled.org/core/appview/models" 16 ) 17 18 func NewPull(tx *sql.Tx, pull *models.Pull) error { ··· 118 return pullId - 1, err 119 } 120 121 - func GetPullsWithLimit(e Execer, limit int, filters ...filter) ([]*models.Pull, error) { 122 pulls := make(map[syntax.ATURI]*models.Pull) 123 124 var conditions []string ··· 229 for _, p := range pulls { 230 pullAts = append(pullAts, p.AtUri()) 231 } 232 - submissionsMap, err := GetPullSubmissions(e, FilterIn("pull_at", pullAts)) 233 if err != nil { 234 return nil, fmt.Errorf("failed to get submissions: %w", err) 235 } ··· 241 } 242 243 // collect allLabels for each issue 244 - allLabels, err := GetLabels(e, FilterIn("subject", pullAts)) 245 if err != nil { 246 return nil, fmt.Errorf("failed to query labels: %w", err) 247 } ··· 258 sourceAts = append(sourceAts, *p.PullSource.RepoAt) 259 } 260 } 261 - sourceRepos, err := GetRepos(e, 0, FilterIn("at_uri", sourceAts)) 262 if err != nil && !errors.Is(err, sql.ErrNoRows) { 263 return nil, fmt.Errorf("failed to get source repos: %w", err) 264 } ··· 274 } 275 } 276 277 - allReferences, err := GetReferencesAll(e, FilterIn("from_at", pullAts)) 278 if err != nil { 279 return nil, fmt.Errorf("failed to query reference_links: %w", err) 280 } ··· 295 return orderedByPullId, nil 296 } 297 298 - func GetPulls(e Execer, filters ...filter) ([]*models.Pull, error) { 299 return GetPullsWithLimit(e, 0, filters...) 300 } 301 302 func GetPullIDs(e Execer, opts models.PullSearchOptions) ([]int64, error) { 303 var ids []int64 304 305 - var filters []filter 306 - filters = append(filters, FilterEq("state", opts.State)) 307 if opts.RepoAt != "" { 308 - filters = append(filters, FilterEq("repo_at", opts.RepoAt)) 309 } 310 311 var conditions []string ··· 361 } 362 363 func GetPull(e Execer, repoAt syntax.ATURI, pullId int) (*models.Pull, error) { 364 - pulls, err := GetPullsWithLimit(e, 1, FilterEq("repo_at", repoAt), FilterEq("pull_id", pullId)) 365 if err != nil { 366 return nil, err 367 } ··· 373 } 374 375 // mapping from pull -> pull submissions 376 - func GetPullSubmissions(e Execer, filters ...filter) (map[syntax.ATURI][]*models.PullSubmission, error) { 377 var conditions []string 378 var args []any 379 for _, filter := range filters { ··· 448 449 // Get comments for all submissions using GetPullComments 450 submissionIds := slices.Collect(maps.Keys(submissionMap)) 451 - comments, err := GetPullComments(e, FilterIn("submission_id", submissionIds)) 452 if err != nil { 453 return nil, fmt.Errorf("failed to get pull comments: %w", err) 454 } ··· 474 return m, nil 475 } 476 477 - func GetPullComments(e Execer, filters ...filter) ([]models.PullComment, error) { 478 var conditions []string 479 var args []any 480 for _, filter := range filters { ··· 542 543 // collect references for each comments 544 commentAts := slices.Collect(maps.Keys(commentMap)) 545 - allReferencs, err := GetReferencesAll(e, FilterIn("from_at", commentAts)) 546 if err != nil { 547 return nil, fmt.Errorf("failed to query reference_links: %w", err) 548 } ··· 708 return err 709 } 710 711 - func SetPullParentChangeId(e Execer, parentChangeId string, filters ...filter) error { 712 var conditions []string 713 var args []any 714 ··· 732 733 // Only used when stacking to update contents in the event of a rebase (the interdiff should be empty). 734 // otherwise submissions are immutable 735 - func UpdatePull(e Execer, newPatch, sourceRev string, filters ...filter) error { 736 var conditions []string 737 var args []any 738 ··· 790 func GetStack(e Execer, stackId string) (models.Stack, error) { 791 unorderedPulls, err := GetPulls( 792 e, 793 - FilterEq("stack_id", stackId), 794 - FilterNotEq("state", models.PullDeleted), 795 ) 796 if err != nil { 797 return nil, err ··· 835 func GetAbandonedPulls(e Execer, stackId string) ([]*models.Pull, error) { 836 pulls, err := GetPulls( 837 e, 838 - FilterEq("stack_id", stackId), 839 - FilterEq("state", models.PullDeleted), 840 ) 841 if err != nil { 842 return nil, err
··· 13 14 "github.com/bluesky-social/indigo/atproto/syntax" 15 "tangled.org/core/appview/models" 16 + "tangled.org/core/orm" 17 ) 18 19 func NewPull(tx *sql.Tx, pull *models.Pull) error { ··· 119 return pullId - 1, err 120 } 121 122 + func GetPullsWithLimit(e Execer, limit int, filters ...orm.Filter) ([]*models.Pull, error) { 123 pulls := make(map[syntax.ATURI]*models.Pull) 124 125 var conditions []string ··· 230 for _, p := range pulls { 231 pullAts = append(pullAts, p.AtUri()) 232 } 233 + submissionsMap, err := GetPullSubmissions(e, orm.FilterIn("pull_at", pullAts)) 234 if err != nil { 235 return nil, fmt.Errorf("failed to get submissions: %w", err) 236 } ··· 242 } 243 244 // collect allLabels for each issue 245 + allLabels, err := GetLabels(e, orm.FilterIn("subject", pullAts)) 246 if err != nil { 247 return nil, fmt.Errorf("failed to query labels: %w", err) 248 } ··· 259 sourceAts = append(sourceAts, *p.PullSource.RepoAt) 260 } 261 } 262 + sourceRepos, err := GetRepos(e, 0, orm.FilterIn("at_uri", sourceAts)) 263 if err != nil && !errors.Is(err, sql.ErrNoRows) { 264 return nil, fmt.Errorf("failed to get source repos: %w", err) 265 } ··· 275 } 276 } 277 278 + allReferences, err := GetReferencesAll(e, orm.FilterIn("from_at", pullAts)) 279 if err != nil { 280 return nil, fmt.Errorf("failed to query reference_links: %w", err) 281 } ··· 296 return orderedByPullId, nil 297 } 298 299 + func GetPulls(e Execer, filters ...orm.Filter) ([]*models.Pull, error) { 300 return GetPullsWithLimit(e, 0, filters...) 301 } 302 303 func GetPullIDs(e Execer, opts models.PullSearchOptions) ([]int64, error) { 304 var ids []int64 305 306 + var filters []orm.Filter 307 + filters = append(filters, orm.FilterEq("state", opts.State)) 308 if opts.RepoAt != "" { 309 + filters = append(filters, orm.FilterEq("repo_at", opts.RepoAt)) 310 } 311 312 var conditions []string ··· 362 } 363 364 func GetPull(e Execer, repoAt syntax.ATURI, pullId int) (*models.Pull, error) { 365 + pulls, err := GetPullsWithLimit(e, 1, orm.FilterEq("repo_at", repoAt), orm.FilterEq("pull_id", pullId)) 366 if err != nil { 367 return nil, err 368 } ··· 374 } 375 376 // mapping from pull -> pull submissions 377 + func GetPullSubmissions(e Execer, filters ...orm.Filter) (map[syntax.ATURI][]*models.PullSubmission, error) { 378 var conditions []string 379 var args []any 380 for _, filter := range filters { ··· 449 450 // Get comments for all submissions using GetPullComments 451 submissionIds := slices.Collect(maps.Keys(submissionMap)) 452 + comments, err := GetPullComments(e, orm.FilterIn("submission_id", submissionIds)) 453 if err != nil { 454 return nil, fmt.Errorf("failed to get pull comments: %w", err) 455 } ··· 475 return m, nil 476 } 477 478 + func GetPullComments(e Execer, filters ...orm.Filter) ([]models.PullComment, error) { 479 var conditions []string 480 var args []any 481 for _, filter := range filters { ··· 543 544 // collect references for each comments 545 commentAts := slices.Collect(maps.Keys(commentMap)) 546 + allReferencs, err := GetReferencesAll(e, orm.FilterIn("from_at", commentAts)) 547 if err != nil { 548 return nil, fmt.Errorf("failed to query reference_links: %w", err) 549 } ··· 709 return err 710 } 711 712 + func SetPullParentChangeId(e Execer, parentChangeId string, filters ...orm.Filter) error { 713 var conditions []string 714 var args []any 715 ··· 733 734 // Only used when stacking to update contents in the event of a rebase (the interdiff should be empty). 735 // otherwise submissions are immutable 736 + func UpdatePull(e Execer, newPatch, sourceRev string, filters ...orm.Filter) error { 737 var conditions []string 738 var args []any 739 ··· 791 func GetStack(e Execer, stackId string) (models.Stack, error) { 792 unorderedPulls, err := GetPulls( 793 e, 794 + orm.FilterEq("stack_id", stackId), 795 + orm.FilterNotEq("state", models.PullDeleted), 796 ) 797 if err != nil { 798 return nil, err ··· 836 func GetAbandonedPulls(e Execer, stackId string) ([]*models.Pull, error) { 837 pulls, err := GetPulls( 838 e, 839 + orm.FilterEq("stack_id", stackId), 840 + orm.FilterEq("state", models.PullDeleted), 841 ) 842 if err != nil { 843 return nil, err
+2 -1
appview/db/punchcard.go
··· 7 "time" 8 9 "tangled.org/core/appview/models" 10 ) 11 12 // this adds to the existing count ··· 20 return err 21 } 22 23 - func MakePunchcard(e Execer, filters ...filter) (*models.Punchcard, error) { 24 punchcard := &models.Punchcard{} 25 now := time.Now() 26 startOfYear := time.Date(now.Year(), 1, 1, 0, 0, 0, 0, time.UTC)
··· 7 "time" 8 9 "tangled.org/core/appview/models" 10 + "tangled.org/core/orm" 11 ) 12 13 // this adds to the existing count ··· 21 return err 22 } 23 24 + func MakePunchcard(e Execer, filters ...orm.Filter) (*models.Punchcard, error) { 25 punchcard := &models.Punchcard{} 26 now := time.Now() 27 startOfYear := time.Date(now.Year(), 1, 1, 0, 0, 0, 0, time.UTC)
+4 -3
appview/db/reference.go
··· 8 "github.com/bluesky-social/indigo/atproto/syntax" 9 "tangled.org/core/api/tangled" 10 "tangled.org/core/appview/models" 11 ) 12 13 // ValidateReferenceLinks resolves refLinks to Issue/PR/IssueComment/PullComment ATURIs. ··· 205 return err 206 } 207 208 - func GetReferencesAll(e Execer, filters ...filter) (map[syntax.ATURI][]syntax.ATURI, error) { 209 var ( 210 conditions []string 211 args []any ··· 347 if len(aturis) == 0 { 348 return nil, nil 349 } 350 - filter := FilterIn("c.at_uri", aturis) 351 rows, err := e.Query( 352 fmt.Sprintf( 353 `select r.did, r.name, i.issue_id, c.id, i.title, i.open ··· 427 if len(aturis) == 0 { 428 return nil, nil 429 } 430 - filter := FilterIn("c.comment_at", aturis) 431 rows, err := e.Query( 432 fmt.Sprintf( 433 `select r.did, r.name, p.pull_id, c.id, p.title, p.state
··· 8 "github.com/bluesky-social/indigo/atproto/syntax" 9 "tangled.org/core/api/tangled" 10 "tangled.org/core/appview/models" 11 + "tangled.org/core/orm" 12 ) 13 14 // ValidateReferenceLinks resolves refLinks to Issue/PR/IssueComment/PullComment ATURIs. ··· 206 return err 207 } 208 209 + func GetReferencesAll(e Execer, filters ...orm.Filter) (map[syntax.ATURI][]syntax.ATURI, error) { 210 var ( 211 conditions []string 212 args []any ··· 348 if len(aturis) == 0 { 349 return nil, nil 350 } 351 + filter := orm.FilterIn("c.at_uri", aturis) 352 rows, err := e.Query( 353 fmt.Sprintf( 354 `select r.did, r.name, i.issue_id, c.id, i.title, i.open ··· 428 if len(aturis) == 0 { 429 return nil, nil 430 } 431 + filter := orm.FilterIn("c.comment_at", aturis) 432 rows, err := e.Query( 433 fmt.Sprintf( 434 `select r.did, r.name, p.pull_id, c.id, p.title, p.state
+4 -3
appview/db/registration.go
··· 7 "time" 8 9 "tangled.org/core/appview/models" 10 ) 11 12 - func GetRegistrations(e Execer, filters ...filter) ([]models.Registration, error) { 13 var registrations []models.Registration 14 15 var conditions []string ··· 69 return registrations, nil 70 } 71 72 - func MarkRegistered(e Execer, filters ...filter) error { 73 var conditions []string 74 var args []any 75 for _, filter := range filters { ··· 94 return err 95 } 96 97 - func DeleteKnot(e Execer, filters ...filter) error { 98 var conditions []string 99 var args []any 100 for _, filter := range filters {
··· 7 "time" 8 9 "tangled.org/core/appview/models" 10 + "tangled.org/core/orm" 11 ) 12 13 + func GetRegistrations(e Execer, filters ...orm.Filter) ([]models.Registration, error) { 14 var registrations []models.Registration 15 16 var conditions []string ··· 70 return registrations, nil 71 } 72 73 + func MarkRegistered(e Execer, filters ...orm.Filter) error { 74 var conditions []string 75 var args []any 76 for _, filter := range filters { ··· 95 return err 96 } 97 98 + func DeleteKnot(e Execer, filters ...orm.Filter) error { 99 var conditions []string 100 var args []any 101 for _, filter := range filters {
+6 -5
appview/db/repos.go
··· 11 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 "tangled.org/core/appview/models" 14 ) 15 16 - func GetRepos(e Execer, limit int, filters ...filter) ([]models.Repo, error) { 17 repoMap := make(map[syntax.ATURI]*models.Repo) 18 19 var conditions []string ··· 294 } 295 296 // helper to get exactly one repo 297 - func GetRepo(e Execer, filters ...filter) (*models.Repo, error) { 298 repos, err := GetRepos(e, 0, filters...) 299 if err != nil { 300 return nil, err ··· 311 return &repos[0], nil 312 } 313 314 - func CountRepos(e Execer, filters ...filter) (int64, error) { 315 var conditions []string 316 var args []any 317 for _, filter := range filters { ··· 542 return err 543 } 544 545 - func UnsubscribeLabel(e Execer, filters ...filter) error { 546 var conditions []string 547 var args []any 548 for _, filter := range filters { ··· 560 return err 561 } 562 563 - func GetRepoLabels(e Execer, filters ...filter) ([]models.RepoLabel, error) { 564 var conditions []string 565 var args []any 566 for _, filter := range filters {
··· 11 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 "tangled.org/core/appview/models" 14 + "tangled.org/core/orm" 15 ) 16 17 + func GetRepos(e Execer, limit int, filters ...orm.Filter) ([]models.Repo, error) { 18 repoMap := make(map[syntax.ATURI]*models.Repo) 19 20 var conditions []string ··· 295 } 296 297 // helper to get exactly one repo 298 + func GetRepo(e Execer, filters ...orm.Filter) (*models.Repo, error) { 299 repos, err := GetRepos(e, 0, filters...) 300 if err != nil { 301 return nil, err ··· 312 return &repos[0], nil 313 } 314 315 + func CountRepos(e Execer, filters ...orm.Filter) (int64, error) { 316 var conditions []string 317 var args []any 318 for _, filter := range filters { ··· 543 return err 544 } 545 546 + func UnsubscribeLabel(e Execer, filters ...orm.Filter) error { 547 var conditions []string 548 var args []any 549 for _, filter := range filters { ··· 561 return err 562 } 563 564 + func GetRepoLabels(e Execer, filters ...orm.Filter) ([]models.RepoLabel, error) { 565 var conditions []string 566 var args []any 567 for _, filter := range filters {
+6 -5
appview/db/spindle.go
··· 7 "time" 8 9 "tangled.org/core/appview/models" 10 ) 11 12 - func GetSpindles(e Execer, filters ...filter) ([]models.Spindle, error) { 13 var spindles []models.Spindle 14 15 var conditions []string ··· 91 return err 92 } 93 94 - func VerifySpindle(e Execer, filters ...filter) (int64, error) { 95 var conditions []string 96 var args []any 97 for _, filter := range filters { ··· 114 return res.RowsAffected() 115 } 116 117 - func DeleteSpindle(e Execer, filters ...filter) error { 118 var conditions []string 119 var args []any 120 for _, filter := range filters { ··· 144 return err 145 } 146 147 - func RemoveSpindleMember(e Execer, filters ...filter) error { 148 var conditions []string 149 var args []any 150 for _, filter := range filters { ··· 163 return err 164 } 165 166 - func GetSpindleMembers(e Execer, filters ...filter) ([]models.SpindleMember, error) { 167 var members []models.SpindleMember 168 169 var conditions []string
··· 7 "time" 8 9 "tangled.org/core/appview/models" 10 + "tangled.org/core/orm" 11 ) 12 13 + func GetSpindles(e Execer, filters ...orm.Filter) ([]models.Spindle, error) { 14 var spindles []models.Spindle 15 16 var conditions []string ··· 92 return err 93 } 94 95 + func VerifySpindle(e Execer, filters ...orm.Filter) (int64, error) { 96 var conditions []string 97 var args []any 98 for _, filter := range filters { ··· 115 return res.RowsAffected() 116 } 117 118 + func DeleteSpindle(e Execer, filters ...orm.Filter) error { 119 var conditions []string 120 var args []any 121 for _, filter := range filters { ··· 145 return err 146 } 147 148 + func RemoveSpindleMember(e Execer, filters ...orm.Filter) error { 149 var conditions []string 150 var args []any 151 for _, filter := range filters { ··· 164 return err 165 } 166 167 + func GetSpindleMembers(e Execer, filters ...orm.Filter) ([]models.SpindleMember, error) { 168 var members []models.SpindleMember 169 170 var conditions []string
+5 -4
appview/db/star.go
··· 11 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 "tangled.org/core/appview/models" 14 ) 15 16 func AddStar(e Execer, star *models.Star) error { ··· 133 134 // GetRepoStars return a list of stars each holding target repository. 135 // If there isn't known repo with starred at-uri, those stars will be ignored. 136 - func GetRepoStars(e Execer, limit int, filters ...filter) ([]models.RepoStar, error) { 137 var conditions []string 138 var args []any 139 for _, filter := range filters { ··· 195 return nil, nil 196 } 197 198 - repos, err := GetRepos(e, 0, FilterIn("at_uri", args)) 199 if err != nil { 200 return nil, err 201 } ··· 225 return repoStars, nil 226 } 227 228 - func CountStars(e Execer, filters ...filter) (int64, error) { 229 var conditions []string 230 var args []any 231 for _, filter := range filters { ··· 298 } 299 300 // get full repo data 301 - repos, err := GetRepos(e, 0, FilterIn("at_uri", repoUris)) 302 if err != nil { 303 return nil, err 304 }
··· 11 12 "github.com/bluesky-social/indigo/atproto/syntax" 13 "tangled.org/core/appview/models" 14 + "tangled.org/core/orm" 15 ) 16 17 func AddStar(e Execer, star *models.Star) error { ··· 134 135 // GetRepoStars return a list of stars each holding target repository. 136 // If there isn't known repo with starred at-uri, those stars will be ignored. 137 + func GetRepoStars(e Execer, limit int, filters ...orm.Filter) ([]models.RepoStar, error) { 138 var conditions []string 139 var args []any 140 for _, filter := range filters { ··· 196 return nil, nil 197 } 198 199 + repos, err := GetRepos(e, 0, orm.FilterIn("at_uri", args)) 200 if err != nil { 201 return nil, err 202 } ··· 226 return repoStars, nil 227 } 228 229 + func CountStars(e Execer, filters ...orm.Filter) (int64, error) { 230 var conditions []string 231 var args []any 232 for _, filter := range filters { ··· 299 } 300 301 // get full repo data 302 + repos, err := GetRepos(e, 0, orm.FilterIn("at_uri", repoUris)) 303 if err != nil { 304 return nil, err 305 }
+4 -3
appview/db/strings.go
··· 8 "time" 9 10 "tangled.org/core/appview/models" 11 ) 12 13 func AddString(e Execer, s models.String) error { ··· 44 return err 45 } 46 47 - func GetStrings(e Execer, limit int, filters ...filter) ([]models.String, error) { 48 var all []models.String 49 50 var conditions []string ··· 127 return all, nil 128 } 129 130 - func CountStrings(e Execer, filters ...filter) (int64, error) { 131 var conditions []string 132 var args []any 133 for _, filter := range filters { ··· 151 return count, nil 152 } 153 154 - func DeleteString(e Execer, filters ...filter) error { 155 var conditions []string 156 var args []any 157 for _, filter := range filters {
··· 8 "time" 9 10 "tangled.org/core/appview/models" 11 + "tangled.org/core/orm" 12 ) 13 14 func AddString(e Execer, s models.String) error { ··· 45 return err 46 } 47 48 + func GetStrings(e Execer, limit int, filters ...orm.Filter) ([]models.String, error) { 49 var all []models.String 50 51 var conditions []string ··· 128 return all, nil 129 } 130 131 + func CountStrings(e Execer, filters ...orm.Filter) (int64, error) { 132 var conditions []string 133 var args []any 134 for _, filter := range filters { ··· 152 return count, nil 153 } 154 155 + func DeleteString(e Execer, filters ...orm.Filter) error { 156 var conditions []string 157 var args []any 158 for _, filter := range filters {
+9 -8
appview/db/timeline.go
··· 5 6 "github.com/bluesky-social/indigo/atproto/syntax" 7 "tangled.org/core/appview/models" 8 ) 9 10 // TODO: this gathers heterogenous events from different sources and aggregates ··· 84 } 85 86 func getTimelineRepos(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) { 87 - filters := make([]filter, 0) 88 if userIsFollowing != nil { 89 - filters = append(filters, FilterIn("did", userIsFollowing)) 90 } 91 92 repos, err := GetRepos(e, limit, filters...) ··· 104 105 var origRepos []models.Repo 106 if args != nil { 107 - origRepos, err = GetRepos(e, 0, FilterIn("at_uri", args)) 108 } 109 if err != nil { 110 return nil, err ··· 144 } 145 146 func getTimelineStars(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) { 147 - filters := make([]filter, 0) 148 if userIsFollowing != nil { 149 - filters = append(filters, FilterIn("did", userIsFollowing)) 150 } 151 152 stars, err := GetRepoStars(e, limit, filters...) ··· 180 } 181 182 func getTimelineFollows(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) { 183 - filters := make([]filter, 0) 184 if userIsFollowing != nil { 185 - filters = append(filters, FilterIn("user_did", userIsFollowing)) 186 } 187 188 follows, err := GetFollows(e, limit, filters...) ··· 199 return nil, nil 200 } 201 202 - profiles, err := GetProfiles(e, FilterIn("did", subjects)) 203 if err != nil { 204 return nil, err 205 }
··· 5 6 "github.com/bluesky-social/indigo/atproto/syntax" 7 "tangled.org/core/appview/models" 8 + "tangled.org/core/orm" 9 ) 10 11 // TODO: this gathers heterogenous events from different sources and aggregates ··· 85 } 86 87 func getTimelineRepos(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) { 88 + filters := make([]orm.Filter, 0) 89 if userIsFollowing != nil { 90 + filters = append(filters, orm.FilterIn("did", userIsFollowing)) 91 } 92 93 repos, err := GetRepos(e, limit, filters...) ··· 105 106 var origRepos []models.Repo 107 if args != nil { 108 + origRepos, err = GetRepos(e, 0, orm.FilterIn("at_uri", args)) 109 } 110 if err != nil { 111 return nil, err ··· 145 } 146 147 func getTimelineStars(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) { 148 + filters := make([]orm.Filter, 0) 149 if userIsFollowing != nil { 150 + filters = append(filters, orm.FilterIn("did", userIsFollowing)) 151 } 152 153 stars, err := GetRepoStars(e, limit, filters...) ··· 181 } 182 183 func getTimelineFollows(e Execer, limit int, loggedInUserDid string, userIsFollowing []string) ([]models.TimelineEvent, error) { 184 + filters := make([]orm.Filter, 0) 185 if userIsFollowing != nil { 186 + filters = append(filters, orm.FilterIn("user_did", userIsFollowing)) 187 } 188 189 follows, err := GetFollows(e, limit, filters...) ··· 200 return nil, nil 201 } 202 203 + profiles, err := GetProfiles(e, orm.FilterIn("did", subjects)) 204 if err != nil { 205 return nil, err 206 }
+31 -24
appview/ingester.go
··· 21 "tangled.org/core/appview/serververify" 22 "tangled.org/core/appview/validator" 23 "tangled.org/core/idresolver" 24 "tangled.org/core/rbac" 25 ) 26 ··· 253 254 err = db.AddArtifact(i.Db, artifact) 255 case jmodels.CommitOperationDelete: 256 - err = db.DeleteArtifact(i.Db, db.FilterEq("did", did), db.FilterEq("rkey", e.Commit.RKey)) 257 } 258 259 if err != nil { ··· 282 if err != nil { 283 l.Error("invalid record", "err", err) 284 return err 285 } 286 287 description := "" ··· 324 325 profile := models.Profile{ 326 Did: did, 327 Description: description, 328 IncludeBluesky: includeBluesky, 329 Location: location, ··· 350 351 err = db.UpsertProfile(tx, &profile) 352 case jmodels.CommitOperationDelete: 353 - err = db.DeleteArtifact(i.Db, db.FilterEq("did", did), db.FilterEq("rkey", e.Commit.RKey)) 354 } 355 356 if err != nil { ··· 424 // get record from db first 425 members, err := db.GetSpindleMembers( 426 ddb, 427 - db.FilterEq("did", did), 428 - db.FilterEq("rkey", rkey), 429 ) 430 if err != nil || len(members) != 1 { 431 return fmt.Errorf("failed to get member: %w, len(members) = %d", err, len(members)) ··· 440 // remove record by rkey && update enforcer 441 if err = db.RemoveSpindleMember( 442 tx, 443 - db.FilterEq("did", did), 444 - db.FilterEq("rkey", rkey), 445 ); err != nil { 446 return fmt.Errorf("failed to remove from db: %w", err) 447 } ··· 523 // get record from db first 524 spindles, err := db.GetSpindles( 525 ddb, 526 - db.FilterEq("owner", did), 527 - db.FilterEq("instance", instance), 528 ) 529 if err != nil || len(spindles) != 1 { 530 return fmt.Errorf("failed to get spindles: %w, len(spindles) = %d", err, len(spindles)) ··· 543 // remove spindle members first 544 err = db.RemoveSpindleMember( 545 tx, 546 - db.FilterEq("owner", did), 547 - db.FilterEq("instance", instance), 548 ) 549 if err != nil { 550 return err ··· 552 553 err = db.DeleteSpindle( 554 tx, 555 - db.FilterEq("owner", did), 556 - db.FilterEq("instance", instance), 557 ) 558 if err != nil { 559 return err ··· 621 case jmodels.CommitOperationDelete: 622 if err := db.DeleteString( 623 ddb, 624 - db.FilterEq("did", did), 625 - db.FilterEq("rkey", rkey), 626 ); err != nil { 627 l.Error("failed to delete", "err", err) 628 return fmt.Errorf("failed to delete string record: %w", err) ··· 740 // get record from db first 741 registrations, err := db.GetRegistrations( 742 ddb, 743 - db.FilterEq("domain", domain), 744 - db.FilterEq("did", did), 745 ) 746 if err != nil { 747 return fmt.Errorf("failed to get registration: %w", err) ··· 762 763 err = db.DeleteKnot( 764 tx, 765 - db.FilterEq("did", did), 766 - db.FilterEq("domain", domain), 767 ) 768 if err != nil { 769 return err ··· 915 case jmodels.CommitOperationDelete: 916 if err := db.DeleteIssueComments( 917 ddb, 918 - db.FilterEq("did", did), 919 - db.FilterEq("rkey", rkey), 920 ); err != nil { 921 return fmt.Errorf("failed to delete issue comment record: %w", err) 922 } ··· 969 case jmodels.CommitOperationDelete: 970 if err := db.DeleteLabelDefinition( 971 ddb, 972 - db.FilterEq("did", did), 973 - db.FilterEq("rkey", rkey), 974 ); err != nil { 975 return fmt.Errorf("failed to delete labeldef record: %w", err) 976 } ··· 1010 var repo *models.Repo 1011 switch collection { 1012 case tangled.RepoIssueNSID: 1013 - i, err := db.GetIssues(ddb, db.FilterEq("at_uri", subject)) 1014 if err != nil || len(i) != 1 { 1015 return fmt.Errorf("failed to find subject: %w || subject count %d", err, len(i)) 1016 } ··· 1019 return fmt.Errorf("unsupport label subject: %s", collection) 1020 } 1021 1022 - actx, err := db.NewLabelApplicationCtx(ddb, db.FilterIn("at_uri", repo.Labels)) 1023 if err != nil { 1024 return fmt.Errorf("failed to build label application ctx: %w", err) 1025 }
··· 21 "tangled.org/core/appview/serververify" 22 "tangled.org/core/appview/validator" 23 "tangled.org/core/idresolver" 24 + "tangled.org/core/orm" 25 "tangled.org/core/rbac" 26 ) 27 ··· 254 255 err = db.AddArtifact(i.Db, artifact) 256 case jmodels.CommitOperationDelete: 257 + err = db.DeleteArtifact(i.Db, orm.FilterEq("did", did), orm.FilterEq("rkey", e.Commit.RKey)) 258 } 259 260 if err != nil { ··· 283 if err != nil { 284 l.Error("invalid record", "err", err) 285 return err 286 + } 287 + 288 + avatar := "" 289 + if record.Avatar != nil { 290 + avatar = record.Avatar.Ref.String() 291 } 292 293 description := "" ··· 330 331 profile := models.Profile{ 332 Did: did, 333 + Avatar: avatar, 334 Description: description, 335 IncludeBluesky: includeBluesky, 336 Location: location, ··· 357 358 err = db.UpsertProfile(tx, &profile) 359 case jmodels.CommitOperationDelete: 360 + err = db.DeleteArtifact(i.Db, orm.FilterEq("did", did), orm.FilterEq("rkey", e.Commit.RKey)) 361 } 362 363 if err != nil { ··· 431 // get record from db first 432 members, err := db.GetSpindleMembers( 433 ddb, 434 + orm.FilterEq("did", did), 435 + orm.FilterEq("rkey", rkey), 436 ) 437 if err != nil || len(members) != 1 { 438 return fmt.Errorf("failed to get member: %w, len(members) = %d", err, len(members)) ··· 447 // remove record by rkey && update enforcer 448 if err = db.RemoveSpindleMember( 449 tx, 450 + orm.FilterEq("did", did), 451 + orm.FilterEq("rkey", rkey), 452 ); err != nil { 453 return fmt.Errorf("failed to remove from db: %w", err) 454 } ··· 530 // get record from db first 531 spindles, err := db.GetSpindles( 532 ddb, 533 + orm.FilterEq("owner", did), 534 + orm.FilterEq("instance", instance), 535 ) 536 if err != nil || len(spindles) != 1 { 537 return fmt.Errorf("failed to get spindles: %w, len(spindles) = %d", err, len(spindles)) ··· 550 // remove spindle members first 551 err = db.RemoveSpindleMember( 552 tx, 553 + orm.FilterEq("owner", did), 554 + orm.FilterEq("instance", instance), 555 ) 556 if err != nil { 557 return err ··· 559 560 err = db.DeleteSpindle( 561 tx, 562 + orm.FilterEq("owner", did), 563 + orm.FilterEq("instance", instance), 564 ) 565 if err != nil { 566 return err ··· 628 case jmodels.CommitOperationDelete: 629 if err := db.DeleteString( 630 ddb, 631 + orm.FilterEq("did", did), 632 + orm.FilterEq("rkey", rkey), 633 ); err != nil { 634 l.Error("failed to delete", "err", err) 635 return fmt.Errorf("failed to delete string record: %w", err) ··· 747 // get record from db first 748 registrations, err := db.GetRegistrations( 749 ddb, 750 + orm.FilterEq("domain", domain), 751 + orm.FilterEq("did", did), 752 ) 753 if err != nil { 754 return fmt.Errorf("failed to get registration: %w", err) ··· 769 770 err = db.DeleteKnot( 771 tx, 772 + orm.FilterEq("did", did), 773 + orm.FilterEq("domain", domain), 774 ) 775 if err != nil { 776 return err ··· 922 case jmodels.CommitOperationDelete: 923 if err := db.DeleteIssueComments( 924 ddb, 925 + orm.FilterEq("did", did), 926 + orm.FilterEq("rkey", rkey), 927 ); err != nil { 928 return fmt.Errorf("failed to delete issue comment record: %w", err) 929 } ··· 976 case jmodels.CommitOperationDelete: 977 if err := db.DeleteLabelDefinition( 978 ddb, 979 + orm.FilterEq("did", did), 980 + orm.FilterEq("rkey", rkey), 981 ); err != nil { 982 return fmt.Errorf("failed to delete labeldef record: %w", err) 983 } ··· 1017 var repo *models.Repo 1018 switch collection { 1019 case tangled.RepoIssueNSID: 1020 + i, err := db.GetIssues(ddb, orm.FilterEq("at_uri", subject)) 1021 if err != nil || len(i) != 1 { 1022 return fmt.Errorf("failed to find subject: %w || subject count %d", err, len(i)) 1023 } ··· 1026 return fmt.Errorf("unsupport label subject: %s", collection) 1027 } 1028 1029 + actx, err := db.NewLabelApplicationCtx(ddb, orm.FilterIn("at_uri", repo.Labels)) 1030 if err != nil { 1031 return fmt.Errorf("failed to build label application ctx: %w", err) 1032 }
+46 -45
appview/issues/issues.go
··· 19 "tangled.org/core/appview/config" 20 "tangled.org/core/appview/db" 21 issues_indexer "tangled.org/core/appview/indexer/issues" 22 "tangled.org/core/appview/models" 23 "tangled.org/core/appview/notify" 24 "tangled.org/core/appview/oauth" 25 "tangled.org/core/appview/pages" 26 "tangled.org/core/appview/pages/repoinfo" 27 "tangled.org/core/appview/pagination" 28 - "tangled.org/core/appview/refresolver" 29 "tangled.org/core/appview/reporesolver" 30 "tangled.org/core/appview/validator" 31 "tangled.org/core/idresolver" 32 "tangled.org/core/rbac" 33 "tangled.org/core/tid" 34 ) 35 36 type Issues struct { 37 - oauth *oauth.OAuth 38 - repoResolver *reporesolver.RepoResolver 39 - enforcer *rbac.Enforcer 40 - pages *pages.Pages 41 - idResolver *idresolver.Resolver 42 - refResolver *refresolver.Resolver 43 - db *db.DB 44 - config *config.Config 45 - notifier notify.Notifier 46 - logger *slog.Logger 47 - validator *validator.Validator 48 - indexer *issues_indexer.Indexer 49 } 50 51 func New( ··· 54 enforcer *rbac.Enforcer, 55 pages *pages.Pages, 56 idResolver *idresolver.Resolver, 57 - refResolver *refresolver.Resolver, 58 db *db.DB, 59 config *config.Config, 60 notifier notify.Notifier, ··· 63 logger *slog.Logger, 64 ) *Issues { 65 return &Issues{ 66 - oauth: oauth, 67 - repoResolver: repoResolver, 68 - enforcer: enforcer, 69 - pages: pages, 70 - idResolver: idResolver, 71 - refResolver: refResolver, 72 - db: db, 73 - config: config, 74 - notifier: notifier, 75 - logger: logger, 76 - validator: validator, 77 - indexer: indexer, 78 } 79 } 80 ··· 113 114 labelDefs, err := db.GetLabelDefinitions( 115 rp.db, 116 - db.FilterIn("at_uri", f.Labels), 117 - db.FilterContains("scope", tangled.RepoIssueNSID), 118 ) 119 if err != nil { 120 l.Error("failed to fetch labels", "err", err) ··· 163 newIssue := issue 164 newIssue.Title = r.FormValue("title") 165 newIssue.Body = r.FormValue("body") 166 - newIssue.Mentions, newIssue.References = rp.refResolver.Resolve(r.Context(), newIssue.Body) 167 168 if err := rp.validator.ValidateIssue(newIssue); err != nil { 169 l.Error("validation error", "err", err) ··· 314 if isIssueOwner || isRepoOwner || isCollaborator { 315 err = db.CloseIssues( 316 rp.db, 317 - db.FilterEq("id", issue.Id), 318 ) 319 if err != nil { 320 l.Error("failed to close issue", "err", err) ··· 361 if isCollaborator || isRepoOwner || isIssueOwner { 362 err := db.ReopenIssues( 363 rp.db, 364 - db.FilterEq("id", issue.Id), 365 ) 366 if err != nil { 367 l.Error("failed to reopen issue", "err", err) ··· 412 replyTo = &replyToUri 413 } 414 415 - mentions, references := rp.refResolver.Resolve(r.Context(), body) 416 417 comment := models.IssueComment{ 418 Did: user.Did, ··· 506 commentId := chi.URLParam(r, "commentId") 507 comments, err := db.GetIssueComments( 508 rp.db, 509 - db.FilterEq("id", commentId), 510 ) 511 if err != nil { 512 l.Error("failed to fetch comment", "id", commentId) ··· 542 commentId := chi.URLParam(r, "commentId") 543 comments, err := db.GetIssueComments( 544 rp.db, 545 - db.FilterEq("id", commentId), 546 ) 547 if err != nil { 548 l.Error("failed to fetch comment", "id", commentId) ··· 584 newComment := comment 585 newComment.Body = newBody 586 newComment.Edited = &now 587 - newComment.Mentions, newComment.References = rp.refResolver.Resolve(r.Context(), newBody) 588 589 record := newComment.AsRecord() 590 ··· 652 commentId := chi.URLParam(r, "commentId") 653 comments, err := db.GetIssueComments( 654 rp.db, 655 - db.FilterEq("id", commentId), 656 ) 657 if err != nil { 658 l.Error("failed to fetch comment", "id", commentId) ··· 688 commentId := chi.URLParam(r, "commentId") 689 comments, err := db.GetIssueComments( 690 rp.db, 691 - db.FilterEq("id", commentId), 692 ) 693 if err != nil { 694 l.Error("failed to fetch comment", "id", commentId) ··· 724 commentId := chi.URLParam(r, "commentId") 725 comments, err := db.GetIssueComments( 726 rp.db, 727 - db.FilterEq("id", commentId), 728 ) 729 if err != nil { 730 l.Error("failed to fetch comment", "id", commentId) ··· 751 752 // optimistic deletion 753 deleted := time.Now() 754 - err = db.DeleteIssueComments(rp.db, db.FilterEq("id", comment.Id)) 755 if err != nil { 756 l.Error("failed to delete comment", "err", err) 757 rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "failed to delete comment") ··· 840 841 issues, err = db.GetIssues( 842 rp.db, 843 - db.FilterIn("id", res.Hits), 844 ) 845 if err != nil { 846 l.Error("failed to get issues", "err", err) ··· 856 issues, err = db.GetIssuesPaginated( 857 rp.db, 858 page, 859 - db.FilterEq("repo_at", f.RepoAt()), 860 - db.FilterEq("open", openInt), 861 ) 862 if err != nil { 863 l.Error("failed to get issues", "err", err) ··· 868 869 labelDefs, err := db.GetLabelDefinitions( 870 rp.db, 871 - db.FilterIn("at_uri", f.Labels), 872 - db.FilterContains("scope", tangled.RepoIssueNSID), 873 ) 874 if err != nil { 875 l.Error("failed to fetch labels", "err", err) ··· 912 }) 913 case http.MethodPost: 914 body := r.FormValue("body") 915 - mentions, references := rp.refResolver.Resolve(r.Context(), body) 916 917 issue := &models.Issue{ 918 RepoAt: f.RepoAt(),
··· 19 "tangled.org/core/appview/config" 20 "tangled.org/core/appview/db" 21 issues_indexer "tangled.org/core/appview/indexer/issues" 22 + "tangled.org/core/appview/mentions" 23 "tangled.org/core/appview/models" 24 "tangled.org/core/appview/notify" 25 "tangled.org/core/appview/oauth" 26 "tangled.org/core/appview/pages" 27 "tangled.org/core/appview/pages/repoinfo" 28 "tangled.org/core/appview/pagination" 29 "tangled.org/core/appview/reporesolver" 30 "tangled.org/core/appview/validator" 31 "tangled.org/core/idresolver" 32 + "tangled.org/core/orm" 33 "tangled.org/core/rbac" 34 "tangled.org/core/tid" 35 ) 36 37 type Issues struct { 38 + oauth *oauth.OAuth 39 + repoResolver *reporesolver.RepoResolver 40 + enforcer *rbac.Enforcer 41 + pages *pages.Pages 42 + idResolver *idresolver.Resolver 43 + mentionsResolver *mentions.Resolver 44 + db *db.DB 45 + config *config.Config 46 + notifier notify.Notifier 47 + logger *slog.Logger 48 + validator *validator.Validator 49 + indexer *issues_indexer.Indexer 50 } 51 52 func New( ··· 55 enforcer *rbac.Enforcer, 56 pages *pages.Pages, 57 idResolver *idresolver.Resolver, 58 + mentionsResolver *mentions.Resolver, 59 db *db.DB, 60 config *config.Config, 61 notifier notify.Notifier, ··· 64 logger *slog.Logger, 65 ) *Issues { 66 return &Issues{ 67 + oauth: oauth, 68 + repoResolver: repoResolver, 69 + enforcer: enforcer, 70 + pages: pages, 71 + idResolver: idResolver, 72 + mentionsResolver: mentionsResolver, 73 + db: db, 74 + config: config, 75 + notifier: notifier, 76 + logger: logger, 77 + validator: validator, 78 + indexer: indexer, 79 } 80 } 81 ··· 114 115 labelDefs, err := db.GetLabelDefinitions( 116 rp.db, 117 + orm.FilterIn("at_uri", f.Labels), 118 + orm.FilterContains("scope", tangled.RepoIssueNSID), 119 ) 120 if err != nil { 121 l.Error("failed to fetch labels", "err", err) ··· 164 newIssue := issue 165 newIssue.Title = r.FormValue("title") 166 newIssue.Body = r.FormValue("body") 167 + newIssue.Mentions, newIssue.References = rp.mentionsResolver.Resolve(r.Context(), newIssue.Body) 168 169 if err := rp.validator.ValidateIssue(newIssue); err != nil { 170 l.Error("validation error", "err", err) ··· 315 if isIssueOwner || isRepoOwner || isCollaborator { 316 err = db.CloseIssues( 317 rp.db, 318 + orm.FilterEq("id", issue.Id), 319 ) 320 if err != nil { 321 l.Error("failed to close issue", "err", err) ··· 362 if isCollaborator || isRepoOwner || isIssueOwner { 363 err := db.ReopenIssues( 364 rp.db, 365 + orm.FilterEq("id", issue.Id), 366 ) 367 if err != nil { 368 l.Error("failed to reopen issue", "err", err) ··· 413 replyTo = &replyToUri 414 } 415 416 + mentions, references := rp.mentionsResolver.Resolve(r.Context(), body) 417 418 comment := models.IssueComment{ 419 Did: user.Did, ··· 507 commentId := chi.URLParam(r, "commentId") 508 comments, err := db.GetIssueComments( 509 rp.db, 510 + orm.FilterEq("id", commentId), 511 ) 512 if err != nil { 513 l.Error("failed to fetch comment", "id", commentId) ··· 543 commentId := chi.URLParam(r, "commentId") 544 comments, err := db.GetIssueComments( 545 rp.db, 546 + orm.FilterEq("id", commentId), 547 ) 548 if err != nil { 549 l.Error("failed to fetch comment", "id", commentId) ··· 585 newComment := comment 586 newComment.Body = newBody 587 newComment.Edited = &now 588 + newComment.Mentions, newComment.References = rp.mentionsResolver.Resolve(r.Context(), newBody) 589 590 record := newComment.AsRecord() 591 ··· 653 commentId := chi.URLParam(r, "commentId") 654 comments, err := db.GetIssueComments( 655 rp.db, 656 + orm.FilterEq("id", commentId), 657 ) 658 if err != nil { 659 l.Error("failed to fetch comment", "id", commentId) ··· 689 commentId := chi.URLParam(r, "commentId") 690 comments, err := db.GetIssueComments( 691 rp.db, 692 + orm.FilterEq("id", commentId), 693 ) 694 if err != nil { 695 l.Error("failed to fetch comment", "id", commentId) ··· 725 commentId := chi.URLParam(r, "commentId") 726 comments, err := db.GetIssueComments( 727 rp.db, 728 + orm.FilterEq("id", commentId), 729 ) 730 if err != nil { 731 l.Error("failed to fetch comment", "id", commentId) ··· 752 753 // optimistic deletion 754 deleted := time.Now() 755 + err = db.DeleteIssueComments(rp.db, orm.FilterEq("id", comment.Id)) 756 if err != nil { 757 l.Error("failed to delete comment", "err", err) 758 rp.pages.Notice(w, fmt.Sprintf("comment-%s-status", commentId), "failed to delete comment") ··· 841 842 issues, err = db.GetIssues( 843 rp.db, 844 + orm.FilterIn("id", res.Hits), 845 ) 846 if err != nil { 847 l.Error("failed to get issues", "err", err) ··· 857 issues, err = db.GetIssuesPaginated( 858 rp.db, 859 page, 860 + orm.FilterEq("repo_at", f.RepoAt()), 861 + orm.FilterEq("open", openInt), 862 ) 863 if err != nil { 864 l.Error("failed to get issues", "err", err) ··· 869 870 labelDefs, err := db.GetLabelDefinitions( 871 rp.db, 872 + orm.FilterIn("at_uri", f.Labels), 873 + orm.FilterContains("scope", tangled.RepoIssueNSID), 874 ) 875 if err != nil { 876 l.Error("failed to fetch labels", "err", err) ··· 913 }) 914 case http.MethodPost: 915 body := r.FormValue("body") 916 + mentions, references := rp.mentionsResolver.Resolve(r.Context(), body) 917 918 issue := &models.Issue{ 919 RepoAt: f.RepoAt(),
+19 -18
appview/knots/knots.go
··· 21 "tangled.org/core/appview/xrpcclient" 22 "tangled.org/core/eventconsumer" 23 "tangled.org/core/idresolver" 24 "tangled.org/core/rbac" 25 "tangled.org/core/tid" 26 ··· 72 user := k.OAuth.GetUser(r) 73 registrations, err := db.GetRegistrations( 74 k.Db, 75 - db.FilterEq("did", user.Did), 76 ) 77 if err != nil { 78 k.Logger.Error("failed to fetch knot registrations", "err", err) ··· 102 103 registrations, err := db.GetRegistrations( 104 k.Db, 105 - db.FilterEq("did", user.Did), 106 - db.FilterEq("domain", domain), 107 ) 108 if err != nil { 109 l.Error("failed to get registrations", "err", err) ··· 127 repos, err := db.GetRepos( 128 k.Db, 129 0, 130 - db.FilterEq("knot", domain), 131 ) 132 if err != nil { 133 l.Error("failed to get knot repos", "err", err) ··· 293 // get record from db first 294 registrations, err := db.GetRegistrations( 295 k.Db, 296 - db.FilterEq("did", user.Did), 297 - db.FilterEq("domain", domain), 298 ) 299 if err != nil { 300 l.Error("failed to get registration", "err", err) ··· 321 322 err = db.DeleteKnot( 323 tx, 324 - db.FilterEq("did", user.Did), 325 - db.FilterEq("domain", domain), 326 ) 327 if err != nil { 328 l.Error("failed to delete registration", "err", err) ··· 402 // get record from db first 403 registrations, err := db.GetRegistrations( 404 k.Db, 405 - db.FilterEq("did", user.Did), 406 - db.FilterEq("domain", domain), 407 ) 408 if err != nil { 409 l.Error("failed to get registration", "err", err) ··· 493 // Get updated registration to show 494 registrations, err = db.GetRegistrations( 495 k.Db, 496 - db.FilterEq("did", user.Did), 497 - db.FilterEq("domain", domain), 498 ) 499 if err != nil { 500 l.Error("failed to get registration", "err", err) ··· 529 530 registrations, err := db.GetRegistrations( 531 k.Db, 532 - db.FilterEq("did", user.Did), 533 - db.FilterEq("domain", domain), 534 - db.FilterIsNot("registered", "null"), 535 ) 536 if err != nil { 537 l.Error("failed to get registration", "err", err) ··· 637 638 registrations, err := db.GetRegistrations( 639 k.Db, 640 - db.FilterEq("did", user.Did), 641 - db.FilterEq("domain", domain), 642 - db.FilterIsNot("registered", "null"), 643 ) 644 if err != nil { 645 l.Error("failed to get registration", "err", err)
··· 21 "tangled.org/core/appview/xrpcclient" 22 "tangled.org/core/eventconsumer" 23 "tangled.org/core/idresolver" 24 + "tangled.org/core/orm" 25 "tangled.org/core/rbac" 26 "tangled.org/core/tid" 27 ··· 73 user := k.OAuth.GetUser(r) 74 registrations, err := db.GetRegistrations( 75 k.Db, 76 + orm.FilterEq("did", user.Did), 77 ) 78 if err != nil { 79 k.Logger.Error("failed to fetch knot registrations", "err", err) ··· 103 104 registrations, err := db.GetRegistrations( 105 k.Db, 106 + orm.FilterEq("did", user.Did), 107 + orm.FilterEq("domain", domain), 108 ) 109 if err != nil { 110 l.Error("failed to get registrations", "err", err) ··· 128 repos, err := db.GetRepos( 129 k.Db, 130 0, 131 + orm.FilterEq("knot", domain), 132 ) 133 if err != nil { 134 l.Error("failed to get knot repos", "err", err) ··· 294 // get record from db first 295 registrations, err := db.GetRegistrations( 296 k.Db, 297 + orm.FilterEq("did", user.Did), 298 + orm.FilterEq("domain", domain), 299 ) 300 if err != nil { 301 l.Error("failed to get registration", "err", err) ··· 322 323 err = db.DeleteKnot( 324 tx, 325 + orm.FilterEq("did", user.Did), 326 + orm.FilterEq("domain", domain), 327 ) 328 if err != nil { 329 l.Error("failed to delete registration", "err", err) ··· 403 // get record from db first 404 registrations, err := db.GetRegistrations( 405 k.Db, 406 + orm.FilterEq("did", user.Did), 407 + orm.FilterEq("domain", domain), 408 ) 409 if err != nil { 410 l.Error("failed to get registration", "err", err) ··· 494 // Get updated registration to show 495 registrations, err = db.GetRegistrations( 496 k.Db, 497 + orm.FilterEq("did", user.Did), 498 + orm.FilterEq("domain", domain), 499 ) 500 if err != nil { 501 l.Error("failed to get registration", "err", err) ··· 530 531 registrations, err := db.GetRegistrations( 532 k.Db, 533 + orm.FilterEq("did", user.Did), 534 + orm.FilterEq("domain", domain), 535 + orm.FilterIsNot("registered", "null"), 536 ) 537 if err != nil { 538 l.Error("failed to get registration", "err", err) ··· 638 639 registrations, err := db.GetRegistrations( 640 k.Db, 641 + orm.FilterEq("did", user.Did), 642 + orm.FilterEq("domain", domain), 643 + orm.FilterIsNot("registered", "null"), 644 ) 645 if err != nil { 646 l.Error("failed to get registration", "err", err)
+5 -4
appview/labels/labels.go
··· 16 "tangled.org/core/appview/oauth" 17 "tangled.org/core/appview/pages" 18 "tangled.org/core/appview/validator" 19 "tangled.org/core/rbac" 20 "tangled.org/core/tid" 21 ··· 88 repoAt := r.Form.Get("repo") 89 subjectUri := r.Form.Get("subject") 90 91 - repo, err := db.GetRepo(l.db, db.FilterEq("at_uri", repoAt)) 92 if err != nil { 93 fail("Failed to get repository.", err) 94 return 95 } 96 97 // find all the labels that this repo subscribes to 98 - repoLabels, err := db.GetRepoLabels(l.db, db.FilterEq("repo_at", repoAt)) 99 if err != nil { 100 fail("Failed to get labels for this repository.", err) 101 return ··· 106 labelAts = append(labelAts, rl.LabelAt.String()) 107 } 108 109 - actx, err := db.NewLabelApplicationCtx(l.db, db.FilterIn("at_uri", labelAts)) 110 if err != nil { 111 fail("Invalid form data.", err) 112 return 113 } 114 115 // calculate the start state by applying already known labels 116 - existingOps, err := db.GetLabelOps(l.db, db.FilterEq("subject", subjectUri)) 117 if err != nil { 118 fail("Invalid form data.", err) 119 return
··· 16 "tangled.org/core/appview/oauth" 17 "tangled.org/core/appview/pages" 18 "tangled.org/core/appview/validator" 19 + "tangled.org/core/orm" 20 "tangled.org/core/rbac" 21 "tangled.org/core/tid" 22 ··· 89 repoAt := r.Form.Get("repo") 90 subjectUri := r.Form.Get("subject") 91 92 + repo, err := db.GetRepo(l.db, orm.FilterEq("at_uri", repoAt)) 93 if err != nil { 94 fail("Failed to get repository.", err) 95 return 96 } 97 98 // find all the labels that this repo subscribes to 99 + repoLabels, err := db.GetRepoLabels(l.db, orm.FilterEq("repo_at", repoAt)) 100 if err != nil { 101 fail("Failed to get labels for this repository.", err) 102 return ··· 107 labelAts = append(labelAts, rl.LabelAt.String()) 108 } 109 110 + actx, err := db.NewLabelApplicationCtx(l.db, orm.FilterIn("at_uri", labelAts)) 111 if err != nil { 112 fail("Invalid form data.", err) 113 return 114 } 115 116 // calculate the start state by applying already known labels 117 + existingOps, err := db.GetLabelOps(l.db, orm.FilterEq("subject", subjectUri)) 118 if err != nil { 119 fail("Invalid form data.", err) 120 return
+67
appview/mentions/resolver.go
···
··· 1 + package mentions 2 + 3 + import ( 4 + "context" 5 + "log/slog" 6 + 7 + "github.com/bluesky-social/indigo/atproto/syntax" 8 + "tangled.org/core/appview/config" 9 + "tangled.org/core/appview/db" 10 + "tangled.org/core/appview/models" 11 + "tangled.org/core/appview/pages/markup" 12 + "tangled.org/core/idresolver" 13 + ) 14 + 15 + type Resolver struct { 16 + config *config.Config 17 + idResolver *idresolver.Resolver 18 + execer db.Execer 19 + logger *slog.Logger 20 + } 21 + 22 + func New( 23 + config *config.Config, 24 + idResolver *idresolver.Resolver, 25 + execer db.Execer, 26 + logger *slog.Logger, 27 + ) *Resolver { 28 + return &Resolver{ 29 + config, 30 + idResolver, 31 + execer, 32 + logger, 33 + } 34 + } 35 + 36 + func (r *Resolver) Resolve(ctx context.Context, source string) ([]syntax.DID, []syntax.ATURI) { 37 + l := r.logger.With("method", "Resolve") 38 + 39 + rawMentions, rawRefs := markup.FindReferences(r.config.Core.AppviewHost, source) 40 + l.Debug("found possible references", "mentions", rawMentions, "refs", rawRefs) 41 + 42 + idents := r.idResolver.ResolveIdents(ctx, rawMentions) 43 + var mentions []syntax.DID 44 + for _, ident := range idents { 45 + if ident != nil && !ident.Handle.IsInvalidHandle() { 46 + mentions = append(mentions, ident.DID) 47 + } 48 + } 49 + l.Debug("found mentions", "mentions", mentions) 50 + 51 + var resolvedRefs []models.ReferenceLink 52 + for _, rawRef := range rawRefs { 53 + ident, err := r.idResolver.ResolveIdent(ctx, rawRef.Handle) 54 + if err != nil || ident == nil || ident.Handle.IsInvalidHandle() { 55 + continue 56 + } 57 + rawRef.Handle = string(ident.DID) 58 + resolvedRefs = append(resolvedRefs, rawRef) 59 + } 60 + aturiRefs, err := db.ValidateReferenceLinks(r.execer, resolvedRefs) 61 + if err != nil { 62 + l.Error("failed running query", "err", err) 63 + } 64 + l.Debug("found references", "refs", aturiRefs) 65 + 66 + return mentions, aturiRefs 67 + }
+3 -2
appview/middleware/middleware.go
··· 18 "tangled.org/core/appview/pagination" 19 "tangled.org/core/appview/reporesolver" 20 "tangled.org/core/idresolver" 21 "tangled.org/core/rbac" 22 ) 23 ··· 217 218 repo, err := db.GetRepo( 219 mw.db, 220 - db.FilterEq("did", id.DID.String()), 221 - db.FilterEq("name", repoName), 222 ) 223 if err != nil { 224 log.Println("failed to resolve repo", "err", err)
··· 18 "tangled.org/core/appview/pagination" 19 "tangled.org/core/appview/reporesolver" 20 "tangled.org/core/idresolver" 21 + "tangled.org/core/orm" 22 "tangled.org/core/rbac" 23 ) 24 ··· 218 219 repo, err := db.GetRepo( 220 mw.db, 221 + orm.FilterEq("did", id.DID.String()), 222 + orm.FilterEq("name", repoName), 223 ) 224 if err != nil { 225 log.Println("failed to resolve repo", "err", err)
+1
appview/models/profile.go
··· 13 Did string 14 15 // data 16 Description string 17 IncludeBluesky bool 18 Location string
··· 13 Did string 14 15 // data 16 + Avatar string // CID of the avatar blob 17 Description string 18 IncludeBluesky bool 19 Location string
+5 -4
appview/notifications/notifications.go
··· 11 "tangled.org/core/appview/oauth" 12 "tangled.org/core/appview/pages" 13 "tangled.org/core/appview/pagination" 14 ) 15 16 type Notifications struct { ··· 53 54 total, err := db.CountNotifications( 55 n.db, 56 - db.FilterEq("recipient_did", user.Did), 57 ) 58 if err != nil { 59 l.Error("failed to get total notifications", "err", err) ··· 64 notifications, err := db.GetNotificationsWithEntities( 65 n.db, 66 page, 67 - db.FilterEq("recipient_did", user.Did), 68 ) 69 if err != nil { 70 l.Error("failed to get notifications", "err", err) ··· 96 97 count, err := db.CountNotifications( 98 n.db, 99 - db.FilterEq("recipient_did", user.Did), 100 - db.FilterEq("read", 0), 101 ) 102 if err != nil { 103 http.Error(w, "Failed to get unread count", http.StatusInternalServerError)
··· 11 "tangled.org/core/appview/oauth" 12 "tangled.org/core/appview/pages" 13 "tangled.org/core/appview/pagination" 14 + "tangled.org/core/orm" 15 ) 16 17 type Notifications struct { ··· 54 55 total, err := db.CountNotifications( 56 n.db, 57 + orm.FilterEq("recipient_did", user.Did), 58 ) 59 if err != nil { 60 l.Error("failed to get total notifications", "err", err) ··· 65 notifications, err := db.GetNotificationsWithEntities( 66 n.db, 67 page, 68 + orm.FilterEq("recipient_did", user.Did), 69 ) 70 if err != nil { 71 l.Error("failed to get notifications", "err", err) ··· 97 98 count, err := db.CountNotifications( 99 n.db, 100 + orm.FilterEq("recipient_did", user.Did), 101 + orm.FilterEq("read", 0), 102 ) 103 if err != nil { 104 http.Error(w, "Failed to get unread count", http.StatusInternalServerError)
+11 -10
appview/notify/db/db.go
··· 12 "tangled.org/core/appview/models" 13 "tangled.org/core/appview/notify" 14 "tangled.org/core/idresolver" 15 ) 16 17 const ( ··· 42 return 43 } 44 var err error 45 - repo, err := db.GetRepo(n.db, db.FilterEq("at_uri", string(star.RepoAt))) 46 if err != nil { 47 log.Printf("NewStar: failed to get repos: %v", err) 48 return ··· 80 // - collaborators in the repo 81 var recipients []syntax.DID 82 recipients = append(recipients, syntax.DID(issue.Repo.Did)) 83 - collaborators, err := db.GetCollaborators(n.db, db.FilterEq("repo_at", issue.Repo.RepoAt())) 84 if err != nil { 85 log.Printf("failed to fetch collaborators: %v", err) 86 return ··· 119 } 120 121 func (n *databaseNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { 122 - issues, err := db.GetIssues(n.db, db.FilterEq("at_uri", comment.IssueAt)) 123 if err != nil { 124 log.Printf("NewIssueComment: failed to get issues: %v", err) 125 return ··· 207 } 208 209 func (n *databaseNotifier) NewPull(ctx context.Context, pull *models.Pull) { 210 - repo, err := db.GetRepo(n.db, db.FilterEq("at_uri", string(pull.RepoAt))) 211 if err != nil { 212 log.Printf("NewPull: failed to get repos: %v", err) 213 return ··· 218 // - collaborators in the repo 219 var recipients []syntax.DID 220 recipients = append(recipients, syntax.DID(repo.Did)) 221 - collaborators, err := db.GetCollaborators(n.db, db.FilterEq("repo_at", repo.RepoAt())) 222 if err != nil { 223 log.Printf("failed to fetch collaborators: %v", err) 224 return ··· 258 return 259 } 260 261 - repo, err := db.GetRepo(n.db, db.FilterEq("at_uri", comment.RepoAt)) 262 if err != nil { 263 log.Printf("NewPullComment: failed to get repos: %v", err) 264 return ··· 327 // - all issue participants 328 var recipients []syntax.DID 329 recipients = append(recipients, syntax.DID(issue.Repo.Did)) 330 - collaborators, err := db.GetCollaborators(n.db, db.FilterEq("repo_at", issue.Repo.RepoAt())) 331 if err != nil { 332 log.Printf("failed to fetch collaborators: %v", err) 333 return ··· 366 367 func (n *databaseNotifier) NewPullState(ctx context.Context, actor syntax.DID, pull *models.Pull) { 368 // Get repo details 369 - repo, err := db.GetRepo(n.db, db.FilterEq("at_uri", string(pull.RepoAt))) 370 if err != nil { 371 log.Printf("NewPullState: failed to get repos: %v", err) 372 return ··· 377 // - all pull participants 378 var recipients []syntax.DID 379 recipients = append(recipients, syntax.DID(repo.Did)) 380 - collaborators, err := db.GetCollaborators(n.db, db.FilterEq("repo_at", repo.RepoAt())) 381 if err != nil { 382 log.Printf("failed to fetch collaborators: %v", err) 383 return ··· 443 444 prefMap, err := db.GetNotificationPreferences( 445 n.db, 446 - db.FilterIn("user_did", slices.Collect(maps.Keys(recipientSet))), 447 ) 448 if err != nil { 449 // failed to get prefs for users
··· 12 "tangled.org/core/appview/models" 13 "tangled.org/core/appview/notify" 14 "tangled.org/core/idresolver" 15 + "tangled.org/core/orm" 16 ) 17 18 const ( ··· 43 return 44 } 45 var err error 46 + repo, err := db.GetRepo(n.db, orm.FilterEq("at_uri", string(star.RepoAt))) 47 if err != nil { 48 log.Printf("NewStar: failed to get repos: %v", err) 49 return ··· 81 // - collaborators in the repo 82 var recipients []syntax.DID 83 recipients = append(recipients, syntax.DID(issue.Repo.Did)) 84 + collaborators, err := db.GetCollaborators(n.db, orm.FilterEq("repo_at", issue.Repo.RepoAt())) 85 if err != nil { 86 log.Printf("failed to fetch collaborators: %v", err) 87 return ··· 120 } 121 122 func (n *databaseNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { 123 + issues, err := db.GetIssues(n.db, orm.FilterEq("at_uri", comment.IssueAt)) 124 if err != nil { 125 log.Printf("NewIssueComment: failed to get issues: %v", err) 126 return ··· 208 } 209 210 func (n *databaseNotifier) NewPull(ctx context.Context, pull *models.Pull) { 211 + repo, err := db.GetRepo(n.db, orm.FilterEq("at_uri", string(pull.RepoAt))) 212 if err != nil { 213 log.Printf("NewPull: failed to get repos: %v", err) 214 return ··· 219 // - collaborators in the repo 220 var recipients []syntax.DID 221 recipients = append(recipients, syntax.DID(repo.Did)) 222 + collaborators, err := db.GetCollaborators(n.db, orm.FilterEq("repo_at", repo.RepoAt())) 223 if err != nil { 224 log.Printf("failed to fetch collaborators: %v", err) 225 return ··· 259 return 260 } 261 262 + repo, err := db.GetRepo(n.db, orm.FilterEq("at_uri", comment.RepoAt)) 263 if err != nil { 264 log.Printf("NewPullComment: failed to get repos: %v", err) 265 return ··· 328 // - all issue participants 329 var recipients []syntax.DID 330 recipients = append(recipients, syntax.DID(issue.Repo.Did)) 331 + collaborators, err := db.GetCollaborators(n.db, orm.FilterEq("repo_at", issue.Repo.RepoAt())) 332 if err != nil { 333 log.Printf("failed to fetch collaborators: %v", err) 334 return ··· 367 368 func (n *databaseNotifier) NewPullState(ctx context.Context, actor syntax.DID, pull *models.Pull) { 369 // Get repo details 370 + repo, err := db.GetRepo(n.db, orm.FilterEq("at_uri", string(pull.RepoAt))) 371 if err != nil { 372 log.Printf("NewPullState: failed to get repos: %v", err) 373 return ··· 378 // - all pull participants 379 var recipients []syntax.DID 380 recipients = append(recipients, syntax.DID(repo.Did)) 381 + collaborators, err := db.GetCollaborators(n.db, orm.FilterEq("repo_at", repo.RepoAt())) 382 if err != nil { 383 log.Printf("failed to fetch collaborators: %v", err) 384 return ··· 444 445 prefMap, err := db.GetNotificationPreferences( 446 n.db, 447 + orm.FilterIn("user_did", slices.Collect(maps.Keys(recipientSet))), 448 ) 449 if err != nil { 450 // failed to get prefs for users
+3 -2
appview/oauth/handler.go
··· 16 "tangled.org/core/api/tangled" 17 "tangled.org/core/appview/db" 18 "tangled.org/core/consts" 19 "tangled.org/core/tid" 20 ) 21 ··· 97 // and create an sh.tangled.spindle.member record with that 98 spindleMembers, err := db.GetSpindleMembers( 99 o.Db, 100 - db.FilterEq("instance", "spindle.tangled.sh"), 101 - db.FilterEq("subject", did), 102 ) 103 if err != nil { 104 l.Error("failed to get spindle members", "err", err)
··· 16 "tangled.org/core/api/tangled" 17 "tangled.org/core/appview/db" 18 "tangled.org/core/consts" 19 + "tangled.org/core/orm" 20 "tangled.org/core/tid" 21 ) 22 ··· 98 // and create an sh.tangled.spindle.member record with that 99 spindleMembers, err := db.GetSpindleMembers( 100 o.Db, 101 + orm.FilterEq("instance", "spindle.tangled.sh"), 102 + orm.FilterEq("subject", did), 103 ) 104 if err != nil { 105 l.Error("failed to get spindle members", "err", err)
+22 -1
appview/pages/funcmap.go
··· 360 "fullAvatar": func(handle string) string { 361 return p.AvatarUrl(handle, "") 362 }, 363 "langColor": enry.GetColor, 364 "layoutSide": func() string { 365 return "col-span-1 md:col-span-2 lg:col-span-3" ··· 399 400 func (p *Pages) AvatarUrl(handle, size string) string { 401 handle = strings.TrimPrefix(handle, "@") 402 - 403 handle = p.resolveDid(handle) 404 405 secret := p.avatar.SharedSecret
··· 360 "fullAvatar": func(handle string) string { 361 return p.AvatarUrl(handle, "") 362 }, 363 + "placeholderAvatar": func(size string) template.HTML { 364 + sizeClass := "size-6" 365 + iconSize := "size-4" 366 + if size == "tiny" { 367 + sizeClass = "size-6" 368 + iconSize = "size-4" 369 + } else if size == "small" { 370 + sizeClass = "size-8" 371 + iconSize = "size-5" 372 + } else { 373 + sizeClass = "size-12" 374 + iconSize = "size-8" 375 + } 376 + icon, _ := p.icon("user-round", []string{iconSize, "text-gray-400", "dark:text-gray-500"}) 377 + return template.HTML(fmt.Sprintf(`<div class="%s rounded-full bg-gray-200 dark:bg-gray-700 flex items-center justify-center flex-shrink-0">%s</div>`, sizeClass, icon)) 378 + }, 379 + "profileAvatarUrl": func(profile *models.Profile, size string) string { 380 + if profile != nil { 381 + return p.AvatarUrl(profile.Did, size) 382 + } 383 + return "" 384 + }, 385 "langColor": enry.GetColor, 386 "layoutSide": func() string { 387 return "col-span-1 md:col-span-2 lg:col-span-3" ··· 421 422 func (p *Pages) AvatarUrl(handle, size string) string { 423 handle = strings.TrimPrefix(handle, "@") 424 handle = p.resolveDid(handle) 425 426 secret := p.avatar.SharedSecret
+1 -1
appview/pages/templates/layouts/profilebase.html
··· 2 3 {{ define "extrameta" }} 4 {{ $handle := resolve .Card.UserDid }} 5 - {{ $avatarUrl := fullAvatar $handle }} 6 <meta property="og:title" content="{{ $handle }}" /> 7 <meta property="og:type" content="profile" /> 8 <meta property="og:url" content="https://tangled.org/{{ $handle }}?tab={{ .Active }}" />
··· 2 3 {{ define "extrameta" }} 4 {{ $handle := resolve .Card.UserDid }} 5 + {{ $avatarUrl := profileAvatarUrl .Card.Profile "" }} 6 <meta property="og:title" content="{{ $handle }}" /> 7 <meta property="og:type" content="profile" /> 8 <meta property="og:url" content="https://tangled.org/{{ $handle }}?tab={{ .Active }}" />
+4 -1
appview/pages/templates/repo/commit.html
··· 100 {{ if $did }} 101 {{ template "user/fragments/picHandleLink" $did }} 102 {{ else }} 103 - <a href="mailto:{{ $email }}" class="no-underline hover:underline text-gray-500 dark:text-gray-300">{{ $name }}</a> 104 {{ end }} 105 {{ end }} 106
··· 100 {{ if $did }} 101 {{ template "user/fragments/picHandleLink" $did }} 102 {{ else }} 103 + <span class="flex items-center gap-1"> 104 + {{ placeholderAvatar "tiny" }} 105 + <a href="mailto:{{ $email }}" class="no-underline hover:underline text-gray-500 dark:text-gray-300">{{ $name }}</a> 106 + </span> 107 {{ end }} 108 {{ end }} 109
+6 -2
appview/pages/templates/repo/index.html
··· 259 {{ define "attribution" }} 260 {{ $commit := index . 0 }} 261 {{ $map := index . 1 }} 262 - <span class="flex items-center"> 263 {{ $author := index $map $commit.Author.Email }} 264 {{ $coauthors := $commit.CoAuthors }} 265 {{ $all := list }} ··· 274 {{ end }} 275 {{ end }} 276 277 - {{ template "fragments/tinyAvatarList" (dict "all" $all "classes" "size-6") }} 278 <a href="{{ if $author }}/{{ $author }}{{ else }}mailto:{{ $commit.Author.Email }}{{ end }}" 279 class="no-underline hover:underline"> 280 {{ if $author }}{{ resolve $author }}{{ else }}{{ $commit.Author.Name }}{{ end }}
··· 259 {{ define "attribution" }} 260 {{ $commit := index . 0 }} 261 {{ $map := index . 1 }} 262 + <span class="flex items-center gap-1"> 263 {{ $author := index $map $commit.Author.Email }} 264 {{ $coauthors := $commit.CoAuthors }} 265 {{ $all := list }} ··· 274 {{ end }} 275 {{ end }} 276 277 + {{ if $author }} 278 + {{ template "fragments/tinyAvatarList" (dict "all" $all "classes" "size-6") }} 279 + {{ else }} 280 + {{ placeholderAvatar "tiny" }} 281 + {{ end }} 282 <a href="{{ if $author }}/{{ $author }}{{ else }}mailto:{{ $commit.Author.Email }}{{ end }}" 283 class="no-underline hover:underline"> 284 {{ if $author }}{{ resolve $author }}{{ else }}{{ $commit.Author.Name }}{{ end }}
+5 -1
appview/pages/templates/repo/log.html
··· 186 {{ end }} 187 {{ end }} 188 189 - {{ template "fragments/tinyAvatarList" (dict "all" $all "classes" "size-6") }} 190 <a href="{{ if $author }}/{{ $author }}{{ else }}mailto:{{ $commit.Author.Email }}{{ end }}" 191 class="no-underline hover:underline"> 192 {{ if $author }}{{ resolve $author }}{{ else }}{{ $commit.Author.Name }}{{ end }}
··· 186 {{ end }} 187 {{ end }} 188 189 + {{ if $author }} 190 + {{ template "fragments/tinyAvatarList" (dict "all" $all "classes" "size-6") }} 191 + {{ else }} 192 + {{ placeholderAvatar "tiny" }} 193 + {{ end }} 194 <a href="{{ if $author }}/{{ $author }}{{ else }}mailto:{{ $commit.Author.Email }}{{ end }}" 195 class="no-underline hover:underline"> 196 {{ if $author }}{{ resolve $author }}{{ else }}{{ $commit.Author.Name }}{{ end }}
+44
appview/pages/templates/user/fragments/editAvatar.html
···
··· 1 + {{ define "user/fragments/editAvatar" }} 2 + <form 3 + hx-post="/profile/avatar" 4 + hx-encoding="multipart/form-data" 5 + hx-indicator="#spinner" 6 + hx-swap="none" 7 + class="flex flex-col gap-2"> 8 + <label for="avatar-file" class="uppercase p-0"> 9 + Upload avatar 10 + </label> 11 + <p class="text-sm text-gray-500 dark:text-gray-400">Select an image (PNG or JPEG, max 1MB)</p> 12 + <input 13 + type="file" 14 + id="avatar-file" 15 + name="avatar" 16 + accept="image/png,image/jpeg" 17 + required 18 + class="block w-full text-sm text-gray-500 dark:text-gray-400 19 + file:mr-4 file:py-2 file:px-4 20 + file:rounded file:border-0 21 + file:text-sm file:font-semibold 22 + file:bg-gray-100 file:text-gray-700 23 + dark:file:bg-gray-700 dark:file:text-gray-300 24 + hover:file:bg-gray-200 dark:hover:file:bg-gray-600" /> 25 + <div class="flex gap-2 pt-2"> 26 + <button 27 + id="cancel-avatar-btn" 28 + type="button" 29 + popovertarget="avatar-upload-modal" 30 + popovertargetaction="hide" 31 + class="btn w-1/2 flex items-center gap-2 text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"> 32 + {{ i "x" "size-4" }} 33 + cancel 34 + </button> 35 + <button type="submit" class="btn w-1/2 flex items-center"> 36 + <span class="inline-flex gap-2 items-center">{{ i "upload" "size-4" }} upload</span> 37 + <span id="spinner" class="group"> 38 + {{ i "loader-circle" "ml-2 w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" }} 39 + </span> 40 + </button> 41 + </div> 42 + <div id="avatar-error" class="text-red-500 dark:text-red-400"></div> 43 + </form> 44 + {{ end }}
+17 -3
appview/pages/templates/user/fragments/profileCard.html
··· 3 <div class="grid grid-cols-3 md:grid-cols-1 gap-1 items-center"> 4 <div id="avatar" class="col-span-1 flex justify-center items-center"> 5 <div class="w-3/4 aspect-square relative"> 6 - <img class="absolute inset-0 w-full h-full object-cover rounded-full p-2" src="{{ fullAvatar .UserDid }}" /> 7 </div> 8 </div> 9 <div class="col-span-2"> 10 <div class="flex items-center flex-row flex-nowrap gap-2"> ··· 36 {{ block "followerFollowing" (list $ $userIdent) }} {{ end }} 37 </div> 38 39 - <div class="flex flex-col gap-2 mb-2 overflow-hidden text-ellipsis whitespace-nowrap max-w-full"> 40 {{ if .Location }} 41 <div class="flex items-center gap-2"> 42 <span class="flex-shrink-0">{{ i "map-pin" "size-4" }}</span> ··· 111 </div> 112 {{ end }} 113 {{ end }} 114 -
··· 3 <div class="grid grid-cols-3 md:grid-cols-1 gap-1 items-center"> 4 <div id="avatar" class="col-span-1 flex justify-center items-center"> 5 <div class="w-3/4 aspect-square relative"> 6 + <img class="absolute inset-0 w-full h-full object-cover rounded-full p-2" src="{{ profileAvatarUrl .Profile "" }}" /> 7 + {{ if eq .FollowStatus.String "IsSelf" }} 8 + <button 9 + class="absolute bottom-2 right-2 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-full p-2 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors" 10 + popovertarget="avatar-upload-modal" 11 + popovertargetaction="toggle" 12 + title="Upload avatar"> 13 + {{ i "camera" "w-4 h-4" }} 14 + </button> 15 + {{ end }} 16 </div> 17 + </div> 18 + <div 19 + id="avatar-upload-modal" 20 + popover 21 + class="bg-white w-full md:w-96 dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 drop-shadow dark:text-white backdrop:bg-gray-400/50 dark:backdrop:bg-gray-800/50"> 22 + {{ template "user/fragments/editAvatar" . }} 23 </div> 24 <div class="col-span-2"> 25 <div class="flex items-center flex-row flex-nowrap gap-2"> ··· 51 {{ block "followerFollowing" (list $ $userIdent) }} {{ end }} 52 </div> 53 54 + <div class="flex flex-col gap-2 mb-2 overflow-hidden text-ellipsis whitespace-nowrap max-w-full"> 55 {{ if .Location }} 56 <div class="flex items-center gap-2"> 57 <span class="flex-shrink-0">{{ i "map-pin" "size-4" }}</span> ··· 126 </div> 127 {{ end }} 128 {{ end }}
+4 -2
appview/pages/templates/user/settings/emails.html
··· 62 hx-swap="none" 63 class="flex flex-col gap-2" 64 > 65 - <p class="uppercase p-0">ADD EMAIL</p> 66 <p class="text-sm text-gray-500 dark:text-gray-400">Commits using this email will be associated with your profile.</p> 67 <input 68 type="email" ··· 91 <div id="settings-emails-error" class="text-red-500 dark:text-red-400"></div> 92 <div id="settings-emails-success" class="text-green-500 dark:text-green-400"></div> 93 </form> 94 - {{ end }}
··· 62 hx-swap="none" 63 class="flex flex-col gap-2" 64 > 65 + <label for="email-address" class="uppercase p-0"> 66 + add email 67 + </label> 68 <p class="text-sm text-gray-500 dark:text-gray-400">Commits using this email will be associated with your profile.</p> 69 <input 70 type="email" ··· 93 <div id="settings-emails-error" class="text-red-500 dark:text-red-400"></div> 94 <div id="settings-emails-success" class="text-green-500 dark:text-green-400"></div> 95 </form> 96 + {{ end }}
+4 -2
appview/pages/templates/user/settings/keys.html
··· 21 <div class="col-span-1 md:col-span-2"> 22 <h2 class="text-sm pb-2 uppercase font-bold">SSH Keys</h2> 23 <p class="text-gray-500 dark:text-gray-400"> 24 - SSH public keys added here will be broadcasted to knots that you are a member of, 25 allowing you to push to repositories there. 26 </p> 27 </div> ··· 63 hx-swap="none" 64 class="flex flex-col gap-2" 65 > 66 - <p class="uppercase p-0">ADD SSH KEY</p> 67 <p class="text-sm text-gray-500 dark:text-gray-400">SSH keys allow you to push to repositories in knots you're a member of.</p> 68 <input 69 type="text"
··· 21 <div class="col-span-1 md:col-span-2"> 22 <h2 class="text-sm pb-2 uppercase font-bold">SSH Keys</h2> 23 <p class="text-gray-500 dark:text-gray-400"> 24 + SSH public keys added here will be broadcasted to knots that you are a member of, 25 allowing you to push to repositories there. 26 </p> 27 </div> ··· 63 hx-swap="none" 64 class="flex flex-col gap-2" 65 > 66 + <label for="key-name" class="uppercase p-0"> 67 + add ssh key 68 + </label> 69 <p class="text-sm text-gray-500 dark:text-gray-400">SSH keys allow you to push to repositories in knots you're a member of.</p> 70 <input 71 type="text"
+12 -11
appview/pipelines/pipelines.go
··· 16 "tangled.org/core/appview/reporesolver" 17 "tangled.org/core/eventconsumer" 18 "tangled.org/core/idresolver" 19 "tangled.org/core/rbac" 20 spindlemodel "tangled.org/core/spindle/models" 21 ··· 81 ps, err := db.GetPipelineStatuses( 82 p.db, 83 30, 84 - db.FilterEq("repo_owner", f.Did), 85 - db.FilterEq("repo_name", f.Name), 86 - db.FilterEq("knot", f.Knot), 87 ) 88 if err != nil { 89 l.Error("failed to query db", "err", err) ··· 122 ps, err := db.GetPipelineStatuses( 123 p.db, 124 1, 125 - db.FilterEq("repo_owner", f.Did), 126 - db.FilterEq("repo_name", f.Name), 127 - db.FilterEq("knot", f.Knot), 128 - db.FilterEq("id", pipelineId), 129 ) 130 if err != nil { 131 l.Error("failed to query db", "err", err) ··· 189 ps, err := db.GetPipelineStatuses( 190 p.db, 191 1, 192 - db.FilterEq("repo_owner", f.Did), 193 - db.FilterEq("repo_name", f.Name), 194 - db.FilterEq("knot", f.Knot), 195 - db.FilterEq("id", pipelineId), 196 ) 197 if err != nil || len(ps) != 1 { 198 l.Error("pipeline query failed", "err", err, "count", len(ps))
··· 16 "tangled.org/core/appview/reporesolver" 17 "tangled.org/core/eventconsumer" 18 "tangled.org/core/idresolver" 19 + "tangled.org/core/orm" 20 "tangled.org/core/rbac" 21 spindlemodel "tangled.org/core/spindle/models" 22 ··· 82 ps, err := db.GetPipelineStatuses( 83 p.db, 84 30, 85 + orm.FilterEq("repo_owner", f.Did), 86 + orm.FilterEq("repo_name", f.Name), 87 + orm.FilterEq("knot", f.Knot), 88 ) 89 if err != nil { 90 l.Error("failed to query db", "err", err) ··· 123 ps, err := db.GetPipelineStatuses( 124 p.db, 125 1, 126 + orm.FilterEq("repo_owner", f.Did), 127 + orm.FilterEq("repo_name", f.Name), 128 + orm.FilterEq("knot", f.Knot), 129 + orm.FilterEq("id", pipelineId), 130 ) 131 if err != nil { 132 l.Error("failed to query db", "err", err) ··· 190 ps, err := db.GetPipelineStatuses( 191 p.db, 192 1, 193 + orm.FilterEq("repo_owner", f.Did), 194 + orm.FilterEq("repo_name", f.Name), 195 + orm.FilterEq("knot", f.Knot), 196 + orm.FilterEq("id", pipelineId), 197 ) 198 if err != nil || len(ps) != 1 { 199 l.Error("pipeline query failed", "err", err, "count", len(ps))
+2 -1
appview/pulls/opengraph.go
··· 13 "tangled.org/core/appview/db" 14 "tangled.org/core/appview/models" 15 "tangled.org/core/appview/ogcard" 16 "tangled.org/core/patchutil" 17 "tangled.org/core/types" 18 ) ··· 276 } 277 278 // Get comment count from database 279 - comments, err := db.GetPullComments(s.db, db.FilterEq("pull_id", pull.ID)) 280 if err != nil { 281 log.Printf("failed to get pull comments: %v", err) 282 }
··· 13 "tangled.org/core/appview/db" 14 "tangled.org/core/appview/models" 15 "tangled.org/core/appview/ogcard" 16 + "tangled.org/core/orm" 17 "tangled.org/core/patchutil" 18 "tangled.org/core/types" 19 ) ··· 277 } 278 279 // Get comment count from database 280 + comments, err := db.GetPullComments(s.db, orm.FilterEq("pull_id", pull.ID)) 281 if err != nil { 282 log.Printf("failed to get pull comments: %v", err) 283 }
+48 -47
appview/pulls/pulls.go
··· 19 "tangled.org/core/appview/config" 20 "tangled.org/core/appview/db" 21 pulls_indexer "tangled.org/core/appview/indexer/pulls" 22 "tangled.org/core/appview/models" 23 "tangled.org/core/appview/notify" 24 "tangled.org/core/appview/oauth" 25 "tangled.org/core/appview/pages" 26 "tangled.org/core/appview/pages/markup" 27 "tangled.org/core/appview/pages/repoinfo" 28 - "tangled.org/core/appview/refresolver" 29 "tangled.org/core/appview/reporesolver" 30 "tangled.org/core/appview/validator" 31 "tangled.org/core/appview/xrpcclient" 32 "tangled.org/core/idresolver" 33 "tangled.org/core/patchutil" 34 "tangled.org/core/rbac" 35 "tangled.org/core/tid" ··· 44 ) 45 46 type Pulls struct { 47 - oauth *oauth.OAuth 48 - repoResolver *reporesolver.RepoResolver 49 - pages *pages.Pages 50 - idResolver *idresolver.Resolver 51 - refResolver *refresolver.Resolver 52 - db *db.DB 53 - config *config.Config 54 - notifier notify.Notifier 55 - enforcer *rbac.Enforcer 56 - logger *slog.Logger 57 - validator *validator.Validator 58 - indexer *pulls_indexer.Indexer 59 } 60 61 func New( ··· 63 repoResolver *reporesolver.RepoResolver, 64 pages *pages.Pages, 65 resolver *idresolver.Resolver, 66 - refResolver *refresolver.Resolver, 67 db *db.DB, 68 config *config.Config, 69 notifier notify.Notifier, ··· 73 logger *slog.Logger, 74 ) *Pulls { 75 return &Pulls{ 76 - oauth: oauth, 77 - repoResolver: repoResolver, 78 - pages: pages, 79 - idResolver: resolver, 80 - refResolver: refResolver, 81 - db: db, 82 - config: config, 83 - notifier: notifier, 84 - enforcer: enforcer, 85 - logger: logger, 86 - validator: validator, 87 - indexer: indexer, 88 } 89 } 90 ··· 190 ps, err := db.GetPipelineStatuses( 191 s.db, 192 len(shas), 193 - db.FilterEq("repo_owner", f.Did), 194 - db.FilterEq("repo_name", f.Name), 195 - db.FilterEq("knot", f.Knot), 196 - db.FilterIn("sha", shas), 197 ) 198 if err != nil { 199 log.Printf("failed to fetch pipeline statuses: %s", err) ··· 217 218 labelDefs, err := db.GetLabelDefinitions( 219 s.db, 220 - db.FilterIn("at_uri", f.Labels), 221 - db.FilterContains("scope", tangled.RepoPullNSID), 222 ) 223 if err != nil { 224 log.Println("failed to fetch labels", err) ··· 597 598 pulls, err := db.GetPulls( 599 s.db, 600 - db.FilterIn("id", ids), 601 ) 602 if err != nil { 603 log.Println("failed to get pulls", err) ··· 648 ps, err := db.GetPipelineStatuses( 649 s.db, 650 len(shas), 651 - db.FilterEq("repo_owner", f.Did), 652 - db.FilterEq("repo_name", f.Name), 653 - db.FilterEq("knot", f.Knot), 654 - db.FilterIn("sha", shas), 655 ) 656 if err != nil { 657 log.Printf("failed to fetch pipeline statuses: %s", err) ··· 664 665 labelDefs, err := db.GetLabelDefinitions( 666 s.db, 667 - db.FilterIn("at_uri", f.Labels), 668 - db.FilterContains("scope", tangled.RepoPullNSID), 669 ) 670 if err != nil { 671 log.Println("failed to fetch labels", err) ··· 729 return 730 } 731 732 - mentions, references := s.refResolver.Resolve(r.Context(), body) 733 734 // Start a transaction 735 tx, err := s.db.BeginTx(r.Context(), nil) ··· 1205 } 1206 } 1207 1208 - mentions, references := s.refResolver.Resolve(r.Context(), body) 1209 1210 rkey := tid.TID() 1211 initialSubmission := models.PullSubmission{ ··· 1498 // fork repo 1499 repo, err := db.GetRepo( 1500 s.db, 1501 - db.FilterEq("did", forkOwnerDid), 1502 - db.FilterEq("name", forkName), 1503 ) 1504 if err != nil { 1505 log.Println("failed to get repo", "did", forkOwnerDid, "name", forkName, "err", err) ··· 2066 tx, 2067 p.ParentChangeId, 2068 // these should be enough filters to be unique per-stack 2069 - db.FilterEq("repo_at", p.RepoAt.String()), 2070 - db.FilterEq("owner_did", p.OwnerDid), 2071 - db.FilterEq("change_id", p.ChangeId), 2072 ) 2073 2074 if err != nil { ··· 2397 body := fp.Body 2398 rkey := tid.TID() 2399 2400 - mentions, references := s.refResolver.Resolve(ctx, body) 2401 2402 initialSubmission := models.PullSubmission{ 2403 Patch: fp.Raw,
··· 19 "tangled.org/core/appview/config" 20 "tangled.org/core/appview/db" 21 pulls_indexer "tangled.org/core/appview/indexer/pulls" 22 + "tangled.org/core/appview/mentions" 23 "tangled.org/core/appview/models" 24 "tangled.org/core/appview/notify" 25 "tangled.org/core/appview/oauth" 26 "tangled.org/core/appview/pages" 27 "tangled.org/core/appview/pages/markup" 28 "tangled.org/core/appview/pages/repoinfo" 29 "tangled.org/core/appview/reporesolver" 30 "tangled.org/core/appview/validator" 31 "tangled.org/core/appview/xrpcclient" 32 "tangled.org/core/idresolver" 33 + "tangled.org/core/orm" 34 "tangled.org/core/patchutil" 35 "tangled.org/core/rbac" 36 "tangled.org/core/tid" ··· 45 ) 46 47 type Pulls struct { 48 + oauth *oauth.OAuth 49 + repoResolver *reporesolver.RepoResolver 50 + pages *pages.Pages 51 + idResolver *idresolver.Resolver 52 + mentionsResolver *mentions.Resolver 53 + db *db.DB 54 + config *config.Config 55 + notifier notify.Notifier 56 + enforcer *rbac.Enforcer 57 + logger *slog.Logger 58 + validator *validator.Validator 59 + indexer *pulls_indexer.Indexer 60 } 61 62 func New( ··· 64 repoResolver *reporesolver.RepoResolver, 65 pages *pages.Pages, 66 resolver *idresolver.Resolver, 67 + mentionsResolver *mentions.Resolver, 68 db *db.DB, 69 config *config.Config, 70 notifier notify.Notifier, ··· 74 logger *slog.Logger, 75 ) *Pulls { 76 return &Pulls{ 77 + oauth: oauth, 78 + repoResolver: repoResolver, 79 + pages: pages, 80 + idResolver: resolver, 81 + mentionsResolver: mentionsResolver, 82 + db: db, 83 + config: config, 84 + notifier: notifier, 85 + enforcer: enforcer, 86 + logger: logger, 87 + validator: validator, 88 + indexer: indexer, 89 } 90 } 91 ··· 191 ps, err := db.GetPipelineStatuses( 192 s.db, 193 len(shas), 194 + orm.FilterEq("repo_owner", f.Did), 195 + orm.FilterEq("repo_name", f.Name), 196 + orm.FilterEq("knot", f.Knot), 197 + orm.FilterIn("sha", shas), 198 ) 199 if err != nil { 200 log.Printf("failed to fetch pipeline statuses: %s", err) ··· 218 219 labelDefs, err := db.GetLabelDefinitions( 220 s.db, 221 + orm.FilterIn("at_uri", f.Labels), 222 + orm.FilterContains("scope", tangled.RepoPullNSID), 223 ) 224 if err != nil { 225 log.Println("failed to fetch labels", err) ··· 598 599 pulls, err := db.GetPulls( 600 s.db, 601 + orm.FilterIn("id", ids), 602 ) 603 if err != nil { 604 log.Println("failed to get pulls", err) ··· 649 ps, err := db.GetPipelineStatuses( 650 s.db, 651 len(shas), 652 + orm.FilterEq("repo_owner", f.Did), 653 + orm.FilterEq("repo_name", f.Name), 654 + orm.FilterEq("knot", f.Knot), 655 + orm.FilterIn("sha", shas), 656 ) 657 if err != nil { 658 log.Printf("failed to fetch pipeline statuses: %s", err) ··· 665 666 labelDefs, err := db.GetLabelDefinitions( 667 s.db, 668 + orm.FilterIn("at_uri", f.Labels), 669 + orm.FilterContains("scope", tangled.RepoPullNSID), 670 ) 671 if err != nil { 672 log.Println("failed to fetch labels", err) ··· 730 return 731 } 732 733 + mentions, references := s.mentionsResolver.Resolve(r.Context(), body) 734 735 // Start a transaction 736 tx, err := s.db.BeginTx(r.Context(), nil) ··· 1206 } 1207 } 1208 1209 + mentions, references := s.mentionsResolver.Resolve(r.Context(), body) 1210 1211 rkey := tid.TID() 1212 initialSubmission := models.PullSubmission{ ··· 1499 // fork repo 1500 repo, err := db.GetRepo( 1501 s.db, 1502 + orm.FilterEq("did", forkOwnerDid), 1503 + orm.FilterEq("name", forkName), 1504 ) 1505 if err != nil { 1506 log.Println("failed to get repo", "did", forkOwnerDid, "name", forkName, "err", err) ··· 2067 tx, 2068 p.ParentChangeId, 2069 // these should be enough filters to be unique per-stack 2070 + orm.FilterEq("repo_at", p.RepoAt.String()), 2071 + orm.FilterEq("owner_did", p.OwnerDid), 2072 + orm.FilterEq("change_id", p.ChangeId), 2073 ) 2074 2075 if err != nil { ··· 2398 body := fp.Body 2399 rkey := tid.TID() 2400 2401 + mentions, references := s.mentionsResolver.Resolve(ctx, body) 2402 2403 initialSubmission := models.PullSubmission{ 2404 Patch: fp.Raw,
-65
appview/refresolver/resolver.go
··· 1 - package refresolver 2 - 3 - import ( 4 - "context" 5 - "log/slog" 6 - 7 - "github.com/bluesky-social/indigo/atproto/syntax" 8 - "tangled.org/core/appview/config" 9 - "tangled.org/core/appview/db" 10 - "tangled.org/core/appview/models" 11 - "tangled.org/core/appview/pages/markup" 12 - "tangled.org/core/idresolver" 13 - ) 14 - 15 - type Resolver struct { 16 - config *config.Config 17 - idResolver *idresolver.Resolver 18 - execer db.Execer 19 - logger *slog.Logger 20 - } 21 - 22 - func New( 23 - config *config.Config, 24 - idResolver *idresolver.Resolver, 25 - execer db.Execer, 26 - logger *slog.Logger, 27 - ) *Resolver { 28 - return &Resolver{ 29 - config, 30 - idResolver, 31 - execer, 32 - logger, 33 - } 34 - } 35 - 36 - func (r *Resolver) Resolve(ctx context.Context, source string) ([]syntax.DID, []syntax.ATURI) { 37 - l := r.logger.With("method", "Resolve") 38 - rawMentions, rawRefs := markup.FindReferences(r.config.Core.AppviewHost, source) 39 - l.Debug("found possible references", "mentions", rawMentions, "refs", rawRefs) 40 - idents := r.idResolver.ResolveIdents(ctx, rawMentions) 41 - var mentions []syntax.DID 42 - for _, ident := range idents { 43 - if ident != nil && !ident.Handle.IsInvalidHandle() { 44 - mentions = append(mentions, ident.DID) 45 - } 46 - } 47 - l.Debug("found mentions", "mentions", mentions) 48 - 49 - var resolvedRefs []models.ReferenceLink 50 - for _, rawRef := range rawRefs { 51 - ident, err := r.idResolver.ResolveIdent(ctx, rawRef.Handle) 52 - if err != nil || ident == nil || ident.Handle.IsInvalidHandle() { 53 - continue 54 - } 55 - rawRef.Handle = string(ident.DID) 56 - resolvedRefs = append(resolvedRefs, rawRef) 57 - } 58 - aturiRefs, err := db.ValidateReferenceLinks(r.execer, resolvedRefs) 59 - if err != nil { 60 - l.Error("failed running query", "err", err) 61 - } 62 - l.Debug("found references", "refs", aturiRefs) 63 - 64 - return mentions, aturiRefs 65 - }
···
+10 -9
appview/repo/artifact.go
··· 15 "tangled.org/core/appview/models" 16 "tangled.org/core/appview/pages" 17 "tangled.org/core/appview/xrpcclient" 18 "tangled.org/core/tid" 19 "tangled.org/core/types" 20 ··· 155 156 artifacts, err := db.GetArtifact( 157 rp.db, 158 - db.FilterEq("repo_at", f.RepoAt()), 159 - db.FilterEq("tag", tag.Tag.Hash[:]), 160 - db.FilterEq("name", filename), 161 ) 162 if err != nil { 163 log.Println("failed to get artifacts", err) ··· 234 235 artifacts, err := db.GetArtifact( 236 rp.db, 237 - db.FilterEq("repo_at", f.RepoAt()), 238 - db.FilterEq("tag", tag[:]), 239 - db.FilterEq("name", filename), 240 ) 241 if err != nil { 242 log.Println("failed to get artifacts", err) ··· 276 defer tx.Rollback() 277 278 err = db.DeleteArtifact(tx, 279 - db.FilterEq("repo_at", f.RepoAt()), 280 - db.FilterEq("tag", artifact.Tag[:]), 281 - db.FilterEq("name", filename), 282 ) 283 if err != nil { 284 log.Println("failed to remove artifact record from db", err)
··· 15 "tangled.org/core/appview/models" 16 "tangled.org/core/appview/pages" 17 "tangled.org/core/appview/xrpcclient" 18 + "tangled.org/core/orm" 19 "tangled.org/core/tid" 20 "tangled.org/core/types" 21 ··· 156 157 artifacts, err := db.GetArtifact( 158 rp.db, 159 + orm.FilterEq("repo_at", f.RepoAt()), 160 + orm.FilterEq("tag", tag.Tag.Hash[:]), 161 + orm.FilterEq("name", filename), 162 ) 163 if err != nil { 164 log.Println("failed to get artifacts", err) ··· 235 236 artifacts, err := db.GetArtifact( 237 rp.db, 238 + orm.FilterEq("repo_at", f.RepoAt()), 239 + orm.FilterEq("tag", tag[:]), 240 + orm.FilterEq("name", filename), 241 ) 242 if err != nil { 243 log.Println("failed to get artifacts", err) ··· 277 defer tx.Rollback() 278 279 err = db.DeleteArtifact(tx, 280 + orm.FilterEq("repo_at", f.RepoAt()), 281 + orm.FilterEq("tag", artifact.Tag[:]), 282 + orm.FilterEq("name", filename), 283 ) 284 if err != nil { 285 log.Println("failed to remove artifact record from db", err)
+3 -2
appview/repo/feed.go
··· 11 "tangled.org/core/appview/db" 12 "tangled.org/core/appview/models" 13 "tangled.org/core/appview/pagination" 14 15 "github.com/bluesky-social/indigo/atproto/identity" 16 "github.com/bluesky-social/indigo/atproto/syntax" ··· 20 func (rp *Repo) getRepoFeed(ctx context.Context, repo *models.Repo, ownerSlashRepo string) (*feeds.Feed, error) { 21 const feedLimitPerType = 100 22 23 - pulls, err := db.GetPullsWithLimit(rp.db, feedLimitPerType, db.FilterEq("repo_at", repo.RepoAt())) 24 if err != nil { 25 return nil, err 26 } ··· 28 issues, err := db.GetIssuesPaginated( 29 rp.db, 30 pagination.Page{Limit: feedLimitPerType}, 31 - db.FilterEq("repo_at", repo.RepoAt()), 32 ) 33 if err != nil { 34 return nil, err
··· 11 "tangled.org/core/appview/db" 12 "tangled.org/core/appview/models" 13 "tangled.org/core/appview/pagination" 14 + "tangled.org/core/orm" 15 16 "github.com/bluesky-social/indigo/atproto/identity" 17 "github.com/bluesky-social/indigo/atproto/syntax" ··· 21 func (rp *Repo) getRepoFeed(ctx context.Context, repo *models.Repo, ownerSlashRepo string) (*feeds.Feed, error) { 22 const feedLimitPerType = 100 23 24 + pulls, err := db.GetPullsWithLimit(rp.db, feedLimitPerType, orm.FilterEq("repo_at", repo.RepoAt())) 25 if err != nil { 26 return nil, err 27 } ··· 29 issues, err := db.GetIssuesPaginated( 30 rp.db, 31 pagination.Page{Limit: feedLimitPerType}, 32 + orm.FilterEq("repo_at", repo.RepoAt()), 33 ) 34 if err != nil { 35 return nil, err
+3 -2
appview/repo/index.go
··· 23 "tangled.org/core/appview/models" 24 "tangled.org/core/appview/pages" 25 "tangled.org/core/appview/xrpcclient" 26 "tangled.org/core/types" 27 28 "github.com/go-chi/chi/v5" ··· 171 // first attempt to fetch from db 172 langs, err := db.GetRepoLanguages( 173 rp.db, 174 - db.FilterEq("repo_at", repo.RepoAt()), 175 - db.FilterEq("ref", currentRef), 176 ) 177 178 if err != nil || langs == nil {
··· 23 "tangled.org/core/appview/models" 24 "tangled.org/core/appview/pages" 25 "tangled.org/core/appview/xrpcclient" 26 + "tangled.org/core/orm" 27 "tangled.org/core/types" 28 29 "github.com/go-chi/chi/v5" ··· 172 // first attempt to fetch from db 173 langs, err := db.GetRepoLanguages( 174 rp.db, 175 + orm.FilterEq("repo_at", repo.RepoAt()), 176 + orm.FilterEq("ref", currentRef), 177 ) 178 179 if err != nil || langs == nil {
+3 -2
appview/repo/opengraph.go
··· 16 "tangled.org/core/appview/db" 17 "tangled.org/core/appview/models" 18 "tangled.org/core/appview/ogcard" 19 "tangled.org/core/types" 20 ) 21 ··· 338 var languageStats []types.RepoLanguageDetails 339 langs, err := db.GetRepoLanguages( 340 rp.db, 341 - db.FilterEq("repo_at", f.RepoAt()), 342 - db.FilterEq("is_default_ref", 1), 343 ) 344 if err != nil { 345 log.Printf("failed to get language stats from db: %v", err)
··· 16 "tangled.org/core/appview/db" 17 "tangled.org/core/appview/models" 18 "tangled.org/core/appview/ogcard" 19 + "tangled.org/core/orm" 20 "tangled.org/core/types" 21 ) 22 ··· 339 var languageStats []types.RepoLanguageDetails 340 langs, err := db.GetRepoLanguages( 341 rp.db, 342 + orm.FilterEq("repo_at", f.RepoAt()), 343 + orm.FilterEq("is_default_ref", 1), 344 ) 345 if err != nil { 346 log.Printf("failed to get language stats from db: %v", err)
+17 -16
appview/repo/repo.go
··· 24 xrpcclient "tangled.org/core/appview/xrpcclient" 25 "tangled.org/core/eventconsumer" 26 "tangled.org/core/idresolver" 27 "tangled.org/core/rbac" 28 "tangled.org/core/tid" 29 "tangled.org/core/xrpc/serviceauth" ··· 345 // get form values 346 labelId := r.FormValue("label-id") 347 348 - label, err := db.GetLabelDefinition(rp.db, db.FilterEq("id", labelId)) 349 if err != nil { 350 fail("Failed to find label definition.", err) 351 return ··· 409 410 err = db.UnsubscribeLabel( 411 tx, 412 - db.FilterEq("repo_at", f.RepoAt()), 413 - db.FilterEq("label_at", removedAt), 414 ) 415 if err != nil { 416 fail("Failed to unsubscribe label.", err) 417 return 418 } 419 420 - err = db.DeleteLabelDefinition(tx, db.FilterEq("id", label.Id)) 421 if err != nil { 422 fail("Failed to delete label definition.", err) 423 return ··· 456 } 457 458 labelAts := r.Form["label"] 459 - _, err = db.GetLabelDefinitions(rp.db, db.FilterIn("at_uri", labelAts)) 460 if err != nil { 461 fail("Failed to subscribe to label.", err) 462 return ··· 542 } 543 544 labelAts := r.Form["label"] 545 - _, err = db.GetLabelDefinitions(rp.db, db.FilterIn("at_uri", labelAts)) 546 if err != nil { 547 fail("Failed to unsubscribe to label.", err) 548 return ··· 582 583 err = db.UnsubscribeLabel( 584 rp.db, 585 - db.FilterEq("repo_at", f.RepoAt()), 586 - db.FilterIn("label_at", labelAts), 587 ) 588 if err != nil { 589 fail("Failed to unsubscribe label.", err) ··· 612 613 labelDefs, err := db.GetLabelDefinitions( 614 rp.db, 615 - db.FilterIn("at_uri", f.Labels), 616 - db.FilterContains("scope", subject.Collection().String()), 617 ) 618 if err != nil { 619 l.Error("failed to fetch label defs", "err", err) ··· 625 defs[l.AtUri().String()] = &l 626 } 627 628 - states, err := db.GetLabels(rp.db, db.FilterEq("subject", subject)) 629 if err != nil { 630 l.Error("failed to build label state", "err", err) 631 return ··· 660 661 labelDefs, err := db.GetLabelDefinitions( 662 rp.db, 663 - db.FilterIn("at_uri", f.Labels), 664 - db.FilterContains("scope", subject.Collection().String()), 665 ) 666 if err != nil { 667 l.Error("failed to fetch labels", "err", err) ··· 673 defs[l.AtUri().String()] = &l 674 } 675 676 - states, err := db.GetLabels(rp.db, db.FilterEq("subject", subject)) 677 if err != nil { 678 l.Error("failed to build label state", "err", err) 679 return ··· 1036 // in the user's account. 1037 existingRepo, err := db.GetRepo( 1038 rp.db, 1039 - db.FilterEq("did", user.Did), 1040 - db.FilterEq("name", forkName), 1041 ) 1042 if err != nil { 1043 if !errors.Is(err, sql.ErrNoRows) {
··· 24 xrpcclient "tangled.org/core/appview/xrpcclient" 25 "tangled.org/core/eventconsumer" 26 "tangled.org/core/idresolver" 27 + "tangled.org/core/orm" 28 "tangled.org/core/rbac" 29 "tangled.org/core/tid" 30 "tangled.org/core/xrpc/serviceauth" ··· 346 // get form values 347 labelId := r.FormValue("label-id") 348 349 + label, err := db.GetLabelDefinition(rp.db, orm.FilterEq("id", labelId)) 350 if err != nil { 351 fail("Failed to find label definition.", err) 352 return ··· 410 411 err = db.UnsubscribeLabel( 412 tx, 413 + orm.FilterEq("repo_at", f.RepoAt()), 414 + orm.FilterEq("label_at", removedAt), 415 ) 416 if err != nil { 417 fail("Failed to unsubscribe label.", err) 418 return 419 } 420 421 + err = db.DeleteLabelDefinition(tx, orm.FilterEq("id", label.Id)) 422 if err != nil { 423 fail("Failed to delete label definition.", err) 424 return ··· 457 } 458 459 labelAts := r.Form["label"] 460 + _, err = db.GetLabelDefinitions(rp.db, orm.FilterIn("at_uri", labelAts)) 461 if err != nil { 462 fail("Failed to subscribe to label.", err) 463 return ··· 543 } 544 545 labelAts := r.Form["label"] 546 + _, err = db.GetLabelDefinitions(rp.db, orm.FilterIn("at_uri", labelAts)) 547 if err != nil { 548 fail("Failed to unsubscribe to label.", err) 549 return ··· 583 584 err = db.UnsubscribeLabel( 585 rp.db, 586 + orm.FilterEq("repo_at", f.RepoAt()), 587 + orm.FilterIn("label_at", labelAts), 588 ) 589 if err != nil { 590 fail("Failed to unsubscribe label.", err) ··· 613 614 labelDefs, err := db.GetLabelDefinitions( 615 rp.db, 616 + orm.FilterIn("at_uri", f.Labels), 617 + orm.FilterContains("scope", subject.Collection().String()), 618 ) 619 if err != nil { 620 l.Error("failed to fetch label defs", "err", err) ··· 626 defs[l.AtUri().String()] = &l 627 } 628 629 + states, err := db.GetLabels(rp.db, orm.FilterEq("subject", subject)) 630 if err != nil { 631 l.Error("failed to build label state", "err", err) 632 return ··· 661 662 labelDefs, err := db.GetLabelDefinitions( 663 rp.db, 664 + orm.FilterIn("at_uri", f.Labels), 665 + orm.FilterContains("scope", subject.Collection().String()), 666 ) 667 if err != nil { 668 l.Error("failed to fetch labels", "err", err) ··· 674 defs[l.AtUri().String()] = &l 675 } 676 677 + states, err := db.GetLabels(rp.db, orm.FilterEq("subject", subject)) 678 if err != nil { 679 l.Error("failed to build label state", "err", err) 680 return ··· 1037 // in the user's account. 1038 existingRepo, err := db.GetRepo( 1039 rp.db, 1040 + orm.FilterEq("did", user.Did), 1041 + orm.FilterEq("name", forkName), 1042 ) 1043 if err != nil { 1044 if !errors.Is(err, sql.ErrNoRows) {
+5 -4
appview/repo/repo_util.go
··· 8 9 "tangled.org/core/appview/db" 10 "tangled.org/core/appview/models" 11 "tangled.org/core/types" 12 ) 13 ··· 102 ps, err := db.GetPipelineStatuses( 103 d, 104 len(shas), 105 - db.FilterEq("repo_owner", repo.Did), 106 - db.FilterEq("repo_name", repo.Name), 107 - db.FilterEq("knot", repo.Knot), 108 - db.FilterIn("sha", shas), 109 ) 110 if err != nil { 111 return nil, err
··· 8 9 "tangled.org/core/appview/db" 10 "tangled.org/core/appview/models" 11 + "tangled.org/core/orm" 12 "tangled.org/core/types" 13 ) 14 ··· 103 ps, err := db.GetPipelineStatuses( 104 d, 105 len(shas), 106 + orm.FilterEq("repo_owner", repo.Did), 107 + orm.FilterEq("repo_name", repo.Name), 108 + orm.FilterEq("knot", repo.Knot), 109 + orm.FilterIn("sha", shas), 110 ) 111 if err != nil { 112 return nil, err
+3 -2
appview/repo/settings.go
··· 14 "tangled.org/core/appview/oauth" 15 "tangled.org/core/appview/pages" 16 xrpcclient "tangled.org/core/appview/xrpcclient" 17 "tangled.org/core/types" 18 19 comatproto "github.com/bluesky-social/indigo/api/atproto" ··· 210 return 211 } 212 213 - defaultLabels, err := db.GetLabelDefinitions(rp.db, db.FilterIn("at_uri", rp.config.Label.DefaultLabelDefs)) 214 if err != nil { 215 l.Error("failed to fetch labels", "err", err) 216 rp.pages.Error503(w) 217 return 218 } 219 220 - labels, err := db.GetLabelDefinitions(rp.db, db.FilterIn("at_uri", f.Labels)) 221 if err != nil { 222 l.Error("failed to fetch labels", "err", err) 223 rp.pages.Error503(w)
··· 14 "tangled.org/core/appview/oauth" 15 "tangled.org/core/appview/pages" 16 xrpcclient "tangled.org/core/appview/xrpcclient" 17 + "tangled.org/core/orm" 18 "tangled.org/core/types" 19 20 comatproto "github.com/bluesky-social/indigo/api/atproto" ··· 211 return 212 } 213 214 + defaultLabels, err := db.GetLabelDefinitions(rp.db, orm.FilterIn("at_uri", rp.config.Label.DefaultLabelDefs)) 215 if err != nil { 216 l.Error("failed to fetch labels", "err", err) 217 rp.pages.Error503(w) 218 return 219 } 220 221 + labels, err := db.GetLabelDefinitions(rp.db, orm.FilterIn("at_uri", f.Labels)) 222 if err != nil { 223 l.Error("failed to fetch labels", "err", err) 224 rp.pages.Error503(w)
+2 -1
appview/repo/tags.go
··· 10 "tangled.org/core/appview/models" 11 "tangled.org/core/appview/pages" 12 xrpcclient "tangled.org/core/appview/xrpcclient" 13 "tangled.org/core/types" 14 15 indigoxrpc "github.com/bluesky-social/indigo/xrpc" ··· 44 rp.pages.Error503(w) 45 return 46 } 47 - artifacts, err := db.GetArtifact(rp.db, db.FilterEq("repo_at", f.RepoAt())) 48 if err != nil { 49 l.Error("failed grab artifacts", "err", err) 50 return
··· 10 "tangled.org/core/appview/models" 11 "tangled.org/core/appview/pages" 12 xrpcclient "tangled.org/core/appview/xrpcclient" 13 + "tangled.org/core/orm" 14 "tangled.org/core/types" 15 16 indigoxrpc "github.com/bluesky-social/indigo/xrpc" ··· 45 rp.pages.Error503(w) 46 return 47 } 48 + artifacts, err := db.GetArtifact(rp.db, orm.FilterEq("repo_at", f.RepoAt())) 49 if err != nil { 50 l.Error("failed grab artifacts", "err", err) 51 return
+5 -4
appview/serververify/verify.go
··· 9 "tangled.org/core/api/tangled" 10 "tangled.org/core/appview/db" 11 "tangled.org/core/appview/xrpcclient" 12 "tangled.org/core/rbac" 13 ) 14 ··· 76 // mark this spindle as verified in the db 77 rowId, err := db.VerifySpindle( 78 tx, 79 - db.FilterEq("owner", owner), 80 - db.FilterEq("instance", instance), 81 ) 82 if err != nil { 83 return 0, fmt.Errorf("failed to write to DB: %w", err) ··· 115 // mark as registered 116 err = db.MarkRegistered( 117 tx, 118 - db.FilterEq("did", owner), 119 - db.FilterEq("domain", domain), 120 ) 121 if err != nil { 122 return fmt.Errorf("failed to register domain: %w", err)
··· 9 "tangled.org/core/api/tangled" 10 "tangled.org/core/appview/db" 11 "tangled.org/core/appview/xrpcclient" 12 + "tangled.org/core/orm" 13 "tangled.org/core/rbac" 14 ) 15 ··· 77 // mark this spindle as verified in the db 78 rowId, err := db.VerifySpindle( 79 tx, 80 + orm.FilterEq("owner", owner), 81 + orm.FilterEq("instance", instance), 82 ) 83 if err != nil { 84 return 0, fmt.Errorf("failed to write to DB: %w", err) ··· 116 // mark as registered 117 err = db.MarkRegistered( 118 tx, 119 + orm.FilterEq("did", owner), 120 + orm.FilterEq("domain", domain), 121 ) 122 if err != nil { 123 return fmt.Errorf("failed to register domain: %w", err)
+25 -24
appview/spindles/spindles.go
··· 20 "tangled.org/core/appview/serververify" 21 "tangled.org/core/appview/xrpcclient" 22 "tangled.org/core/idresolver" 23 "tangled.org/core/rbac" 24 "tangled.org/core/tid" 25 ··· 71 user := s.OAuth.GetUser(r) 72 all, err := db.GetSpindles( 73 s.Db, 74 - db.FilterEq("owner", user.Did), 75 ) 76 if err != nil { 77 s.Logger.Error("failed to fetch spindles", "err", err) ··· 101 102 spindles, err := db.GetSpindles( 103 s.Db, 104 - db.FilterEq("instance", instance), 105 - db.FilterEq("owner", user.Did), 106 - db.FilterIsNot("verified", "null"), 107 ) 108 if err != nil || len(spindles) != 1 { 109 l.Error("failed to get spindle", "err", err, "len(spindles)", len(spindles)) ··· 123 repos, err := db.GetRepos( 124 s.Db, 125 0, 126 - db.FilterEq("spindle", instance), 127 ) 128 if err != nil { 129 l.Error("failed to get spindle repos", "err", err) ··· 290 291 spindles, err := db.GetSpindles( 292 s.Db, 293 - db.FilterEq("owner", user.Did), 294 - db.FilterEq("instance", instance), 295 ) 296 if err != nil || len(spindles) != 1 { 297 l.Error("failed to retrieve instance", "err", err, "len(spindles)", len(spindles)) ··· 319 // remove spindle members first 320 err = db.RemoveSpindleMember( 321 tx, 322 - db.FilterEq("did", user.Did), 323 - db.FilterEq("instance", instance), 324 ) 325 if err != nil { 326 l.Error("failed to remove spindle members", "err", err) ··· 330 331 err = db.DeleteSpindle( 332 tx, 333 - db.FilterEq("owner", user.Did), 334 - db.FilterEq("instance", instance), 335 ) 336 if err != nil { 337 l.Error("failed to delete spindle", "err", err) ··· 410 411 spindles, err := db.GetSpindles( 412 s.Db, 413 - db.FilterEq("owner", user.Did), 414 - db.FilterEq("instance", instance), 415 ) 416 if err != nil || len(spindles) != 1 { 417 l.Error("failed to retrieve instance", "err", err, "len(spindles)", len(spindles)) ··· 453 454 verifiedSpindle, err := db.GetSpindles( 455 s.Db, 456 - db.FilterEq("id", rowId), 457 ) 458 if err != nil || len(verifiedSpindle) != 1 { 459 l.Error("failed get new spindle", "err", err) ··· 486 487 spindles, err := db.GetSpindles( 488 s.Db, 489 - db.FilterEq("owner", user.Did), 490 - db.FilterEq("instance", instance), 491 ) 492 if err != nil || len(spindles) != 1 { 493 l.Error("failed to retrieve instance", "err", err, "len(spindles)", len(spindles)) ··· 622 623 spindles, err := db.GetSpindles( 624 s.Db, 625 - db.FilterEq("owner", user.Did), 626 - db.FilterEq("instance", instance), 627 ) 628 if err != nil || len(spindles) != 1 { 629 l.Error("failed to retrieve instance", "err", err, "len(spindles)", len(spindles)) ··· 672 // get the record from the DB first: 673 members, err := db.GetSpindleMembers( 674 s.Db, 675 - db.FilterEq("did", user.Did), 676 - db.FilterEq("instance", instance), 677 - db.FilterEq("subject", memberId.DID), 678 ) 679 if err != nil || len(members) != 1 { 680 l.Error("failed to get member", "err", err) ··· 685 // remove from db 686 if err = db.RemoveSpindleMember( 687 tx, 688 - db.FilterEq("did", user.Did), 689 - db.FilterEq("instance", instance), 690 - db.FilterEq("subject", memberId.DID), 691 ); err != nil { 692 l.Error("failed to remove spindle member", "err", err) 693 fail()
··· 20 "tangled.org/core/appview/serververify" 21 "tangled.org/core/appview/xrpcclient" 22 "tangled.org/core/idresolver" 23 + "tangled.org/core/orm" 24 "tangled.org/core/rbac" 25 "tangled.org/core/tid" 26 ··· 72 user := s.OAuth.GetUser(r) 73 all, err := db.GetSpindles( 74 s.Db, 75 + orm.FilterEq("owner", user.Did), 76 ) 77 if err != nil { 78 s.Logger.Error("failed to fetch spindles", "err", err) ··· 102 103 spindles, err := db.GetSpindles( 104 s.Db, 105 + orm.FilterEq("instance", instance), 106 + orm.FilterEq("owner", user.Did), 107 + orm.FilterIsNot("verified", "null"), 108 ) 109 if err != nil || len(spindles) != 1 { 110 l.Error("failed to get spindle", "err", err, "len(spindles)", len(spindles)) ··· 124 repos, err := db.GetRepos( 125 s.Db, 126 0, 127 + orm.FilterEq("spindle", instance), 128 ) 129 if err != nil { 130 l.Error("failed to get spindle repos", "err", err) ··· 291 292 spindles, err := db.GetSpindles( 293 s.Db, 294 + orm.FilterEq("owner", user.Did), 295 + orm.FilterEq("instance", instance), 296 ) 297 if err != nil || len(spindles) != 1 { 298 l.Error("failed to retrieve instance", "err", err, "len(spindles)", len(spindles)) ··· 320 // remove spindle members first 321 err = db.RemoveSpindleMember( 322 tx, 323 + orm.FilterEq("did", user.Did), 324 + orm.FilterEq("instance", instance), 325 ) 326 if err != nil { 327 l.Error("failed to remove spindle members", "err", err) ··· 331 332 err = db.DeleteSpindle( 333 tx, 334 + orm.FilterEq("owner", user.Did), 335 + orm.FilterEq("instance", instance), 336 ) 337 if err != nil { 338 l.Error("failed to delete spindle", "err", err) ··· 411 412 spindles, err := db.GetSpindles( 413 s.Db, 414 + orm.FilterEq("owner", user.Did), 415 + orm.FilterEq("instance", instance), 416 ) 417 if err != nil || len(spindles) != 1 { 418 l.Error("failed to retrieve instance", "err", err, "len(spindles)", len(spindles)) ··· 454 455 verifiedSpindle, err := db.GetSpindles( 456 s.Db, 457 + orm.FilterEq("id", rowId), 458 ) 459 if err != nil || len(verifiedSpindle) != 1 { 460 l.Error("failed get new spindle", "err", err) ··· 487 488 spindles, err := db.GetSpindles( 489 s.Db, 490 + orm.FilterEq("owner", user.Did), 491 + orm.FilterEq("instance", instance), 492 ) 493 if err != nil || len(spindles) != 1 { 494 l.Error("failed to retrieve instance", "err", err, "len(spindles)", len(spindles)) ··· 623 624 spindles, err := db.GetSpindles( 625 s.Db, 626 + orm.FilterEq("owner", user.Did), 627 + orm.FilterEq("instance", instance), 628 ) 629 if err != nil || len(spindles) != 1 { 630 l.Error("failed to retrieve instance", "err", err, "len(spindles)", len(spindles)) ··· 673 // get the record from the DB first: 674 members, err := db.GetSpindleMembers( 675 s.Db, 676 + orm.FilterEq("did", user.Did), 677 + orm.FilterEq("instance", instance), 678 + orm.FilterEq("subject", memberId.DID), 679 ) 680 if err != nil || len(members) != 1 { 681 l.Error("failed to get member", "err", err) ··· 686 // remove from db 687 if err = db.RemoveSpindleMember( 688 tx, 689 + orm.FilterEq("did", user.Did), 690 + orm.FilterEq("instance", instance), 691 + orm.FilterEq("subject", memberId.DID), 692 ); err != nil { 693 l.Error("failed to remove spindle member", "err", err) 694 fail()
+6 -5
appview/state/gfi.go
··· 11 "tangled.org/core/appview/pages" 12 "tangled.org/core/appview/pagination" 13 "tangled.org/core/consts" 14 ) 15 16 func (s *State) GoodFirstIssues(w http.ResponseWriter, r *http.Request) { ··· 20 21 goodFirstIssueLabel := s.config.Label.GoodFirstIssue 22 23 - gfiLabelDef, err := db.GetLabelDefinition(s.db, db.FilterEq("at_uri", goodFirstIssueLabel)) 24 if err != nil { 25 log.Println("failed to get gfi label def", err) 26 s.pages.Error500(w) 27 return 28 } 29 30 - repoLabels, err := db.GetRepoLabels(s.db, db.FilterEq("label_at", goodFirstIssueLabel)) 31 if err != nil { 32 log.Println("failed to get repo labels", err) 33 s.pages.Error503(w) ··· 55 pagination.Page{ 56 Limit: 500, 57 }, 58 - db.FilterIn("repo_at", repoUris), 59 - db.FilterEq("open", 1), 60 ) 61 if err != nil { 62 log.Println("failed to get issues", err) ··· 132 } 133 134 if len(uriList) > 0 { 135 - allLabelDefs, err = db.GetLabelDefinitions(s.db, db.FilterIn("at_uri", uriList)) 136 if err != nil { 137 log.Println("failed to fetch labels", err) 138 }
··· 11 "tangled.org/core/appview/pages" 12 "tangled.org/core/appview/pagination" 13 "tangled.org/core/consts" 14 + "tangled.org/core/orm" 15 ) 16 17 func (s *State) GoodFirstIssues(w http.ResponseWriter, r *http.Request) { ··· 21 22 goodFirstIssueLabel := s.config.Label.GoodFirstIssue 23 24 + gfiLabelDef, err := db.GetLabelDefinition(s.db, orm.FilterEq("at_uri", goodFirstIssueLabel)) 25 if err != nil { 26 log.Println("failed to get gfi label def", err) 27 s.pages.Error500(w) 28 return 29 } 30 31 + repoLabels, err := db.GetRepoLabels(s.db, orm.FilterEq("label_at", goodFirstIssueLabel)) 32 if err != nil { 33 log.Println("failed to get repo labels", err) 34 s.pages.Error503(w) ··· 56 pagination.Page{ 57 Limit: 500, 58 }, 59 + orm.FilterIn("repo_at", repoUris), 60 + orm.FilterEq("open", 1), 61 ) 62 if err != nil { 63 log.Println("failed to get issues", err) ··· 133 } 134 135 if len(uriList) > 0 { 136 + allLabelDefs, err = db.GetLabelDefinitions(s.db, orm.FilterIn("at_uri", uriList)) 137 if err != nil { 138 log.Println("failed to fetch labels", err) 139 }
+6 -5
appview/state/knotstream.go
··· 16 ec "tangled.org/core/eventconsumer" 17 "tangled.org/core/eventconsumer/cursor" 18 "tangled.org/core/log" 19 "tangled.org/core/rbac" 20 "tangled.org/core/workflow" 21 ··· 30 31 knots, err := db.GetRegistrations( 32 d, 33 - db.FilterIsNot("registered", "null"), 34 ) 35 if err != nil { 36 return nil, err ··· 143 repos, err := db.GetRepos( 144 d, 145 0, 146 - db.FilterEq("did", record.RepoDid), 147 - db.FilterEq("name", record.RepoName), 148 ) 149 if err != nil { 150 return fmt.Errorf("failed to look for repo in DB (%s/%s): %w", record.RepoDid, record.RepoName, err) ··· 209 repos, err := db.GetRepos( 210 d, 211 0, 212 - db.FilterEq("did", record.TriggerMetadata.Repo.Did), 213 - db.FilterEq("name", record.TriggerMetadata.Repo.Repo), 214 ) 215 if err != nil { 216 return fmt.Errorf("failed to look for repo in DB: nsid %s, rkey %s, %w", msg.Nsid, msg.Rkey, err)
··· 16 ec "tangled.org/core/eventconsumer" 17 "tangled.org/core/eventconsumer/cursor" 18 "tangled.org/core/log" 19 + "tangled.org/core/orm" 20 "tangled.org/core/rbac" 21 "tangled.org/core/workflow" 22 ··· 31 32 knots, err := db.GetRegistrations( 33 d, 34 + orm.FilterIsNot("registered", "null"), 35 ) 36 if err != nil { 37 return nil, err ··· 144 repos, err := db.GetRepos( 145 d, 146 0, 147 + orm.FilterEq("did", record.RepoDid), 148 + orm.FilterEq("name", record.RepoName), 149 ) 150 if err != nil { 151 return fmt.Errorf("failed to look for repo in DB (%s/%s): %w", record.RepoDid, record.RepoName, err) ··· 210 repos, err := db.GetRepos( 211 d, 212 0, 213 + orm.FilterEq("did", record.TriggerMetadata.Repo.Did), 214 + orm.FilterEq("name", record.TriggerMetadata.Repo.Repo), 215 ) 216 if err != nil { 217 return fmt.Errorf("failed to look for repo in DB: nsid %s, rkey %s, %w", msg.Nsid, msg.Rkey, err)
+137 -12
appview/state/profile.go
··· 19 "tangled.org/core/appview/db" 20 "tangled.org/core/appview/models" 21 "tangled.org/core/appview/pages" 22 ) 23 24 func (s *State) Profile(w http.ResponseWriter, r *http.Request) { ··· 56 return nil, fmt.Errorf("failed to get profile: %w", err) 57 } 58 59 - repoCount, err := db.CountRepos(s.db, db.FilterEq("did", did)) 60 if err != nil { 61 return nil, fmt.Errorf("failed to get repo count: %w", err) 62 } 63 64 - stringCount, err := db.CountStrings(s.db, db.FilterEq("did", did)) 65 if err != nil { 66 return nil, fmt.Errorf("failed to get string count: %w", err) 67 } 68 69 - starredCount, err := db.CountStars(s.db, db.FilterEq("did", did)) 70 if err != nil { 71 return nil, fmt.Errorf("failed to get starred repo count: %w", err) 72 } ··· 86 startOfYear := time.Date(now.Year(), 1, 1, 0, 0, 0, 0, time.UTC) 87 punchcard, err := db.MakePunchcard( 88 s.db, 89 - db.FilterEq("did", did), 90 - db.FilterGte("date", startOfYear.Format(time.DateOnly)), 91 - db.FilterLte("date", now.Format(time.DateOnly)), 92 ) 93 if err != nil { 94 return nil, fmt.Errorf("failed to get punchcard for %s: %w", did, err) ··· 123 repos, err := db.GetRepos( 124 s.db, 125 0, 126 - db.FilterEq("did", profile.UserDid), 127 ) 128 if err != nil { 129 l.Error("failed to fetch repos", "err", err) ··· 193 repos, err := db.GetRepos( 194 s.db, 195 0, 196 - db.FilterEq("did", profile.UserDid), 197 ) 198 if err != nil { 199 l.Error("failed to get repos", "err", err) ··· 219 } 220 l = l.With("profileDid", profile.UserDid) 221 222 - stars, err := db.GetRepoStars(s.db, 0, db.FilterEq("did", profile.UserDid)) 223 if err != nil { 224 l.Error("failed to get stars", "err", err) 225 s.pages.Error500(w) ··· 248 } 249 l = l.With("profileDid", profile.UserDid) 250 251 - strings, err := db.GetStrings(s.db, 0, db.FilterEq("did", profile.UserDid)) 252 if err != nil { 253 l.Error("failed to get strings", "err", err) 254 s.pages.Error500(w) ··· 300 followDids = append(followDids, extractDid(follow)) 301 } 302 303 - profiles, err := db.GetProfiles(s.db, db.FilterIn("did", followDids)) 304 if err != nil { 305 l.Error("failed to get profiles", "followDids", followDids, "err", err) 306 return &params, err ··· 703 log.Printf("getting profile data for %s: %s", user.Did, err) 704 } 705 706 - repos, err := db.GetRepos(s.db, 0, db.FilterEq("did", user.Did)) 707 if err != nil { 708 log.Printf("getting repos for %s: %s", user.Did, err) 709 } ··· 736 AllRepos: allRepos, 737 }) 738 }
··· 19 "tangled.org/core/appview/db" 20 "tangled.org/core/appview/models" 21 "tangled.org/core/appview/pages" 22 + "tangled.org/core/orm" 23 ) 24 25 func (s *State) Profile(w http.ResponseWriter, r *http.Request) { ··· 57 return nil, fmt.Errorf("failed to get profile: %w", err) 58 } 59 60 + repoCount, err := db.CountRepos(s.db, orm.FilterEq("did", did)) 61 if err != nil { 62 return nil, fmt.Errorf("failed to get repo count: %w", err) 63 } 64 65 + stringCount, err := db.CountStrings(s.db, orm.FilterEq("did", did)) 66 if err != nil { 67 return nil, fmt.Errorf("failed to get string count: %w", err) 68 } 69 70 + starredCount, err := db.CountStars(s.db, orm.FilterEq("did", did)) 71 if err != nil { 72 return nil, fmt.Errorf("failed to get starred repo count: %w", err) 73 } ··· 87 startOfYear := time.Date(now.Year(), 1, 1, 0, 0, 0, 0, time.UTC) 88 punchcard, err := db.MakePunchcard( 89 s.db, 90 + orm.FilterEq("did", did), 91 + orm.FilterGte("date", startOfYear.Format(time.DateOnly)), 92 + orm.FilterLte("date", now.Format(time.DateOnly)), 93 ) 94 if err != nil { 95 return nil, fmt.Errorf("failed to get punchcard for %s: %w", did, err) ··· 124 repos, err := db.GetRepos( 125 s.db, 126 0, 127 + orm.FilterEq("did", profile.UserDid), 128 ) 129 if err != nil { 130 l.Error("failed to fetch repos", "err", err) ··· 194 repos, err := db.GetRepos( 195 s.db, 196 0, 197 + orm.FilterEq("did", profile.UserDid), 198 ) 199 if err != nil { 200 l.Error("failed to get repos", "err", err) ··· 220 } 221 l = l.With("profileDid", profile.UserDid) 222 223 + stars, err := db.GetRepoStars(s.db, 0, orm.FilterEq("did", profile.UserDid)) 224 if err != nil { 225 l.Error("failed to get stars", "err", err) 226 s.pages.Error500(w) ··· 249 } 250 l = l.With("profileDid", profile.UserDid) 251 252 + strings, err := db.GetStrings(s.db, 0, orm.FilterEq("did", profile.UserDid)) 253 if err != nil { 254 l.Error("failed to get strings", "err", err) 255 s.pages.Error500(w) ··· 301 followDids = append(followDids, extractDid(follow)) 302 } 303 304 + profiles, err := db.GetProfiles(s.db, orm.FilterIn("did", followDids)) 305 if err != nil { 306 l.Error("failed to get profiles", "followDids", followDids, "err", err) 307 return &params, err ··· 704 log.Printf("getting profile data for %s: %s", user.Did, err) 705 } 706 707 + repos, err := db.GetRepos(s.db, 0, orm.FilterEq("did", user.Did)) 708 if err != nil { 709 log.Printf("getting repos for %s: %s", user.Did, err) 710 } ··· 737 AllRepos: allRepos, 738 }) 739 } 740 + 741 + func (s *State) UploadProfileAvatar(w http.ResponseWriter, r *http.Request) { 742 + l := s.logger.With("handler", "UploadProfileAvatar") 743 + user := s.oauth.GetUser(r) 744 + l = l.With("did", user.Did) 745 + 746 + // Parse multipart form (10MB max) 747 + if err := r.ParseMultipartForm(10 << 20); err != nil { 748 + l.Error("failed to parse form", "err", err) 749 + w.WriteHeader(http.StatusBadRequest) 750 + fmt.Fprintf(w, "Failed to parse form") 751 + return 752 + } 753 + 754 + file, handler, err := r.FormFile("avatar") 755 + if err != nil { 756 + l.Error("failed to read avatar file", "err", err) 757 + w.WriteHeader(http.StatusBadRequest) 758 + fmt.Fprintf(w, "Failed to read avatar file") 759 + return 760 + } 761 + defer file.Close() 762 + 763 + if handler.Size > 1000000 { 764 + l.Warn("avatar file too large", "size", handler.Size) 765 + w.WriteHeader(http.StatusBadRequest) 766 + fmt.Fprintf(w, "Avatar file too large (max 1MB)") 767 + return 768 + } 769 + 770 + contentType := handler.Header.Get("Content-Type") 771 + if contentType != "image/png" && contentType != "image/jpeg" { 772 + l.Warn("invalid image type", "contentType", contentType) 773 + w.WriteHeader(http.StatusBadRequest) 774 + fmt.Fprintf(w, "Invalid image type (only PNG and JPEG allowed)") 775 + return 776 + } 777 + 778 + client, err := s.oauth.AuthorizedClient(r) 779 + if err != nil { 780 + l.Error("failed to get PDS client", "err", err) 781 + w.WriteHeader(http.StatusInternalServerError) 782 + fmt.Fprintf(w, "Failed to connect to your PDS") 783 + return 784 + } 785 + 786 + uploadBlobResp, err := comatproto.RepoUploadBlob(r.Context(), client, file) 787 + if err != nil { 788 + l.Error("failed to upload avatar blob", "err", err) 789 + w.WriteHeader(http.StatusInternalServerError) 790 + fmt.Fprintf(w, "Failed to upload avatar to your PDS") 791 + return 792 + } 793 + 794 + l.Info("uploaded avatar blob", "cid", uploadBlobResp.Blob.Ref.String()) 795 + 796 + // get current profile record from PDS to get its CID for swap 797 + getRecordResp, err := comatproto.RepoGetRecord(r.Context(), client, "", tangled.ActorProfileNSID, user.Did, "self") 798 + if err != nil { 799 + l.Error("failed to get current profile record", "err", err) 800 + w.WriteHeader(http.StatusInternalServerError) 801 + fmt.Fprintf(w, "Failed to get current profile from your PDS") 802 + return 803 + } 804 + 805 + var profileRecord *tangled.ActorProfile 806 + if getRecordResp.Value != nil { 807 + if val, ok := getRecordResp.Value.Val.(*tangled.ActorProfile); ok { 808 + profileRecord = val 809 + } else { 810 + l.Warn("profile record type assertion failed, creating new record") 811 + profileRecord = &tangled.ActorProfile{} 812 + } 813 + } else { 814 + l.Warn("no existing profile record, creating new record") 815 + profileRecord = &tangled.ActorProfile{} 816 + } 817 + 818 + profileRecord.Avatar = uploadBlobResp.Blob 819 + 820 + _, err = comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 821 + Collection: tangled.ActorProfileNSID, 822 + Repo: user.Did, 823 + Rkey: "self", 824 + Record: &lexutil.LexiconTypeDecoder{Val: profileRecord}, 825 + SwapRecord: getRecordResp.Cid, 826 + }) 827 + 828 + if err != nil { 829 + l.Error("failed to update profile record", "err", err) 830 + w.WriteHeader(http.StatusInternalServerError) 831 + fmt.Fprintf(w, "Failed to update profile on your PDS") 832 + return 833 + } 834 + 835 + l.Info("successfully updated profile with avatar") 836 + 837 + profile, err := db.GetProfile(s.db, user.Did) 838 + if err != nil { 839 + l.Warn("getting profile data from DB", "err", err) 840 + profile = &models.Profile{Did: user.Did} 841 + } 842 + profile.Avatar = uploadBlobResp.Blob.Ref.String() 843 + 844 + tx, err := s.db.BeginTx(r.Context(), nil) 845 + if err != nil { 846 + l.Error("failed to start transaction", "err", err) 847 + s.pages.HxRefresh(w) 848 + w.WriteHeader(http.StatusOK) 849 + return 850 + } 851 + 852 + err = db.UpsertProfile(tx, profile) 853 + if err != nil { 854 + l.Error("failed to update profile in DB", "err", err) 855 + tx.Rollback() 856 + s.pages.HxRefresh(w) 857 + w.WriteHeader(http.StatusOK) 858 + return 859 + } 860 + 861 + w.Header().Set("HX-Redirect", r.Header.Get("Referer")) 862 + w.WriteHeader(http.StatusOK) 863 + }
+3 -2
appview/state/router.go
··· 162 r.Get("/edit-pins", s.EditPinsFragment) 163 r.Post("/bio", s.UpdateProfileBio) 164 r.Post("/pins", s.UpdateProfilePins) 165 }) 166 167 r.Mount("/settings", s.SettingsRouter()) ··· 266 s.enforcer, 267 s.pages, 268 s.idResolver, 269 - s.refResolver, 270 s.db, 271 s.config, 272 s.notifier, ··· 283 s.repoResolver, 284 s.pages, 285 s.idResolver, 286 - s.refResolver, 287 s.db, 288 s.config, 289 s.notifier,
··· 162 r.Get("/edit-pins", s.EditPinsFragment) 163 r.Post("/bio", s.UpdateProfileBio) 164 r.Post("/pins", s.UpdateProfilePins) 165 + r.Post("/avatar", s.UploadProfileAvatar) 166 }) 167 168 r.Mount("/settings", s.SettingsRouter()) ··· 267 s.enforcer, 268 s.pages, 269 s.idResolver, 270 + s.mentionsResolver, 271 s.db, 272 s.config, 273 s.notifier, ··· 284 s.repoResolver, 285 s.pages, 286 s.idResolver, 287 + s.mentionsResolver, 288 s.db, 289 s.config, 290 s.notifier,
+2 -1
appview/state/spindlestream.go
··· 17 ec "tangled.org/core/eventconsumer" 18 "tangled.org/core/eventconsumer/cursor" 19 "tangled.org/core/log" 20 "tangled.org/core/rbac" 21 spindle "tangled.org/core/spindle/models" 22 ) ··· 27 28 spindles, err := db.GetSpindles( 29 d, 30 - db.FilterIsNot("verified", "null"), 31 ) 32 if err != nil { 33 return nil, err
··· 17 ec "tangled.org/core/eventconsumer" 18 "tangled.org/core/eventconsumer/cursor" 19 "tangled.org/core/log" 20 + "tangled.org/core/orm" 21 "tangled.org/core/rbac" 22 spindle "tangled.org/core/spindle/models" 23 ) ··· 28 29 spindles, err := db.GetSpindles( 30 d, 31 + orm.FilterIsNot("verified", "null"), 32 ) 33 if err != nil { 34 return nil, err
+28 -27
appview/state/state.go
··· 15 "tangled.org/core/appview/config" 16 "tangled.org/core/appview/db" 17 "tangled.org/core/appview/indexer" 18 "tangled.org/core/appview/models" 19 "tangled.org/core/appview/notify" 20 dbnotify "tangled.org/core/appview/notify/db" 21 phnotify "tangled.org/core/appview/notify/posthog" 22 "tangled.org/core/appview/oauth" 23 "tangled.org/core/appview/pages" 24 - "tangled.org/core/appview/refresolver" 25 "tangled.org/core/appview/reporesolver" 26 "tangled.org/core/appview/validator" 27 xrpcclient "tangled.org/core/appview/xrpcclient" ··· 30 "tangled.org/core/jetstream" 31 "tangled.org/core/log" 32 tlog "tangled.org/core/log" 33 "tangled.org/core/rbac" 34 "tangled.org/core/tid" 35 ··· 43 ) 44 45 type State struct { 46 - db *db.DB 47 - notifier notify.Notifier 48 - indexer *indexer.Indexer 49 - oauth *oauth.OAuth 50 - enforcer *rbac.Enforcer 51 - pages *pages.Pages 52 - idResolver *idresolver.Resolver 53 - refResolver *refresolver.Resolver 54 - posthog posthog.Client 55 - jc *jetstream.JetstreamClient 56 - config *config.Config 57 - repoResolver *reporesolver.RepoResolver 58 - knotstream *eventconsumer.Consumer 59 - spindlestream *eventconsumer.Consumer 60 - logger *slog.Logger 61 - validator *validator.Validator 62 } 63 64 func Make(ctx context.Context, config *config.Config) (*State, error) { ··· 100 101 repoResolver := reporesolver.New(config, enforcer, d) 102 103 - refResolver := refresolver.New(config, res, d, log.SubLogger(logger, "refResolver")) 104 105 wrapper := db.DbWrapper{Execer: d} 106 jc, err := jetstream.NewJetstreamClient( ··· 182 enforcer, 183 pages, 184 res, 185 - refResolver, 186 posthog, 187 jc, 188 config, ··· 299 return 300 } 301 302 - gfiLabel, err := db.GetLabelDefinition(s.db, db.FilterEq("at_uri", s.config.Label.GoodFirstIssue)) 303 if err != nil { 304 // non-fatal 305 } ··· 323 324 regs, err := db.GetRegistrations( 325 s.db, 326 - db.FilterEq("did", user.Did), 327 - db.FilterEq("needs_upgrade", 1), 328 ) 329 if err != nil { 330 l.Error("non-fatal: failed to get registrations", "err", err) ··· 332 333 spindles, err := db.GetSpindles( 334 s.db, 335 - db.FilterEq("owner", user.Did), 336 - db.FilterEq("needs_upgrade", 1), 337 ) 338 if err != nil { 339 l.Error("non-fatal: failed to get spindles", "err", err) ··· 504 // Check for existing repos 505 existingRepo, err := db.GetRepo( 506 s.db, 507 - db.FilterEq("did", user.Did), 508 - db.FilterEq("name", repoName), 509 ) 510 if err == nil && existingRepo != nil { 511 l.Info("repo exists") ··· 665 } 666 667 func BackfillDefaultDefs(e db.Execer, r *idresolver.Resolver, defaults []string) error { 668 - defaultLabels, err := db.GetLabelDefinitions(e, db.FilterIn("at_uri", defaults)) 669 if err != nil { 670 return err 671 }
··· 15 "tangled.org/core/appview/config" 16 "tangled.org/core/appview/db" 17 "tangled.org/core/appview/indexer" 18 + "tangled.org/core/appview/mentions" 19 "tangled.org/core/appview/models" 20 "tangled.org/core/appview/notify" 21 dbnotify "tangled.org/core/appview/notify/db" 22 phnotify "tangled.org/core/appview/notify/posthog" 23 "tangled.org/core/appview/oauth" 24 "tangled.org/core/appview/pages" 25 "tangled.org/core/appview/reporesolver" 26 "tangled.org/core/appview/validator" 27 xrpcclient "tangled.org/core/appview/xrpcclient" ··· 30 "tangled.org/core/jetstream" 31 "tangled.org/core/log" 32 tlog "tangled.org/core/log" 33 + "tangled.org/core/orm" 34 "tangled.org/core/rbac" 35 "tangled.org/core/tid" 36 ··· 44 ) 45 46 type State struct { 47 + db *db.DB 48 + notifier notify.Notifier 49 + indexer *indexer.Indexer 50 + oauth *oauth.OAuth 51 + enforcer *rbac.Enforcer 52 + pages *pages.Pages 53 + idResolver *idresolver.Resolver 54 + mentionsResolver *mentions.Resolver 55 + posthog posthog.Client 56 + jc *jetstream.JetstreamClient 57 + config *config.Config 58 + repoResolver *reporesolver.RepoResolver 59 + knotstream *eventconsumer.Consumer 60 + spindlestream *eventconsumer.Consumer 61 + logger *slog.Logger 62 + validator *validator.Validator 63 } 64 65 func Make(ctx context.Context, config *config.Config) (*State, error) { ··· 101 102 repoResolver := reporesolver.New(config, enforcer, d) 103 104 + mentionsResolver := mentions.New(config, res, d, log.SubLogger(logger, "mentionsResolver")) 105 106 wrapper := db.DbWrapper{Execer: d} 107 jc, err := jetstream.NewJetstreamClient( ··· 183 enforcer, 184 pages, 185 res, 186 + mentionsResolver, 187 posthog, 188 jc, 189 config, ··· 300 return 301 } 302 303 + gfiLabel, err := db.GetLabelDefinition(s.db, orm.FilterEq("at_uri", s.config.Label.GoodFirstIssue)) 304 if err != nil { 305 // non-fatal 306 } ··· 324 325 regs, err := db.GetRegistrations( 326 s.db, 327 + orm.FilterEq("did", user.Did), 328 + orm.FilterEq("needs_upgrade", 1), 329 ) 330 if err != nil { 331 l.Error("non-fatal: failed to get registrations", "err", err) ··· 333 334 spindles, err := db.GetSpindles( 335 s.db, 336 + orm.FilterEq("owner", user.Did), 337 + orm.FilterEq("needs_upgrade", 1), 338 ) 339 if err != nil { 340 l.Error("non-fatal: failed to get spindles", "err", err) ··· 505 // Check for existing repos 506 existingRepo, err := db.GetRepo( 507 s.db, 508 + orm.FilterEq("did", user.Did), 509 + orm.FilterEq("name", repoName), 510 ) 511 if err == nil && existingRepo != nil { 512 l.Info("repo exists") ··· 666 } 667 668 func BackfillDefaultDefs(e db.Execer, r *idresolver.Resolver, defaults []string) error { 669 + defaultLabels, err := db.GetLabelDefinitions(e, orm.FilterIn("at_uri", defaults)) 670 if err != nil { 671 return err 672 }
+7 -6
appview/strings/strings.go
··· 17 "tangled.org/core/appview/pages" 18 "tangled.org/core/appview/pages/markup" 19 "tangled.org/core/idresolver" 20 "tangled.org/core/tid" 21 22 "github.com/bluesky-social/indigo/api/atproto" ··· 108 strings, err := db.GetStrings( 109 s.Db, 110 0, 111 - db.FilterEq("did", id.DID), 112 - db.FilterEq("rkey", rkey), 113 ) 114 if err != nil { 115 l.Error("failed to fetch string", "err", err) ··· 199 all, err := db.GetStrings( 200 s.Db, 201 0, 202 - db.FilterEq("did", id.DID), 203 - db.FilterEq("rkey", rkey), 204 ) 205 if err != nil { 206 l.Error("failed to fetch string", "err", err) ··· 408 409 if err := db.DeleteString( 410 s.Db, 411 - db.FilterEq("did", user.Did), 412 - db.FilterEq("rkey", rkey), 413 ); err != nil { 414 fail("Failed to delete string.", err) 415 return
··· 17 "tangled.org/core/appview/pages" 18 "tangled.org/core/appview/pages/markup" 19 "tangled.org/core/idresolver" 20 + "tangled.org/core/orm" 21 "tangled.org/core/tid" 22 23 "github.com/bluesky-social/indigo/api/atproto" ··· 109 strings, err := db.GetStrings( 110 s.Db, 111 0, 112 + orm.FilterEq("did", id.DID), 113 + orm.FilterEq("rkey", rkey), 114 ) 115 if err != nil { 116 l.Error("failed to fetch string", "err", err) ··· 200 all, err := db.GetStrings( 201 s.Db, 202 0, 203 + orm.FilterEq("did", id.DID), 204 + orm.FilterEq("rkey", rkey), 205 ) 206 if err != nil { 207 l.Error("failed to fetch string", "err", err) ··· 409 410 if err := db.DeleteString( 411 s.Db, 412 + orm.FilterEq("did", user.Did), 413 + orm.FilterEq("rkey", rkey), 414 ); err != nil { 415 fail("Failed to delete string.", err) 416 return
+2 -1
appview/validator/issue.go
··· 6 7 "tangled.org/core/appview/db" 8 "tangled.org/core/appview/models" 9 ) 10 11 func (v *Validator) ValidateIssueComment(comment *models.IssueComment) error { 12 // if comments have parents, only ingest ones that are 1 level deep 13 if comment.ReplyTo != nil { 14 - parents, err := db.GetIssueComments(v.db, db.FilterEq("at_uri", *comment.ReplyTo)) 15 if err != nil { 16 return fmt.Errorf("failed to fetch parent comment: %w", err) 17 }
··· 6 7 "tangled.org/core/appview/db" 8 "tangled.org/core/appview/models" 9 + "tangled.org/core/orm" 10 ) 11 12 func (v *Validator) ValidateIssueComment(comment *models.IssueComment) error { 13 // if comments have parents, only ingest ones that are 1 level deep 14 if comment.ReplyTo != nil { 15 + parents, err := db.GetIssueComments(v.db, orm.FilterEq("at_uri", *comment.ReplyTo)) 16 if err != nil { 17 return fmt.Errorf("failed to fetch parent comment: %w", err) 18 }
+3137 -3022
avatar/package-lock.json
··· 1 { 2 - "name": "avatar", 3 - "version": "0.0.0", 4 - "lockfileVersion": 3, 5 - "requires": true, 6 - "packages": { 7 - "": { 8 - "name": "avatar", 9 - "version": "0.0.0", 10 - "devDependencies": { 11 - "@cloudflare/vitest-pool-workers": "^0.8.19", 12 - "vitest": "~3.0.7", 13 - "wrangler": "^4.14.1" 14 - } 15 - }, 16 - "node_modules/@cloudflare/kv-asset-handler": { 17 - "version": "0.4.0", 18 - "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.0.tgz", 19 - "integrity": "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA==", 20 - "dev": true, 21 - "license": "MIT OR Apache-2.0", 22 - "dependencies": { 23 - "mime": "^3.0.0" 24 - }, 25 - "engines": { 26 - "node": ">=18.0.0" 27 - } 28 - }, 29 - "node_modules/@cloudflare/unenv-preset": { 30 - "version": "2.3.1", 31 - "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.3.1.tgz", 32 - "integrity": "sha512-Xq57Qd+ADpt6hibcVBO0uLG9zzRgyRhfCUgBT9s+g3+3Ivg5zDyVgLFy40ES1VdNcu8rPNSivm9A+kGP5IVaPg==", 33 - "dev": true, 34 - "license": "MIT OR Apache-2.0", 35 - "peerDependencies": { 36 - "unenv": "2.0.0-rc.15", 37 - "workerd": "^1.20250320.0" 38 - }, 39 - "peerDependenciesMeta": { 40 - "workerd": { 41 - "optional": true 42 - } 43 - } 44 - }, 45 - "node_modules/@cloudflare/vitest-pool-workers": { 46 - "version": "0.8.24", 47 - "resolved": "https://registry.npmjs.org/@cloudflare/vitest-pool-workers/-/vitest-pool-workers-0.8.24.tgz", 48 - "integrity": "sha512-wT2PABJQ9YLYWrVu4CRZOjvmjHkdbMyLTZPU9n/7JEMM3pgG8dY41F1Rj31UsXRQaXX39A/CTPGlk58dcMUysA==", 49 - "dev": true, 50 - "license": "MIT", 51 - "dependencies": { 52 - "birpc": "0.2.14", 53 - "cjs-module-lexer": "^1.2.3", 54 - "devalue": "^4.3.0", 55 - "miniflare": "4.20250428.1", 56 - "semver": "^7.7.1", 57 - "wrangler": "4.14.1", 58 - "zod": "^3.22.3" 59 - }, 60 - "peerDependencies": { 61 - "@vitest/runner": "2.0.x - 3.1.x", 62 - "@vitest/snapshot": "2.0.x - 3.1.x", 63 - "vitest": "2.0.x - 3.1.x" 64 - } 65 - }, 66 - "node_modules/@cloudflare/workerd-darwin-64": { 67 - "version": "1.20250428.0", 68 - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250428.0.tgz", 69 - "integrity": "sha512-6nVe9oV4Hdec6ctzMtW80TiDvNTd2oFPi3VsKqSDVaJSJbL+4b6seyJ7G/UEPI+si6JhHBSLV2/9lNXNGLjClA==", 70 - "cpu": [ 71 - "x64" 72 - ], 73 - "dev": true, 74 - "license": "Apache-2.0", 75 - "optional": true, 76 - "os": [ 77 - "darwin" 78 - ], 79 - "engines": { 80 - "node": ">=16" 81 - } 82 - }, 83 - "node_modules/@cloudflare/workerd-darwin-arm64": { 84 - "version": "1.20250428.0", 85 - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250428.0.tgz", 86 - "integrity": "sha512-/TB7bh7SIJ5f+6r4PHsAz7+9Qal/TK1cJuKFkUno1kqGlZbdrMwH0ATYwlWC/nBFeu2FB3NUolsTntEuy23hnQ==", 87 - "cpu": [ 88 - "arm64" 89 - ], 90 - "dev": true, 91 - "license": "Apache-2.0", 92 - "optional": true, 93 - "os": [ 94 - "darwin" 95 - ], 96 - "engines": { 97 - "node": ">=16" 98 - } 99 - }, 100 - "node_modules/@cloudflare/workerd-linux-64": { 101 - "version": "1.20250428.0", 102 - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250428.0.tgz", 103 - "integrity": "sha512-9eCbj+R3CKqpiXP6DfAA20DxKge+OTj7Hyw3ZewiEhWH9INIHiJwJQYybu4iq9kJEGjnGvxgguLFjSCWm26hgg==", 104 - "cpu": [ 105 - "x64" 106 - ], 107 - "dev": true, 108 - "license": "Apache-2.0", 109 - "optional": true, 110 - "os": [ 111 - "linux" 112 - ], 113 - "engines": { 114 - "node": ">=16" 115 - } 116 - }, 117 - "node_modules/@cloudflare/workerd-linux-arm64": { 118 - "version": "1.20250428.0", 119 - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250428.0.tgz", 120 - "integrity": "sha512-D9NRBnW46nl1EQsP13qfkYb5lbt4C6nxl38SBKY/NOcZAUoHzNB5K0GaK8LxvpkM7X/97ySojlMfR5jh5DNXYQ==", 121 - "cpu": [ 122 - "arm64" 123 - ], 124 - "dev": true, 125 - "license": "Apache-2.0", 126 - "optional": true, 127 - "os": [ 128 - "linux" 129 - ], 130 - "engines": { 131 - "node": ">=16" 132 - } 133 - }, 134 - "node_modules/@cloudflare/workerd-windows-64": { 135 - "version": "1.20250428.0", 136 - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250428.0.tgz", 137 - "integrity": "sha512-RQCRj28eitjKD0tmei6iFOuWqMuHMHdNGEigRmbkmuTlpbWHNAoHikgCzZQ/dkKDdatA76TmcpbyECNf31oaTA==", 138 - "cpu": [ 139 - "x64" 140 - ], 141 - "dev": true, 142 - "license": "Apache-2.0", 143 - "optional": true, 144 - "os": [ 145 - "win32" 146 - ], 147 - "engines": { 148 - "node": ">=16" 149 - } 150 - }, 151 - "node_modules/@cspotcode/source-map-support": { 152 - "version": "0.8.1", 153 - "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 154 - "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 155 - "dev": true, 156 - "license": "MIT", 157 - "dependencies": { 158 - "@jridgewell/trace-mapping": "0.3.9" 159 - }, 160 - "engines": { 161 - "node": ">=12" 162 - } 163 - }, 164 - "node_modules/@emnapi/runtime": { 165 - "version": "1.4.3", 166 - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", 167 - "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", 168 - "dev": true, 169 - "license": "MIT", 170 - "optional": true, 171 - "dependencies": { 172 - "tslib": "^2.4.0" 173 - } 174 - }, 175 - "node_modules/@esbuild/aix-ppc64": { 176 - "version": "0.25.3", 177 - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", 178 - "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==", 179 - "cpu": [ 180 - "ppc64" 181 - ], 182 - "dev": true, 183 - "license": "MIT", 184 - "optional": true, 185 - "os": [ 186 - "aix" 187 - ], 188 - "engines": { 189 - "node": ">=18" 190 - } 191 - }, 192 - "node_modules/@esbuild/android-arm": { 193 - "version": "0.25.3", 194 - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz", 195 - "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==", 196 - "cpu": [ 197 - "arm" 198 - ], 199 - "dev": true, 200 - "license": "MIT", 201 - "optional": true, 202 - "os": [ 203 - "android" 204 - ], 205 - "engines": { 206 - "node": ">=18" 207 - } 208 - }, 209 - "node_modules/@esbuild/android-arm64": { 210 - "version": "0.25.3", 211 - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz", 212 - "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==", 213 - "cpu": [ 214 - "arm64" 215 - ], 216 - "dev": true, 217 - "license": "MIT", 218 - "optional": true, 219 - "os": [ 220 - "android" 221 - ], 222 - "engines": { 223 - "node": ">=18" 224 - } 225 - }, 226 - "node_modules/@esbuild/android-x64": { 227 - "version": "0.25.3", 228 - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz", 229 - "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==", 230 - "cpu": [ 231 - "x64" 232 - ], 233 - "dev": true, 234 - "license": "MIT", 235 - "optional": true, 236 - "os": [ 237 - "android" 238 - ], 239 - "engines": { 240 - "node": ">=18" 241 - } 242 - }, 243 - "node_modules/@esbuild/darwin-arm64": { 244 - "version": "0.25.3", 245 - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz", 246 - "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==", 247 - "cpu": [ 248 - "arm64" 249 - ], 250 - "dev": true, 251 - "license": "MIT", 252 - "optional": true, 253 - "os": [ 254 - "darwin" 255 - ], 256 - "engines": { 257 - "node": ">=18" 258 - } 259 - }, 260 - "node_modules/@esbuild/darwin-x64": { 261 - "version": "0.25.3", 262 - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz", 263 - "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==", 264 - "cpu": [ 265 - "x64" 266 - ], 267 - "dev": true, 268 - "license": "MIT", 269 - "optional": true, 270 - "os": [ 271 - "darwin" 272 - ], 273 - "engines": { 274 - "node": ">=18" 275 - } 276 - }, 277 - "node_modules/@esbuild/freebsd-arm64": { 278 - "version": "0.25.3", 279 - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz", 280 - "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==", 281 - "cpu": [ 282 - "arm64" 283 - ], 284 - "dev": true, 285 - "license": "MIT", 286 - "optional": true, 287 - "os": [ 288 - "freebsd" 289 - ], 290 - "engines": { 291 - "node": ">=18" 292 - } 293 - }, 294 - "node_modules/@esbuild/freebsd-x64": { 295 - "version": "0.25.3", 296 - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz", 297 - "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==", 298 - "cpu": [ 299 - "x64" 300 - ], 301 - "dev": true, 302 - "license": "MIT", 303 - "optional": true, 304 - "os": [ 305 - "freebsd" 306 - ], 307 - "engines": { 308 - "node": ">=18" 309 - } 310 - }, 311 - "node_modules/@esbuild/linux-arm": { 312 - "version": "0.25.3", 313 - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz", 314 - "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==", 315 - "cpu": [ 316 - "arm" 317 - ], 318 - "dev": true, 319 - "license": "MIT", 320 - "optional": true, 321 - "os": [ 322 - "linux" 323 - ], 324 - "engines": { 325 - "node": ">=18" 326 - } 327 - }, 328 - "node_modules/@esbuild/linux-arm64": { 329 - "version": "0.25.3", 330 - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz", 331 - "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==", 332 - "cpu": [ 333 - "arm64" 334 - ], 335 - "dev": true, 336 - "license": "MIT", 337 - "optional": true, 338 - "os": [ 339 - "linux" 340 - ], 341 - "engines": { 342 - "node": ">=18" 343 - } 344 - }, 345 - "node_modules/@esbuild/linux-ia32": { 346 - "version": "0.25.3", 347 - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz", 348 - "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==", 349 - "cpu": [ 350 - "ia32" 351 - ], 352 - "dev": true, 353 - "license": "MIT", 354 - "optional": true, 355 - "os": [ 356 - "linux" 357 - ], 358 - "engines": { 359 - "node": ">=18" 360 - } 361 - }, 362 - "node_modules/@esbuild/linux-loong64": { 363 - "version": "0.25.3", 364 - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz", 365 - "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==", 366 - "cpu": [ 367 - "loong64" 368 - ], 369 - "dev": true, 370 - "license": "MIT", 371 - "optional": true, 372 - "os": [ 373 - "linux" 374 - ], 375 - "engines": { 376 - "node": ">=18" 377 - } 378 - }, 379 - "node_modules/@esbuild/linux-mips64el": { 380 - "version": "0.25.3", 381 - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz", 382 - "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==", 383 - "cpu": [ 384 - "mips64el" 385 - ], 386 - "dev": true, 387 - "license": "MIT", 388 - "optional": true, 389 - "os": [ 390 - "linux" 391 - ], 392 - "engines": { 393 - "node": ">=18" 394 - } 395 - }, 396 - "node_modules/@esbuild/linux-ppc64": { 397 - "version": "0.25.3", 398 - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz", 399 - "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==", 400 - "cpu": [ 401 - "ppc64" 402 - ], 403 - "dev": true, 404 - "license": "MIT", 405 - "optional": true, 406 - "os": [ 407 - "linux" 408 - ], 409 - "engines": { 410 - "node": ">=18" 411 - } 412 - }, 413 - "node_modules/@esbuild/linux-riscv64": { 414 - "version": "0.25.3", 415 - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz", 416 - "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==", 417 - "cpu": [ 418 - "riscv64" 419 - ], 420 - "dev": true, 421 - "license": "MIT", 422 - "optional": true, 423 - "os": [ 424 - "linux" 425 - ], 426 - "engines": { 427 - "node": ">=18" 428 - } 429 - }, 430 - "node_modules/@esbuild/linux-s390x": { 431 - "version": "0.25.3", 432 - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz", 433 - "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==", 434 - "cpu": [ 435 - "s390x" 436 - ], 437 - "dev": true, 438 - "license": "MIT", 439 - "optional": true, 440 - "os": [ 441 - "linux" 442 - ], 443 - "engines": { 444 - "node": ">=18" 445 - } 446 - }, 447 - "node_modules/@esbuild/linux-x64": { 448 - "version": "0.25.3", 449 - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz", 450 - "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==", 451 - "cpu": [ 452 - "x64" 453 - ], 454 - "dev": true, 455 - "license": "MIT", 456 - "optional": true, 457 - "os": [ 458 - "linux" 459 - ], 460 - "engines": { 461 - "node": ">=18" 462 - } 463 - }, 464 - "node_modules/@esbuild/netbsd-arm64": { 465 - "version": "0.25.3", 466 - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz", 467 - "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==", 468 - "cpu": [ 469 - "arm64" 470 - ], 471 - "dev": true, 472 - "license": "MIT", 473 - "optional": true, 474 - "os": [ 475 - "netbsd" 476 - ], 477 - "engines": { 478 - "node": ">=18" 479 - } 480 - }, 481 - "node_modules/@esbuild/netbsd-x64": { 482 - "version": "0.25.3", 483 - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz", 484 - "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==", 485 - "cpu": [ 486 - "x64" 487 - ], 488 - "dev": true, 489 - "license": "MIT", 490 - "optional": true, 491 - "os": [ 492 - "netbsd" 493 - ], 494 - "engines": { 495 - "node": ">=18" 496 - } 497 - }, 498 - "node_modules/@esbuild/openbsd-arm64": { 499 - "version": "0.25.3", 500 - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz", 501 - "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==", 502 - "cpu": [ 503 - "arm64" 504 - ], 505 - "dev": true, 506 - "license": "MIT", 507 - "optional": true, 508 - "os": [ 509 - "openbsd" 510 - ], 511 - "engines": { 512 - "node": ">=18" 513 - } 514 - }, 515 - "node_modules/@esbuild/openbsd-x64": { 516 - "version": "0.25.3", 517 - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz", 518 - "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==", 519 - "cpu": [ 520 - "x64" 521 - ], 522 - "dev": true, 523 - "license": "MIT", 524 - "optional": true, 525 - "os": [ 526 - "openbsd" 527 - ], 528 - "engines": { 529 - "node": ">=18" 530 - } 531 - }, 532 - "node_modules/@esbuild/sunos-x64": { 533 - "version": "0.25.3", 534 - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz", 535 - "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==", 536 - "cpu": [ 537 - "x64" 538 - ], 539 - "dev": true, 540 - "license": "MIT", 541 - "optional": true, 542 - "os": [ 543 - "sunos" 544 - ], 545 - "engines": { 546 - "node": ">=18" 547 - } 548 - }, 549 - "node_modules/@esbuild/win32-arm64": { 550 - "version": "0.25.3", 551 - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz", 552 - "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==", 553 - "cpu": [ 554 - "arm64" 555 - ], 556 - "dev": true, 557 - "license": "MIT", 558 - "optional": true, 559 - "os": [ 560 - "win32" 561 - ], 562 - "engines": { 563 - "node": ">=18" 564 - } 565 - }, 566 - "node_modules/@esbuild/win32-ia32": { 567 - "version": "0.25.3", 568 - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz", 569 - "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==", 570 - "cpu": [ 571 - "ia32" 572 - ], 573 - "dev": true, 574 - "license": "MIT", 575 - "optional": true, 576 - "os": [ 577 - "win32" 578 - ], 579 - "engines": { 580 - "node": ">=18" 581 - } 582 - }, 583 - "node_modules/@esbuild/win32-x64": { 584 - "version": "0.25.3", 585 - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz", 586 - "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==", 587 - "cpu": [ 588 - "x64" 589 - ], 590 - "dev": true, 591 - "license": "MIT", 592 - "optional": true, 593 - "os": [ 594 - "win32" 595 - ], 596 - "engines": { 597 - "node": ">=18" 598 - } 599 - }, 600 - "node_modules/@fastify/busboy": { 601 - "version": "2.1.1", 602 - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", 603 - "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", 604 - "dev": true, 605 - "license": "MIT", 606 - "engines": { 607 - "node": ">=14" 608 - } 609 - }, 610 - "node_modules/@img/sharp-darwin-arm64": { 611 - "version": "0.33.5", 612 - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", 613 - "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", 614 - "cpu": [ 615 - "arm64" 616 - ], 617 - "dev": true, 618 - "license": "Apache-2.0", 619 - "optional": true, 620 - "os": [ 621 - "darwin" 622 - ], 623 - "engines": { 624 - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 625 - }, 626 - "funding": { 627 - "url": "https://opencollective.com/libvips" 628 - }, 629 - "optionalDependencies": { 630 - "@img/sharp-libvips-darwin-arm64": "1.0.4" 631 - } 632 - }, 633 - "node_modules/@img/sharp-darwin-x64": { 634 - "version": "0.33.5", 635 - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", 636 - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", 637 - "cpu": [ 638 - "x64" 639 - ], 640 - "dev": true, 641 - "license": "Apache-2.0", 642 - "optional": true, 643 - "os": [ 644 - "darwin" 645 - ], 646 - "engines": { 647 - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 648 - }, 649 - "funding": { 650 - "url": "https://opencollective.com/libvips" 651 - }, 652 - "optionalDependencies": { 653 - "@img/sharp-libvips-darwin-x64": "1.0.4" 654 - } 655 - }, 656 - "node_modules/@img/sharp-libvips-darwin-arm64": { 657 - "version": "1.0.4", 658 - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", 659 - "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", 660 - "cpu": [ 661 - "arm64" 662 - ], 663 - "dev": true, 664 - "license": "LGPL-3.0-or-later", 665 - "optional": true, 666 - "os": [ 667 - "darwin" 668 - ], 669 - "funding": { 670 - "url": "https://opencollective.com/libvips" 671 - } 672 - }, 673 - "node_modules/@img/sharp-libvips-darwin-x64": { 674 - "version": "1.0.4", 675 - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", 676 - "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", 677 - "cpu": [ 678 - "x64" 679 - ], 680 - "dev": true, 681 - "license": "LGPL-3.0-or-later", 682 - "optional": true, 683 - "os": [ 684 - "darwin" 685 - ], 686 - "funding": { 687 - "url": "https://opencollective.com/libvips" 688 - } 689 - }, 690 - "node_modules/@img/sharp-libvips-linux-arm": { 691 - "version": "1.0.5", 692 - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", 693 - "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", 694 - "cpu": [ 695 - "arm" 696 - ], 697 - "dev": true, 698 - "license": "LGPL-3.0-or-later", 699 - "optional": true, 700 - "os": [ 701 - "linux" 702 - ], 703 - "funding": { 704 - "url": "https://opencollective.com/libvips" 705 - } 706 - }, 707 - "node_modules/@img/sharp-libvips-linux-arm64": { 708 - "version": "1.0.4", 709 - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", 710 - "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", 711 - "cpu": [ 712 - "arm64" 713 - ], 714 - "dev": true, 715 - "license": "LGPL-3.0-or-later", 716 - "optional": true, 717 - "os": [ 718 - "linux" 719 - ], 720 - "funding": { 721 - "url": "https://opencollective.com/libvips" 722 - } 723 - }, 724 - "node_modules/@img/sharp-libvips-linux-s390x": { 725 - "version": "1.0.4", 726 - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", 727 - "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", 728 - "cpu": [ 729 - "s390x" 730 - ], 731 - "dev": true, 732 - "license": "LGPL-3.0-or-later", 733 - "optional": true, 734 - "os": [ 735 - "linux" 736 - ], 737 - "funding": { 738 - "url": "https://opencollective.com/libvips" 739 - } 740 - }, 741 - "node_modules/@img/sharp-libvips-linux-x64": { 742 - "version": "1.0.4", 743 - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", 744 - "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", 745 - "cpu": [ 746 - "x64" 747 - ], 748 - "dev": true, 749 - "license": "LGPL-3.0-or-later", 750 - "optional": true, 751 - "os": [ 752 - "linux" 753 - ], 754 - "funding": { 755 - "url": "https://opencollective.com/libvips" 756 - } 757 - }, 758 - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { 759 - "version": "1.0.4", 760 - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", 761 - "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", 762 - "cpu": [ 763 - "arm64" 764 - ], 765 - "dev": true, 766 - "license": "LGPL-3.0-or-later", 767 - "optional": true, 768 - "os": [ 769 - "linux" 770 - ], 771 - "funding": { 772 - "url": "https://opencollective.com/libvips" 773 - } 774 - }, 775 - "node_modules/@img/sharp-libvips-linuxmusl-x64": { 776 - "version": "1.0.4", 777 - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", 778 - "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", 779 - "cpu": [ 780 - "x64" 781 - ], 782 - "dev": true, 783 - "license": "LGPL-3.0-or-later", 784 - "optional": true, 785 - "os": [ 786 - "linux" 787 - ], 788 - "funding": { 789 - "url": "https://opencollective.com/libvips" 790 - } 791 - }, 792 - "node_modules/@img/sharp-linux-arm": { 793 - "version": "0.33.5", 794 - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", 795 - "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", 796 - "cpu": [ 797 - "arm" 798 - ], 799 - "dev": true, 800 - "license": "Apache-2.0", 801 - "optional": true, 802 - "os": [ 803 - "linux" 804 - ], 805 - "engines": { 806 - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 807 - }, 808 - "funding": { 809 - "url": "https://opencollective.com/libvips" 810 - }, 811 - "optionalDependencies": { 812 - "@img/sharp-libvips-linux-arm": "1.0.5" 813 - } 814 - }, 815 - "node_modules/@img/sharp-linux-arm64": { 816 - "version": "0.33.5", 817 - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", 818 - "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", 819 - "cpu": [ 820 - "arm64" 821 - ], 822 - "dev": true, 823 - "license": "Apache-2.0", 824 - "optional": true, 825 - "os": [ 826 - "linux" 827 - ], 828 - "engines": { 829 - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 830 - }, 831 - "funding": { 832 - "url": "https://opencollective.com/libvips" 833 - }, 834 - "optionalDependencies": { 835 - "@img/sharp-libvips-linux-arm64": "1.0.4" 836 - } 837 - }, 838 - "node_modules/@img/sharp-linux-s390x": { 839 - "version": "0.33.5", 840 - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", 841 - "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", 842 - "cpu": [ 843 - "s390x" 844 - ], 845 - "dev": true, 846 - "license": "Apache-2.0", 847 - "optional": true, 848 - "os": [ 849 - "linux" 850 - ], 851 - "engines": { 852 - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 853 - }, 854 - "funding": { 855 - "url": "https://opencollective.com/libvips" 856 - }, 857 - "optionalDependencies": { 858 - "@img/sharp-libvips-linux-s390x": "1.0.4" 859 - } 860 - }, 861 - "node_modules/@img/sharp-linux-x64": { 862 - "version": "0.33.5", 863 - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", 864 - "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", 865 - "cpu": [ 866 - "x64" 867 - ], 868 - "dev": true, 869 - "license": "Apache-2.0", 870 - "optional": true, 871 - "os": [ 872 - "linux" 873 - ], 874 - "engines": { 875 - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 876 - }, 877 - "funding": { 878 - "url": "https://opencollective.com/libvips" 879 - }, 880 - "optionalDependencies": { 881 - "@img/sharp-libvips-linux-x64": "1.0.4" 882 - } 883 - }, 884 - "node_modules/@img/sharp-linuxmusl-arm64": { 885 - "version": "0.33.5", 886 - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", 887 - "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", 888 - "cpu": [ 889 - "arm64" 890 - ], 891 - "dev": true, 892 - "license": "Apache-2.0", 893 - "optional": true, 894 - "os": [ 895 - "linux" 896 - ], 897 - "engines": { 898 - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 899 - }, 900 - "funding": { 901 - "url": "https://opencollective.com/libvips" 902 - }, 903 - "optionalDependencies": { 904 - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" 905 - } 906 - }, 907 - "node_modules/@img/sharp-linuxmusl-x64": { 908 - "version": "0.33.5", 909 - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", 910 - "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", 911 - "cpu": [ 912 - "x64" 913 - ], 914 - "dev": true, 915 - "license": "Apache-2.0", 916 - "optional": true, 917 - "os": [ 918 - "linux" 919 - ], 920 - "engines": { 921 - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 922 - }, 923 - "funding": { 924 - "url": "https://opencollective.com/libvips" 925 - }, 926 - "optionalDependencies": { 927 - "@img/sharp-libvips-linuxmusl-x64": "1.0.4" 928 - } 929 - }, 930 - "node_modules/@img/sharp-wasm32": { 931 - "version": "0.33.5", 932 - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", 933 - "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", 934 - "cpu": [ 935 - "wasm32" 936 - ], 937 - "dev": true, 938 - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", 939 - "optional": true, 940 - "dependencies": { 941 - "@emnapi/runtime": "^1.2.0" 942 - }, 943 - "engines": { 944 - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 945 - }, 946 - "funding": { 947 - "url": "https://opencollective.com/libvips" 948 - } 949 - }, 950 - "node_modules/@img/sharp-win32-ia32": { 951 - "version": "0.33.5", 952 - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", 953 - "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", 954 - "cpu": [ 955 - "ia32" 956 - ], 957 - "dev": true, 958 - "license": "Apache-2.0 AND LGPL-3.0-or-later", 959 - "optional": true, 960 - "os": [ 961 - "win32" 962 - ], 963 - "engines": { 964 - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 965 - }, 966 - "funding": { 967 - "url": "https://opencollective.com/libvips" 968 - } 969 - }, 970 - "node_modules/@img/sharp-win32-x64": { 971 - "version": "0.33.5", 972 - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", 973 - "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", 974 - "cpu": [ 975 - "x64" 976 - ], 977 - "dev": true, 978 - "license": "Apache-2.0 AND LGPL-3.0-or-later", 979 - "optional": true, 980 - "os": [ 981 - "win32" 982 - ], 983 - "engines": { 984 - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 985 - }, 986 - "funding": { 987 - "url": "https://opencollective.com/libvips" 988 - } 989 - }, 990 - "node_modules/@jridgewell/resolve-uri": { 991 - "version": "3.1.2", 992 - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 993 - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 994 - "dev": true, 995 - "license": "MIT", 996 - "engines": { 997 - "node": ">=6.0.0" 998 - } 999 - }, 1000 - "node_modules/@jridgewell/sourcemap-codec": { 1001 - "version": "1.5.0", 1002 - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", 1003 - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", 1004 - "dev": true, 1005 - "license": "MIT" 1006 - }, 1007 - "node_modules/@jridgewell/trace-mapping": { 1008 - "version": "0.3.9", 1009 - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 1010 - "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 1011 - "dev": true, 1012 - "license": "MIT", 1013 - "dependencies": { 1014 - "@jridgewell/resolve-uri": "^3.0.3", 1015 - "@jridgewell/sourcemap-codec": "^1.4.10" 1016 - } 1017 - }, 1018 - "node_modules/@rollup/rollup-android-arm-eabi": { 1019 - "version": "4.40.1", 1020 - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.1.tgz", 1021 - "integrity": "sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw==", 1022 - "cpu": [ 1023 - "arm" 1024 - ], 1025 - "dev": true, 1026 - "license": "MIT", 1027 - "optional": true, 1028 - "os": [ 1029 - "android" 1030 - ] 1031 - }, 1032 - "node_modules/@rollup/rollup-android-arm64": { 1033 - "version": "4.40.1", 1034 - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.1.tgz", 1035 - "integrity": "sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw==", 1036 - "cpu": [ 1037 - "arm64" 1038 - ], 1039 - "dev": true, 1040 - "license": "MIT", 1041 - "optional": true, 1042 - "os": [ 1043 - "android" 1044 - ] 1045 - }, 1046 - "node_modules/@rollup/rollup-darwin-arm64": { 1047 - "version": "4.40.1", 1048 - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.1.tgz", 1049 - "integrity": "sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA==", 1050 - "cpu": [ 1051 - "arm64" 1052 - ], 1053 - "dev": true, 1054 - "license": "MIT", 1055 - "optional": true, 1056 - "os": [ 1057 - "darwin" 1058 - ] 1059 - }, 1060 - "node_modules/@rollup/rollup-darwin-x64": { 1061 - "version": "4.40.1", 1062 - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.1.tgz", 1063 - "integrity": "sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw==", 1064 - "cpu": [ 1065 - "x64" 1066 - ], 1067 - "dev": true, 1068 - "license": "MIT", 1069 - "optional": true, 1070 - "os": [ 1071 - "darwin" 1072 - ] 1073 - }, 1074 - "node_modules/@rollup/rollup-freebsd-arm64": { 1075 - "version": "4.40.1", 1076 - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.1.tgz", 1077 - "integrity": "sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw==", 1078 - "cpu": [ 1079 - "arm64" 1080 - ], 1081 - "dev": true, 1082 - "license": "MIT", 1083 - "optional": true, 1084 - "os": [ 1085 - "freebsd" 1086 - ] 1087 - }, 1088 - "node_modules/@rollup/rollup-freebsd-x64": { 1089 - "version": "4.40.1", 1090 - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.1.tgz", 1091 - "integrity": "sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q==", 1092 - "cpu": [ 1093 - "x64" 1094 - ], 1095 - "dev": true, 1096 - "license": "MIT", 1097 - "optional": true, 1098 - "os": [ 1099 - "freebsd" 1100 - ] 1101 - }, 1102 - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { 1103 - "version": "4.40.1", 1104 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.1.tgz", 1105 - "integrity": "sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==", 1106 - "cpu": [ 1107 - "arm" 1108 - ], 1109 - "dev": true, 1110 - "license": "MIT", 1111 - "optional": true, 1112 - "os": [ 1113 - "linux" 1114 - ] 1115 - }, 1116 - "node_modules/@rollup/rollup-linux-arm-musleabihf": { 1117 - "version": "4.40.1", 1118 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.1.tgz", 1119 - "integrity": "sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==", 1120 - "cpu": [ 1121 - "arm" 1122 - ], 1123 - "dev": true, 1124 - "license": "MIT", 1125 - "optional": true, 1126 - "os": [ 1127 - "linux" 1128 - ] 1129 - }, 1130 - "node_modules/@rollup/rollup-linux-arm64-gnu": { 1131 - "version": "4.40.1", 1132 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.1.tgz", 1133 - "integrity": "sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==", 1134 - "cpu": [ 1135 - "arm64" 1136 - ], 1137 - "dev": true, 1138 - "license": "MIT", 1139 - "optional": true, 1140 - "os": [ 1141 - "linux" 1142 - ] 1143 - }, 1144 - "node_modules/@rollup/rollup-linux-arm64-musl": { 1145 - "version": "4.40.1", 1146 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.1.tgz", 1147 - "integrity": "sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==", 1148 - "cpu": [ 1149 - "arm64" 1150 - ], 1151 - "dev": true, 1152 - "license": "MIT", 1153 - "optional": true, 1154 - "os": [ 1155 - "linux" 1156 - ] 1157 - }, 1158 - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { 1159 - "version": "4.40.1", 1160 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.1.tgz", 1161 - "integrity": "sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==", 1162 - "cpu": [ 1163 - "loong64" 1164 - ], 1165 - "dev": true, 1166 - "license": "MIT", 1167 - "optional": true, 1168 - "os": [ 1169 - "linux" 1170 - ] 1171 - }, 1172 - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { 1173 - "version": "4.40.1", 1174 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.1.tgz", 1175 - "integrity": "sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==", 1176 - "cpu": [ 1177 - "ppc64" 1178 - ], 1179 - "dev": true, 1180 - "license": "MIT", 1181 - "optional": true, 1182 - "os": [ 1183 - "linux" 1184 - ] 1185 - }, 1186 - "node_modules/@rollup/rollup-linux-riscv64-gnu": { 1187 - "version": "4.40.1", 1188 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.1.tgz", 1189 - "integrity": "sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==", 1190 - "cpu": [ 1191 - "riscv64" 1192 - ], 1193 - "dev": true, 1194 - "license": "MIT", 1195 - "optional": true, 1196 - "os": [ 1197 - "linux" 1198 - ] 1199 - }, 1200 - "node_modules/@rollup/rollup-linux-riscv64-musl": { 1201 - "version": "4.40.1", 1202 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.1.tgz", 1203 - "integrity": "sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==", 1204 - "cpu": [ 1205 - "riscv64" 1206 - ], 1207 - "dev": true, 1208 - "license": "MIT", 1209 - "optional": true, 1210 - "os": [ 1211 - "linux" 1212 - ] 1213 - }, 1214 - "node_modules/@rollup/rollup-linux-s390x-gnu": { 1215 - "version": "4.40.1", 1216 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.1.tgz", 1217 - "integrity": "sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==", 1218 - "cpu": [ 1219 - "s390x" 1220 - ], 1221 - "dev": true, 1222 - "license": "MIT", 1223 - "optional": true, 1224 - "os": [ 1225 - "linux" 1226 - ] 1227 - }, 1228 - "node_modules/@rollup/rollup-linux-x64-gnu": { 1229 - "version": "4.40.1", 1230 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.1.tgz", 1231 - "integrity": "sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==", 1232 - "cpu": [ 1233 - "x64" 1234 - ], 1235 - "dev": true, 1236 - "license": "MIT", 1237 - "optional": true, 1238 - "os": [ 1239 - "linux" 1240 - ] 1241 - }, 1242 - "node_modules/@rollup/rollup-linux-x64-musl": { 1243 - "version": "4.40.1", 1244 - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.1.tgz", 1245 - "integrity": "sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==", 1246 - "cpu": [ 1247 - "x64" 1248 - ], 1249 - "dev": true, 1250 - "license": "MIT", 1251 - "optional": true, 1252 - "os": [ 1253 - "linux" 1254 - ] 1255 - }, 1256 - "node_modules/@rollup/rollup-win32-arm64-msvc": { 1257 - "version": "4.40.1", 1258 - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.1.tgz", 1259 - "integrity": "sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==", 1260 - "cpu": [ 1261 - "arm64" 1262 - ], 1263 - "dev": true, 1264 - "license": "MIT", 1265 - "optional": true, 1266 - "os": [ 1267 - "win32" 1268 - ] 1269 - }, 1270 - "node_modules/@rollup/rollup-win32-ia32-msvc": { 1271 - "version": "4.40.1", 1272 - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.1.tgz", 1273 - "integrity": "sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA==", 1274 - "cpu": [ 1275 - "ia32" 1276 - ], 1277 - "dev": true, 1278 - "license": "MIT", 1279 - "optional": true, 1280 - "os": [ 1281 - "win32" 1282 - ] 1283 - }, 1284 - "node_modules/@rollup/rollup-win32-x64-msvc": { 1285 - "version": "4.40.1", 1286 - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.1.tgz", 1287 - "integrity": "sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==", 1288 - "cpu": [ 1289 - "x64" 1290 - ], 1291 - "dev": true, 1292 - "license": "MIT", 1293 - "optional": true, 1294 - "os": [ 1295 - "win32" 1296 - ] 1297 - }, 1298 - "node_modules/@types/estree": { 1299 - "version": "1.0.7", 1300 - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", 1301 - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", 1302 - "dev": true, 1303 - "license": "MIT" 1304 - }, 1305 - "node_modules/@vitest/expect": { 1306 - "version": "3.0.9", 1307 - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.9.tgz", 1308 - "integrity": "sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig==", 1309 - "dev": true, 1310 - "license": "MIT", 1311 - "dependencies": { 1312 - "@vitest/spy": "3.0.9", 1313 - "@vitest/utils": "3.0.9", 1314 - "chai": "^5.2.0", 1315 - "tinyrainbow": "^2.0.0" 1316 - }, 1317 - "funding": { 1318 - "url": "https://opencollective.com/vitest" 1319 - } 1320 - }, 1321 - "node_modules/@vitest/mocker": { 1322 - "version": "3.0.9", 1323 - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.9.tgz", 1324 - "integrity": "sha512-ryERPIBOnvevAkTq+L1lD+DTFBRcjueL9lOUfXsLfwP92h4e+Heb+PjiqS3/OURWPtywfafK0kj++yDFjWUmrA==", 1325 - "dev": true, 1326 - "license": "MIT", 1327 - "dependencies": { 1328 - "@vitest/spy": "3.0.9", 1329 - "estree-walker": "^3.0.3", 1330 - "magic-string": "^0.30.17" 1331 - }, 1332 - "funding": { 1333 - "url": "https://opencollective.com/vitest" 1334 - }, 1335 - "peerDependencies": { 1336 - "msw": "^2.4.9", 1337 - "vite": "^5.0.0 || ^6.0.0" 1338 - }, 1339 - "peerDependenciesMeta": { 1340 - "msw": { 1341 - "optional": true 1342 - }, 1343 - "vite": { 1344 - "optional": true 1345 - } 1346 - } 1347 - }, 1348 - "node_modules/@vitest/pretty-format": { 1349 - "version": "3.1.2", 1350 - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.2.tgz", 1351 - "integrity": "sha512-R0xAiHuWeDjTSB3kQ3OQpT8Rx3yhdOAIm/JM4axXxnG7Q/fS8XUwggv/A4xzbQA+drYRjzkMnpYnOGAc4oeq8w==", 1352 - "dev": true, 1353 - "license": "MIT", 1354 - "dependencies": { 1355 - "tinyrainbow": "^2.0.0" 1356 - }, 1357 - "funding": { 1358 - "url": "https://opencollective.com/vitest" 1359 - } 1360 - }, 1361 - "node_modules/@vitest/runner": { 1362 - "version": "3.0.9", 1363 - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.9.tgz", 1364 - "integrity": "sha512-NX9oUXgF9HPfJSwl8tUZCMP1oGx2+Sf+ru6d05QjzQz4OwWg0psEzwY6VexP2tTHWdOkhKHUIZH+fS6nA7jfOw==", 1365 - "dev": true, 1366 - "license": "MIT", 1367 - "dependencies": { 1368 - "@vitest/utils": "3.0.9", 1369 - "pathe": "^2.0.3" 1370 - }, 1371 - "funding": { 1372 - "url": "https://opencollective.com/vitest" 1373 - } 1374 - }, 1375 - "node_modules/@vitest/snapshot": { 1376 - "version": "3.0.9", 1377 - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.9.tgz", 1378 - "integrity": "sha512-AiLUiuZ0FuA+/8i19mTYd+re5jqjEc2jZbgJ2up0VY0Ddyyxg/uUtBDpIFAy4uzKaQxOW8gMgBdAJJ2ydhu39A==", 1379 - "dev": true, 1380 - "license": "MIT", 1381 - "dependencies": { 1382 - "@vitest/pretty-format": "3.0.9", 1383 - "magic-string": "^0.30.17", 1384 - "pathe": "^2.0.3" 1385 - }, 1386 - "funding": { 1387 - "url": "https://opencollective.com/vitest" 1388 - } 1389 - }, 1390 - "node_modules/@vitest/snapshot/node_modules/@vitest/pretty-format": { 1391 - "version": "3.0.9", 1392 - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.9.tgz", 1393 - "integrity": "sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==", 1394 - "dev": true, 1395 - "license": "MIT", 1396 - "dependencies": { 1397 - "tinyrainbow": "^2.0.0" 1398 - }, 1399 - "funding": { 1400 - "url": "https://opencollective.com/vitest" 1401 - } 1402 - }, 1403 - "node_modules/@vitest/spy": { 1404 - "version": "3.0.9", 1405 - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.9.tgz", 1406 - "integrity": "sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ==", 1407 - "dev": true, 1408 - "license": "MIT", 1409 - "dependencies": { 1410 - "tinyspy": "^3.0.2" 1411 - }, 1412 - "funding": { 1413 - "url": "https://opencollective.com/vitest" 1414 - } 1415 - }, 1416 - "node_modules/@vitest/utils": { 1417 - "version": "3.0.9", 1418 - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.9.tgz", 1419 - "integrity": "sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==", 1420 - "dev": true, 1421 - "license": "MIT", 1422 - "dependencies": { 1423 - "@vitest/pretty-format": "3.0.9", 1424 - "loupe": "^3.1.3", 1425 - "tinyrainbow": "^2.0.0" 1426 - }, 1427 - "funding": { 1428 - "url": "https://opencollective.com/vitest" 1429 - } 1430 - }, 1431 - "node_modules/@vitest/utils/node_modules/@vitest/pretty-format": { 1432 - "version": "3.0.9", 1433 - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.9.tgz", 1434 - "integrity": "sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==", 1435 - "dev": true, 1436 - "license": "MIT", 1437 - "dependencies": { 1438 - "tinyrainbow": "^2.0.0" 1439 - }, 1440 - "funding": { 1441 - "url": "https://opencollective.com/vitest" 1442 - } 1443 - }, 1444 - "node_modules/acorn": { 1445 - "version": "8.14.0", 1446 - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", 1447 - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", 1448 - "dev": true, 1449 - "license": "MIT", 1450 - "bin": { 1451 - "acorn": "bin/acorn" 1452 - }, 1453 - "engines": { 1454 - "node": ">=0.4.0" 1455 - } 1456 - }, 1457 - "node_modules/acorn-walk": { 1458 - "version": "8.3.2", 1459 - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", 1460 - "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", 1461 - "dev": true, 1462 - "license": "MIT", 1463 - "engines": { 1464 - "node": ">=0.4.0" 1465 - } 1466 - }, 1467 - "node_modules/as-table": { 1468 - "version": "1.0.55", 1469 - "resolved": "https://registry.npmjs.org/as-table/-/as-table-1.0.55.tgz", 1470 - "integrity": "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==", 1471 - "dev": true, 1472 - "license": "MIT", 1473 - "dependencies": { 1474 - "printable-characters": "^1.0.42" 1475 - } 1476 - }, 1477 - "node_modules/assertion-error": { 1478 - "version": "2.0.1", 1479 - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", 1480 - "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", 1481 - "dev": true, 1482 - "license": "MIT", 1483 - "engines": { 1484 - "node": ">=12" 1485 - } 1486 - }, 1487 - "node_modules/birpc": { 1488 - "version": "0.2.14", 1489 - "resolved": "https://registry.npmjs.org/birpc/-/birpc-0.2.14.tgz", 1490 - "integrity": "sha512-37FHE8rqsYM5JEKCnXFyHpBCzvgHEExwVVTq+nUmloInU7l8ezD1TpOhKpS8oe1DTYFqEK27rFZVKG43oTqXRA==", 1491 - "dev": true, 1492 - "license": "MIT", 1493 - "funding": { 1494 - "url": "https://github.com/sponsors/antfu" 1495 - } 1496 - }, 1497 - "node_modules/blake3-wasm": { 1498 - "version": "2.1.5", 1499 - "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", 1500 - "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", 1501 - "dev": true, 1502 - "license": "MIT" 1503 - }, 1504 - "node_modules/cac": { 1505 - "version": "6.7.14", 1506 - "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", 1507 - "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", 1508 - "dev": true, 1509 - "license": "MIT", 1510 - "engines": { 1511 - "node": ">=8" 1512 - } 1513 - }, 1514 - "node_modules/chai": { 1515 - "version": "5.2.0", 1516 - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", 1517 - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", 1518 - "dev": true, 1519 - "license": "MIT", 1520 - "dependencies": { 1521 - "assertion-error": "^2.0.1", 1522 - "check-error": "^2.1.1", 1523 - "deep-eql": "^5.0.1", 1524 - "loupe": "^3.1.0", 1525 - "pathval": "^2.0.0" 1526 - }, 1527 - "engines": { 1528 - "node": ">=12" 1529 - } 1530 - }, 1531 - "node_modules/check-error": { 1532 - "version": "2.1.1", 1533 - "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", 1534 - "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", 1535 - "dev": true, 1536 - "license": "MIT", 1537 - "engines": { 1538 - "node": ">= 16" 1539 - } 1540 - }, 1541 - "node_modules/cjs-module-lexer": { 1542 - "version": "1.4.3", 1543 - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", 1544 - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", 1545 - "dev": true, 1546 - "license": "MIT" 1547 - }, 1548 - "node_modules/color": { 1549 - "version": "4.2.3", 1550 - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", 1551 - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", 1552 - "dev": true, 1553 - "license": "MIT", 1554 - "optional": true, 1555 - "dependencies": { 1556 - "color-convert": "^2.0.1", 1557 - "color-string": "^1.9.0" 1558 - }, 1559 - "engines": { 1560 - "node": ">=12.5.0" 1561 - } 1562 - }, 1563 - "node_modules/color-convert": { 1564 - "version": "2.0.1", 1565 - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1566 - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1567 - "dev": true, 1568 - "license": "MIT", 1569 - "optional": true, 1570 - "dependencies": { 1571 - "color-name": "~1.1.4" 1572 - }, 1573 - "engines": { 1574 - "node": ">=7.0.0" 1575 - } 1576 - }, 1577 - "node_modules/color-name": { 1578 - "version": "1.1.4", 1579 - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1580 - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1581 - "dev": true, 1582 - "license": "MIT", 1583 - "optional": true 1584 - }, 1585 - "node_modules/color-string": { 1586 - "version": "1.9.1", 1587 - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", 1588 - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", 1589 - "dev": true, 1590 - "license": "MIT", 1591 - "optional": true, 1592 - "dependencies": { 1593 - "color-name": "^1.0.0", 1594 - "simple-swizzle": "^0.2.2" 1595 - } 1596 - }, 1597 - "node_modules/cookie": { 1598 - "version": "0.7.2", 1599 - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", 1600 - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", 1601 - "dev": true, 1602 - "license": "MIT", 1603 - "engines": { 1604 - "node": ">= 0.6" 1605 - } 1606 - }, 1607 - "node_modules/data-uri-to-buffer": { 1608 - "version": "2.0.2", 1609 - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz", 1610 - "integrity": "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==", 1611 - "dev": true, 1612 - "license": "MIT" 1613 - }, 1614 - "node_modules/debug": { 1615 - "version": "4.4.0", 1616 - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 1617 - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 1618 - "dev": true, 1619 - "license": "MIT", 1620 - "dependencies": { 1621 - "ms": "^2.1.3" 1622 - }, 1623 - "engines": { 1624 - "node": ">=6.0" 1625 - }, 1626 - "peerDependenciesMeta": { 1627 - "supports-color": { 1628 - "optional": true 1629 - } 1630 - } 1631 - }, 1632 - "node_modules/deep-eql": { 1633 - "version": "5.0.2", 1634 - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", 1635 - "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", 1636 - "dev": true, 1637 - "license": "MIT", 1638 - "engines": { 1639 - "node": ">=6" 1640 - } 1641 - }, 1642 - "node_modules/defu": { 1643 - "version": "6.1.4", 1644 - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", 1645 - "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", 1646 - "dev": true, 1647 - "license": "MIT" 1648 - }, 1649 - "node_modules/detect-libc": { 1650 - "version": "2.0.4", 1651 - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", 1652 - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", 1653 - "dev": true, 1654 - "license": "Apache-2.0", 1655 - "optional": true, 1656 - "engines": { 1657 - "node": ">=8" 1658 - } 1659 - }, 1660 - "node_modules/devalue": { 1661 - "version": "4.3.3", 1662 - "resolved": "https://registry.npmjs.org/devalue/-/devalue-4.3.3.tgz", 1663 - "integrity": "sha512-UH8EL6H2ifcY8TbD2QsxwCC/pr5xSwPvv85LrLXVihmHVC3T3YqTCIwnR5ak0yO1KYqlxrPVOA/JVZJYPy2ATg==", 1664 - "dev": true, 1665 - "license": "MIT" 1666 - }, 1667 - "node_modules/es-module-lexer": { 1668 - "version": "1.7.0", 1669 - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", 1670 - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", 1671 - "dev": true, 1672 - "license": "MIT" 1673 - }, 1674 - "node_modules/esbuild": { 1675 - "version": "0.25.3", 1676 - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", 1677 - "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", 1678 - "dev": true, 1679 - "hasInstallScript": true, 1680 - "license": "MIT", 1681 - "bin": { 1682 - "esbuild": "bin/esbuild" 1683 - }, 1684 - "engines": { 1685 - "node": ">=18" 1686 - }, 1687 - "optionalDependencies": { 1688 - "@esbuild/aix-ppc64": "0.25.3", 1689 - "@esbuild/android-arm": "0.25.3", 1690 - "@esbuild/android-arm64": "0.25.3", 1691 - "@esbuild/android-x64": "0.25.3", 1692 - "@esbuild/darwin-arm64": "0.25.3", 1693 - "@esbuild/darwin-x64": "0.25.3", 1694 - "@esbuild/freebsd-arm64": "0.25.3", 1695 - "@esbuild/freebsd-x64": "0.25.3", 1696 - "@esbuild/linux-arm": "0.25.3", 1697 - "@esbuild/linux-arm64": "0.25.3", 1698 - "@esbuild/linux-ia32": "0.25.3", 1699 - "@esbuild/linux-loong64": "0.25.3", 1700 - "@esbuild/linux-mips64el": "0.25.3", 1701 - "@esbuild/linux-ppc64": "0.25.3", 1702 - "@esbuild/linux-riscv64": "0.25.3", 1703 - "@esbuild/linux-s390x": "0.25.3", 1704 - "@esbuild/linux-x64": "0.25.3", 1705 - "@esbuild/netbsd-arm64": "0.25.3", 1706 - "@esbuild/netbsd-x64": "0.25.3", 1707 - "@esbuild/openbsd-arm64": "0.25.3", 1708 - "@esbuild/openbsd-x64": "0.25.3", 1709 - "@esbuild/sunos-x64": "0.25.3", 1710 - "@esbuild/win32-arm64": "0.25.3", 1711 - "@esbuild/win32-ia32": "0.25.3", 1712 - "@esbuild/win32-x64": "0.25.3" 1713 - } 1714 - }, 1715 - "node_modules/estree-walker": { 1716 - "version": "3.0.3", 1717 - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", 1718 - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", 1719 - "dev": true, 1720 - "license": "MIT", 1721 - "dependencies": { 1722 - "@types/estree": "^1.0.0" 1723 - } 1724 - }, 1725 - "node_modules/exit-hook": { 1726 - "version": "2.2.1", 1727 - "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz", 1728 - "integrity": "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==", 1729 - "dev": true, 1730 - "license": "MIT", 1731 - "engines": { 1732 - "node": ">=6" 1733 - }, 1734 - "funding": { 1735 - "url": "https://github.com/sponsors/sindresorhus" 1736 - } 1737 - }, 1738 - "node_modules/expect-type": { 1739 - "version": "1.2.1", 1740 - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", 1741 - "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", 1742 - "dev": true, 1743 - "license": "Apache-2.0", 1744 - "engines": { 1745 - "node": ">=12.0.0" 1746 - } 1747 - }, 1748 - "node_modules/exsolve": { 1749 - "version": "1.0.5", 1750 - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.5.tgz", 1751 - "integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==", 1752 - "dev": true, 1753 - "license": "MIT" 1754 - }, 1755 - "node_modules/fdir": { 1756 - "version": "6.4.4", 1757 - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", 1758 - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", 1759 - "dev": true, 1760 - "license": "MIT", 1761 - "peerDependencies": { 1762 - "picomatch": "^3 || ^4" 1763 - }, 1764 - "peerDependenciesMeta": { 1765 - "picomatch": { 1766 - "optional": true 1767 - } 1768 - } 1769 - }, 1770 - "node_modules/fsevents": { 1771 - "version": "2.3.3", 1772 - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 1773 - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 1774 - "dev": true, 1775 - "hasInstallScript": true, 1776 - "license": "MIT", 1777 - "optional": true, 1778 - "os": [ 1779 - "darwin" 1780 - ], 1781 - "engines": { 1782 - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1783 - } 1784 - }, 1785 - "node_modules/get-source": { 1786 - "version": "2.0.12", 1787 - "resolved": "https://registry.npmjs.org/get-source/-/get-source-2.0.12.tgz", 1788 - "integrity": "sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==", 1789 - "dev": true, 1790 - "license": "Unlicense", 1791 - "dependencies": { 1792 - "data-uri-to-buffer": "^2.0.0", 1793 - "source-map": "^0.6.1" 1794 - } 1795 - }, 1796 - "node_modules/glob-to-regexp": { 1797 - "version": "0.4.1", 1798 - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", 1799 - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", 1800 - "dev": true, 1801 - "license": "BSD-2-Clause" 1802 - }, 1803 - "node_modules/is-arrayish": { 1804 - "version": "0.3.2", 1805 - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", 1806 - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", 1807 - "dev": true, 1808 - "license": "MIT", 1809 - "optional": true 1810 - }, 1811 - "node_modules/loupe": { 1812 - "version": "3.1.3", 1813 - "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", 1814 - "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", 1815 - "dev": true, 1816 - "license": "MIT" 1817 - }, 1818 - "node_modules/magic-string": { 1819 - "version": "0.30.17", 1820 - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", 1821 - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", 1822 - "dev": true, 1823 - "license": "MIT", 1824 - "dependencies": { 1825 - "@jridgewell/sourcemap-codec": "^1.5.0" 1826 - } 1827 - }, 1828 - "node_modules/mime": { 1829 - "version": "3.0.0", 1830 - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", 1831 - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", 1832 - "dev": true, 1833 - "license": "MIT", 1834 - "bin": { 1835 - "mime": "cli.js" 1836 - }, 1837 - "engines": { 1838 - "node": ">=10.0.0" 1839 - } 1840 - }, 1841 - "node_modules/miniflare": { 1842 - "version": "4.20250428.1", 1843 - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250428.1.tgz", 1844 - "integrity": "sha512-M3qcJXjeAEimHrEeWXEhrJiC3YHB5M3QSqqK67pOTI+lHn0QyVG/2iFUjVJ/nv+i10uxeAEva8GRGeu+tKRCmQ==", 1845 - "dev": true, 1846 - "license": "MIT", 1847 - "dependencies": { 1848 - "@cspotcode/source-map-support": "0.8.1", 1849 - "acorn": "8.14.0", 1850 - "acorn-walk": "8.3.2", 1851 - "exit-hook": "2.2.1", 1852 - "glob-to-regexp": "0.4.1", 1853 - "stoppable": "1.1.0", 1854 - "undici": "^5.28.5", 1855 - "workerd": "1.20250428.0", 1856 - "ws": "8.18.0", 1857 - "youch": "3.3.4", 1858 - "zod": "3.22.3" 1859 - }, 1860 - "bin": { 1861 - "miniflare": "bootstrap.js" 1862 - }, 1863 - "engines": { 1864 - "node": ">=18.0.0" 1865 - } 1866 - }, 1867 - "node_modules/miniflare/node_modules/zod": { 1868 - "version": "3.22.3", 1869 - "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz", 1870 - "integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==", 1871 - "dev": true, 1872 - "license": "MIT", 1873 - "funding": { 1874 - "url": "https://github.com/sponsors/colinhacks" 1875 - } 1876 - }, 1877 - "node_modules/ms": { 1878 - "version": "2.1.3", 1879 - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1880 - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1881 - "dev": true, 1882 - "license": "MIT" 1883 - }, 1884 - "node_modules/mustache": { 1885 - "version": "4.2.0", 1886 - "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", 1887 - "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", 1888 - "dev": true, 1889 - "license": "MIT", 1890 - "bin": { 1891 - "mustache": "bin/mustache" 1892 - } 1893 - }, 1894 - "node_modules/nanoid": { 1895 - "version": "3.3.11", 1896 - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", 1897 - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", 1898 - "dev": true, 1899 - "funding": [ 1900 - { 1901 - "type": "github", 1902 - "url": "https://github.com/sponsors/ai" 1903 - } 1904 - ], 1905 - "license": "MIT", 1906 - "bin": { 1907 - "nanoid": "bin/nanoid.cjs" 1908 - }, 1909 - "engines": { 1910 - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 1911 - } 1912 - }, 1913 - "node_modules/ohash": { 1914 - "version": "2.0.11", 1915 - "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", 1916 - "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", 1917 - "dev": true, 1918 - "license": "MIT" 1919 - }, 1920 - "node_modules/path-to-regexp": { 1921 - "version": "6.3.0", 1922 - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", 1923 - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", 1924 - "dev": true, 1925 - "license": "MIT" 1926 - }, 1927 - "node_modules/pathe": { 1928 - "version": "2.0.3", 1929 - "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", 1930 - "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", 1931 - "dev": true, 1932 - "license": "MIT" 1933 - }, 1934 - "node_modules/pathval": { 1935 - "version": "2.0.0", 1936 - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", 1937 - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", 1938 - "dev": true, 1939 - "license": "MIT", 1940 - "engines": { 1941 - "node": ">= 14.16" 1942 - } 1943 - }, 1944 - "node_modules/picocolors": { 1945 - "version": "1.1.1", 1946 - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", 1947 - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", 1948 - "dev": true, 1949 - "license": "ISC" 1950 - }, 1951 - "node_modules/picomatch": { 1952 - "version": "4.0.2", 1953 - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", 1954 - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", 1955 - "dev": true, 1956 - "license": "MIT", 1957 - "engines": { 1958 - "node": ">=12" 1959 - }, 1960 - "funding": { 1961 - "url": "https://github.com/sponsors/jonschlinkert" 1962 - } 1963 - }, 1964 - "node_modules/postcss": { 1965 - "version": "8.5.3", 1966 - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", 1967 - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", 1968 - "dev": true, 1969 - "funding": [ 1970 - { 1971 - "type": "opencollective", 1972 - "url": "https://opencollective.com/postcss/" 1973 - }, 1974 - { 1975 - "type": "tidelift", 1976 - "url": "https://tidelift.com/funding/github/npm/postcss" 1977 - }, 1978 - { 1979 - "type": "github", 1980 - "url": "https://github.com/sponsors/ai" 1981 - } 1982 - ], 1983 - "license": "MIT", 1984 - "dependencies": { 1985 - "nanoid": "^3.3.8", 1986 - "picocolors": "^1.1.1", 1987 - "source-map-js": "^1.2.1" 1988 - }, 1989 - "engines": { 1990 - "node": "^10 || ^12 || >=14" 1991 - } 1992 - }, 1993 - "node_modules/printable-characters": { 1994 - "version": "1.0.42", 1995 - "resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz", 1996 - "integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==", 1997 - "dev": true, 1998 - "license": "Unlicense" 1999 - }, 2000 - "node_modules/rollup": { 2001 - "version": "4.40.1", 2002 - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.1.tgz", 2003 - "integrity": "sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==", 2004 - "dev": true, 2005 - "license": "MIT", 2006 - "dependencies": { 2007 - "@types/estree": "1.0.7" 2008 - }, 2009 - "bin": { 2010 - "rollup": "dist/bin/rollup" 2011 - }, 2012 - "engines": { 2013 - "node": ">=18.0.0", 2014 - "npm": ">=8.0.0" 2015 - }, 2016 - "optionalDependencies": { 2017 - "@rollup/rollup-android-arm-eabi": "4.40.1", 2018 - "@rollup/rollup-android-arm64": "4.40.1", 2019 - "@rollup/rollup-darwin-arm64": "4.40.1", 2020 - "@rollup/rollup-darwin-x64": "4.40.1", 2021 - "@rollup/rollup-freebsd-arm64": "4.40.1", 2022 - "@rollup/rollup-freebsd-x64": "4.40.1", 2023 - "@rollup/rollup-linux-arm-gnueabihf": "4.40.1", 2024 - "@rollup/rollup-linux-arm-musleabihf": "4.40.1", 2025 - "@rollup/rollup-linux-arm64-gnu": "4.40.1", 2026 - "@rollup/rollup-linux-arm64-musl": "4.40.1", 2027 - "@rollup/rollup-linux-loongarch64-gnu": "4.40.1", 2028 - "@rollup/rollup-linux-powerpc64le-gnu": "4.40.1", 2029 - "@rollup/rollup-linux-riscv64-gnu": "4.40.1", 2030 - "@rollup/rollup-linux-riscv64-musl": "4.40.1", 2031 - "@rollup/rollup-linux-s390x-gnu": "4.40.1", 2032 - "@rollup/rollup-linux-x64-gnu": "4.40.1", 2033 - "@rollup/rollup-linux-x64-musl": "4.40.1", 2034 - "@rollup/rollup-win32-arm64-msvc": "4.40.1", 2035 - "@rollup/rollup-win32-ia32-msvc": "4.40.1", 2036 - "@rollup/rollup-win32-x64-msvc": "4.40.1", 2037 - "fsevents": "~2.3.2" 2038 - } 2039 - }, 2040 - "node_modules/semver": { 2041 - "version": "7.7.1", 2042 - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", 2043 - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", 2044 - "dev": true, 2045 - "license": "ISC", 2046 - "bin": { 2047 - "semver": "bin/semver.js" 2048 - }, 2049 - "engines": { 2050 - "node": ">=10" 2051 - } 2052 - }, 2053 - "node_modules/sharp": { 2054 - "version": "0.33.5", 2055 - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", 2056 - "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", 2057 - "dev": true, 2058 - "hasInstallScript": true, 2059 - "license": "Apache-2.0", 2060 - "optional": true, 2061 - "dependencies": { 2062 - "color": "^4.2.3", 2063 - "detect-libc": "^2.0.3", 2064 - "semver": "^7.6.3" 2065 - }, 2066 - "engines": { 2067 - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 2068 - }, 2069 - "funding": { 2070 - "url": "https://opencollective.com/libvips" 2071 - }, 2072 - "optionalDependencies": { 2073 - "@img/sharp-darwin-arm64": "0.33.5", 2074 - "@img/sharp-darwin-x64": "0.33.5", 2075 - "@img/sharp-libvips-darwin-arm64": "1.0.4", 2076 - "@img/sharp-libvips-darwin-x64": "1.0.4", 2077 - "@img/sharp-libvips-linux-arm": "1.0.5", 2078 - "@img/sharp-libvips-linux-arm64": "1.0.4", 2079 - "@img/sharp-libvips-linux-s390x": "1.0.4", 2080 - "@img/sharp-libvips-linux-x64": "1.0.4", 2081 - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", 2082 - "@img/sharp-libvips-linuxmusl-x64": "1.0.4", 2083 - "@img/sharp-linux-arm": "0.33.5", 2084 - "@img/sharp-linux-arm64": "0.33.5", 2085 - "@img/sharp-linux-s390x": "0.33.5", 2086 - "@img/sharp-linux-x64": "0.33.5", 2087 - "@img/sharp-linuxmusl-arm64": "0.33.5", 2088 - "@img/sharp-linuxmusl-x64": "0.33.5", 2089 - "@img/sharp-wasm32": "0.33.5", 2090 - "@img/sharp-win32-ia32": "0.33.5", 2091 - "@img/sharp-win32-x64": "0.33.5" 2092 - } 2093 - }, 2094 - "node_modules/siginfo": { 2095 - "version": "2.0.0", 2096 - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", 2097 - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", 2098 - "dev": true, 2099 - "license": "ISC" 2100 - }, 2101 - "node_modules/simple-swizzle": { 2102 - "version": "0.2.2", 2103 - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", 2104 - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", 2105 - "dev": true, 2106 - "license": "MIT", 2107 - "optional": true, 2108 - "dependencies": { 2109 - "is-arrayish": "^0.3.1" 2110 - } 2111 - }, 2112 - "node_modules/source-map": { 2113 - "version": "0.6.1", 2114 - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 2115 - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 2116 - "dev": true, 2117 - "license": "BSD-3-Clause", 2118 - "engines": { 2119 - "node": ">=0.10.0" 2120 - } 2121 - }, 2122 - "node_modules/source-map-js": { 2123 - "version": "1.2.1", 2124 - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", 2125 - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", 2126 - "dev": true, 2127 - "license": "BSD-3-Clause", 2128 - "engines": { 2129 - "node": ">=0.10.0" 2130 - } 2131 - }, 2132 - "node_modules/stackback": { 2133 - "version": "0.0.2", 2134 - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", 2135 - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", 2136 - "dev": true, 2137 - "license": "MIT" 2138 - }, 2139 - "node_modules/stacktracey": { 2140 - "version": "2.1.8", 2141 - "resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-2.1.8.tgz", 2142 - "integrity": "sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==", 2143 - "dev": true, 2144 - "license": "Unlicense", 2145 - "dependencies": { 2146 - "as-table": "^1.0.36", 2147 - "get-source": "^2.0.12" 2148 - } 2149 - }, 2150 - "node_modules/std-env": { 2151 - "version": "3.9.0", 2152 - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", 2153 - "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", 2154 - "dev": true, 2155 - "license": "MIT" 2156 - }, 2157 - "node_modules/stoppable": { 2158 - "version": "1.1.0", 2159 - "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", 2160 - "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", 2161 - "dev": true, 2162 - "license": "MIT", 2163 - "engines": { 2164 - "node": ">=4", 2165 - "npm": ">=6" 2166 - } 2167 - }, 2168 - "node_modules/tinybench": { 2169 - "version": "2.9.0", 2170 - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", 2171 - "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", 2172 - "dev": true, 2173 - "license": "MIT" 2174 - }, 2175 - "node_modules/tinyexec": { 2176 - "version": "0.3.2", 2177 - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", 2178 - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", 2179 - "dev": true, 2180 - "license": "MIT" 2181 - }, 2182 - "node_modules/tinyglobby": { 2183 - "version": "0.2.13", 2184 - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", 2185 - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", 2186 - "dev": true, 2187 - "license": "MIT", 2188 - "dependencies": { 2189 - "fdir": "^6.4.4", 2190 - "picomatch": "^4.0.2" 2191 - }, 2192 - "engines": { 2193 - "node": ">=12.0.0" 2194 - }, 2195 - "funding": { 2196 - "url": "https://github.com/sponsors/SuperchupuDev" 2197 - } 2198 - }, 2199 - "node_modules/tinypool": { 2200 - "version": "1.0.2", 2201 - "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", 2202 - "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", 2203 - "dev": true, 2204 - "license": "MIT", 2205 - "engines": { 2206 - "node": "^18.0.0 || >=20.0.0" 2207 - } 2208 - }, 2209 - "node_modules/tinyrainbow": { 2210 - "version": "2.0.0", 2211 - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", 2212 - "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", 2213 - "dev": true, 2214 - "license": "MIT", 2215 - "engines": { 2216 - "node": ">=14.0.0" 2217 - } 2218 - }, 2219 - "node_modules/tinyspy": { 2220 - "version": "3.0.2", 2221 - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", 2222 - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", 2223 - "dev": true, 2224 - "license": "MIT", 2225 - "engines": { 2226 - "node": ">=14.0.0" 2227 - } 2228 - }, 2229 - "node_modules/tslib": { 2230 - "version": "2.8.1", 2231 - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 2232 - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 2233 - "dev": true, 2234 - "license": "0BSD", 2235 - "optional": true 2236 - }, 2237 - "node_modules/ufo": { 2238 - "version": "1.6.1", 2239 - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", 2240 - "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", 2241 - "dev": true, 2242 - "license": "MIT" 2243 - }, 2244 - "node_modules/undici": { 2245 - "version": "5.29.0", 2246 - "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", 2247 - "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", 2248 - "dev": true, 2249 - "license": "MIT", 2250 - "dependencies": { 2251 - "@fastify/busboy": "^2.0.0" 2252 - }, 2253 - "engines": { 2254 - "node": ">=14.0" 2255 - } 2256 - }, 2257 - "node_modules/unenv": { 2258 - "version": "2.0.0-rc.15", 2259 - "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.15.tgz", 2260 - "integrity": "sha512-J/rEIZU8w6FOfLNz/hNKsnY+fFHWnu9MH4yRbSZF3xbbGHovcetXPs7sD+9p8L6CeNC//I9bhRYAOsBt2u7/OA==", 2261 - "dev": true, 2262 - "license": "MIT", 2263 - "dependencies": { 2264 - "defu": "^6.1.4", 2265 - "exsolve": "^1.0.4", 2266 - "ohash": "^2.0.11", 2267 - "pathe": "^2.0.3", 2268 - "ufo": "^1.5.4" 2269 - } 2270 - }, 2271 - "node_modules/vite": { 2272 - "version": "6.3.4", 2273 - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.4.tgz", 2274 - "integrity": "sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==", 2275 - "dev": true, 2276 - "license": "MIT", 2277 - "dependencies": { 2278 - "esbuild": "^0.25.0", 2279 - "fdir": "^6.4.4", 2280 - "picomatch": "^4.0.2", 2281 - "postcss": "^8.5.3", 2282 - "rollup": "^4.34.9", 2283 - "tinyglobby": "^0.2.13" 2284 - }, 2285 - "bin": { 2286 - "vite": "bin/vite.js" 2287 - }, 2288 - "engines": { 2289 - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" 2290 - }, 2291 - "funding": { 2292 - "url": "https://github.com/vitejs/vite?sponsor=1" 2293 - }, 2294 - "optionalDependencies": { 2295 - "fsevents": "~2.3.3" 2296 - }, 2297 - "peerDependencies": { 2298 - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", 2299 - "jiti": ">=1.21.0", 2300 - "less": "*", 2301 - "lightningcss": "^1.21.0", 2302 - "sass": "*", 2303 - "sass-embedded": "*", 2304 - "stylus": "*", 2305 - "sugarss": "*", 2306 - "terser": "^5.16.0", 2307 - "tsx": "^4.8.1", 2308 - "yaml": "^2.4.2" 2309 - }, 2310 - "peerDependenciesMeta": { 2311 - "@types/node": { 2312 - "optional": true 2313 - }, 2314 - "jiti": { 2315 - "optional": true 2316 - }, 2317 - "less": { 2318 - "optional": true 2319 - }, 2320 - "lightningcss": { 2321 - "optional": true 2322 - }, 2323 - "sass": { 2324 - "optional": true 2325 - }, 2326 - "sass-embedded": { 2327 - "optional": true 2328 - }, 2329 - "stylus": { 2330 - "optional": true 2331 - }, 2332 - "sugarss": { 2333 - "optional": true 2334 - }, 2335 - "terser": { 2336 - "optional": true 2337 - }, 2338 - "tsx": { 2339 - "optional": true 2340 - }, 2341 - "yaml": { 2342 - "optional": true 2343 - } 2344 - } 2345 - }, 2346 - "node_modules/vite-node": { 2347 - "version": "3.0.9", 2348 - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.9.tgz", 2349 - "integrity": "sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==", 2350 - "dev": true, 2351 - "license": "MIT", 2352 - "dependencies": { 2353 - "cac": "^6.7.14", 2354 - "debug": "^4.4.0", 2355 - "es-module-lexer": "^1.6.0", 2356 - "pathe": "^2.0.3", 2357 - "vite": "^5.0.0 || ^6.0.0" 2358 - }, 2359 - "bin": { 2360 - "vite-node": "vite-node.mjs" 2361 - }, 2362 - "engines": { 2363 - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" 2364 - }, 2365 - "funding": { 2366 - "url": "https://opencollective.com/vitest" 2367 - } 2368 - }, 2369 - "node_modules/vitest": { 2370 - "version": "3.0.9", 2371 - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.9.tgz", 2372 - "integrity": "sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ==", 2373 - "dev": true, 2374 - "license": "MIT", 2375 - "dependencies": { 2376 - "@vitest/expect": "3.0.9", 2377 - "@vitest/mocker": "3.0.9", 2378 - "@vitest/pretty-format": "^3.0.9", 2379 - "@vitest/runner": "3.0.9", 2380 - "@vitest/snapshot": "3.0.9", 2381 - "@vitest/spy": "3.0.9", 2382 - "@vitest/utils": "3.0.9", 2383 - "chai": "^5.2.0", 2384 - "debug": "^4.4.0", 2385 - "expect-type": "^1.1.0", 2386 - "magic-string": "^0.30.17", 2387 - "pathe": "^2.0.3", 2388 - "std-env": "^3.8.0", 2389 - "tinybench": "^2.9.0", 2390 - "tinyexec": "^0.3.2", 2391 - "tinypool": "^1.0.2", 2392 - "tinyrainbow": "^2.0.0", 2393 - "vite": "^5.0.0 || ^6.0.0", 2394 - "vite-node": "3.0.9", 2395 - "why-is-node-running": "^2.3.0" 2396 - }, 2397 - "bin": { 2398 - "vitest": "vitest.mjs" 2399 - }, 2400 - "engines": { 2401 - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" 2402 - }, 2403 - "funding": { 2404 - "url": "https://opencollective.com/vitest" 2405 - }, 2406 - "peerDependencies": { 2407 - "@edge-runtime/vm": "*", 2408 - "@types/debug": "^4.1.12", 2409 - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", 2410 - "@vitest/browser": "3.0.9", 2411 - "@vitest/ui": "3.0.9", 2412 - "happy-dom": "*", 2413 - "jsdom": "*" 2414 - }, 2415 - "peerDependenciesMeta": { 2416 - "@edge-runtime/vm": { 2417 - "optional": true 2418 - }, 2419 - "@types/debug": { 2420 - "optional": true 2421 - }, 2422 - "@types/node": { 2423 - "optional": true 2424 - }, 2425 - "@vitest/browser": { 2426 - "optional": true 2427 - }, 2428 - "@vitest/ui": { 2429 - "optional": true 2430 - }, 2431 - "happy-dom": { 2432 - "optional": true 2433 - }, 2434 - "jsdom": { 2435 - "optional": true 2436 - } 2437 - } 2438 - }, 2439 - "node_modules/why-is-node-running": { 2440 - "version": "2.3.0", 2441 - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", 2442 - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", 2443 - "dev": true, 2444 - "license": "MIT", 2445 - "dependencies": { 2446 - "siginfo": "^2.0.0", 2447 - "stackback": "0.0.2" 2448 - }, 2449 - "bin": { 2450 - "why-is-node-running": "cli.js" 2451 - }, 2452 - "engines": { 2453 - "node": ">=8" 2454 - } 2455 - }, 2456 - "node_modules/workerd": { 2457 - "version": "1.20250428.0", 2458 - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250428.0.tgz", 2459 - "integrity": "sha512-JJNWkHkwPQKQdvtM9UORijgYdcdJsihA4SfYjwh02IUQsdMyZ9jizV1sX9yWi9B9ptlohTW8UNHJEATuphGgdg==", 2460 - "dev": true, 2461 - "hasInstallScript": true, 2462 - "license": "Apache-2.0", 2463 - "bin": { 2464 - "workerd": "bin/workerd" 2465 - }, 2466 - "engines": { 2467 - "node": ">=16" 2468 - }, 2469 - "optionalDependencies": { 2470 - "@cloudflare/workerd-darwin-64": "1.20250428.0", 2471 - "@cloudflare/workerd-darwin-arm64": "1.20250428.0", 2472 - "@cloudflare/workerd-linux-64": "1.20250428.0", 2473 - "@cloudflare/workerd-linux-arm64": "1.20250428.0", 2474 - "@cloudflare/workerd-windows-64": "1.20250428.0" 2475 - } 2476 - }, 2477 - "node_modules/wrangler": { 2478 - "version": "4.14.1", 2479 - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.14.1.tgz", 2480 - "integrity": "sha512-EU7IThP7i68TBftJJSveogvWZ5k/WRijcJh3UclDWiWWhDZTPbL6LOJEFhHKqFzHOaC4Y2Aewt48rfTz0e7oCw==", 2481 - "dev": true, 2482 - "license": "MIT OR Apache-2.0", 2483 - "dependencies": { 2484 - "@cloudflare/kv-asset-handler": "0.4.0", 2485 - "@cloudflare/unenv-preset": "2.3.1", 2486 - "blake3-wasm": "2.1.5", 2487 - "esbuild": "0.25.2", 2488 - "miniflare": "4.20250428.1", 2489 - "path-to-regexp": "6.3.0", 2490 - "unenv": "2.0.0-rc.15", 2491 - "workerd": "1.20250428.0" 2492 - }, 2493 - "bin": { 2494 - "wrangler": "bin/wrangler.js", 2495 - "wrangler2": "bin/wrangler.js" 2496 - }, 2497 - "engines": { 2498 - "node": ">=18.0.0" 2499 - }, 2500 - "optionalDependencies": { 2501 - "fsevents": "~2.3.2", 2502 - "sharp": "^0.33.5" 2503 - }, 2504 - "peerDependencies": { 2505 - "@cloudflare/workers-types": "^4.20250428.0" 2506 - }, 2507 - "peerDependenciesMeta": { 2508 - "@cloudflare/workers-types": { 2509 - "optional": true 2510 - } 2511 - } 2512 - }, 2513 - "node_modules/wrangler/node_modules/@esbuild/aix-ppc64": { 2514 - "version": "0.25.2", 2515 - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", 2516 - "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", 2517 - "cpu": [ 2518 - "ppc64" 2519 - ], 2520 - "dev": true, 2521 - "license": "MIT", 2522 - "optional": true, 2523 - "os": [ 2524 - "aix" 2525 - ], 2526 - "engines": { 2527 - "node": ">=18" 2528 - } 2529 - }, 2530 - "node_modules/wrangler/node_modules/@esbuild/android-arm": { 2531 - "version": "0.25.2", 2532 - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", 2533 - "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", 2534 - "cpu": [ 2535 - "arm" 2536 - ], 2537 - "dev": true, 2538 - "license": "MIT", 2539 - "optional": true, 2540 - "os": [ 2541 - "android" 2542 - ], 2543 - "engines": { 2544 - "node": ">=18" 2545 - } 2546 - }, 2547 - "node_modules/wrangler/node_modules/@esbuild/android-arm64": { 2548 - "version": "0.25.2", 2549 - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", 2550 - "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", 2551 - "cpu": [ 2552 - "arm64" 2553 - ], 2554 - "dev": true, 2555 - "license": "MIT", 2556 - "optional": true, 2557 - "os": [ 2558 - "android" 2559 - ], 2560 - "engines": { 2561 - "node": ">=18" 2562 - } 2563 - }, 2564 - "node_modules/wrangler/node_modules/@esbuild/android-x64": { 2565 - "version": "0.25.2", 2566 - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", 2567 - "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", 2568 - "cpu": [ 2569 - "x64" 2570 - ], 2571 - "dev": true, 2572 - "license": "MIT", 2573 - "optional": true, 2574 - "os": [ 2575 - "android" 2576 - ], 2577 - "engines": { 2578 - "node": ">=18" 2579 - } 2580 - }, 2581 - "node_modules/wrangler/node_modules/@esbuild/darwin-arm64": { 2582 - "version": "0.25.2", 2583 - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", 2584 - "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", 2585 - "cpu": [ 2586 - "arm64" 2587 - ], 2588 - "dev": true, 2589 - "license": "MIT", 2590 - "optional": true, 2591 - "os": [ 2592 - "darwin" 2593 - ], 2594 - "engines": { 2595 - "node": ">=18" 2596 - } 2597 - }, 2598 - "node_modules/wrangler/node_modules/@esbuild/darwin-x64": { 2599 - "version": "0.25.2", 2600 - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", 2601 - "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", 2602 - "cpu": [ 2603 - "x64" 2604 - ], 2605 - "dev": true, 2606 - "license": "MIT", 2607 - "optional": true, 2608 - "os": [ 2609 - "darwin" 2610 - ], 2611 - "engines": { 2612 - "node": ">=18" 2613 - } 2614 - }, 2615 - "node_modules/wrangler/node_modules/@esbuild/freebsd-arm64": { 2616 - "version": "0.25.2", 2617 - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", 2618 - "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", 2619 - "cpu": [ 2620 - "arm64" 2621 - ], 2622 - "dev": true, 2623 - "license": "MIT", 2624 - "optional": true, 2625 - "os": [ 2626 - "freebsd" 2627 - ], 2628 - "engines": { 2629 - "node": ">=18" 2630 - } 2631 - }, 2632 - "node_modules/wrangler/node_modules/@esbuild/freebsd-x64": { 2633 - "version": "0.25.2", 2634 - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", 2635 - "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", 2636 - "cpu": [ 2637 - "x64" 2638 - ], 2639 - "dev": true, 2640 - "license": "MIT", 2641 - "optional": true, 2642 - "os": [ 2643 - "freebsd" 2644 - ], 2645 - "engines": { 2646 - "node": ">=18" 2647 - } 2648 - }, 2649 - "node_modules/wrangler/node_modules/@esbuild/linux-arm": { 2650 - "version": "0.25.2", 2651 - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", 2652 - "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", 2653 - "cpu": [ 2654 - "arm" 2655 - ], 2656 - "dev": true, 2657 - "license": "MIT", 2658 - "optional": true, 2659 - "os": [ 2660 - "linux" 2661 - ], 2662 - "engines": { 2663 - "node": ">=18" 2664 - } 2665 - }, 2666 - "node_modules/wrangler/node_modules/@esbuild/linux-arm64": { 2667 - "version": "0.25.2", 2668 - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", 2669 - "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", 2670 - "cpu": [ 2671 - "arm64" 2672 - ], 2673 - "dev": true, 2674 - "license": "MIT", 2675 - "optional": true, 2676 - "os": [ 2677 - "linux" 2678 - ], 2679 - "engines": { 2680 - "node": ">=18" 2681 - } 2682 - }, 2683 - "node_modules/wrangler/node_modules/@esbuild/linux-ia32": { 2684 - "version": "0.25.2", 2685 - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", 2686 - "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", 2687 - "cpu": [ 2688 - "ia32" 2689 - ], 2690 - "dev": true, 2691 - "license": "MIT", 2692 - "optional": true, 2693 - "os": [ 2694 - "linux" 2695 - ], 2696 - "engines": { 2697 - "node": ">=18" 2698 - } 2699 - }, 2700 - "node_modules/wrangler/node_modules/@esbuild/linux-loong64": { 2701 - "version": "0.25.2", 2702 - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", 2703 - "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", 2704 - "cpu": [ 2705 - "loong64" 2706 - ], 2707 - "dev": true, 2708 - "license": "MIT", 2709 - "optional": true, 2710 - "os": [ 2711 - "linux" 2712 - ], 2713 - "engines": { 2714 - "node": ">=18" 2715 - } 2716 - }, 2717 - "node_modules/wrangler/node_modules/@esbuild/linux-mips64el": { 2718 - "version": "0.25.2", 2719 - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", 2720 - "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", 2721 - "cpu": [ 2722 - "mips64el" 2723 - ], 2724 - "dev": true, 2725 - "license": "MIT", 2726 - "optional": true, 2727 - "os": [ 2728 - "linux" 2729 - ], 2730 - "engines": { 2731 - "node": ">=18" 2732 - } 2733 - }, 2734 - "node_modules/wrangler/node_modules/@esbuild/linux-ppc64": { 2735 - "version": "0.25.2", 2736 - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", 2737 - "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", 2738 - "cpu": [ 2739 - "ppc64" 2740 - ], 2741 - "dev": true, 2742 - "license": "MIT", 2743 - "optional": true, 2744 - "os": [ 2745 - "linux" 2746 - ], 2747 - "engines": { 2748 - "node": ">=18" 2749 - } 2750 - }, 2751 - "node_modules/wrangler/node_modules/@esbuild/linux-riscv64": { 2752 - "version": "0.25.2", 2753 - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", 2754 - "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", 2755 - "cpu": [ 2756 - "riscv64" 2757 - ], 2758 - "dev": true, 2759 - "license": "MIT", 2760 - "optional": true, 2761 - "os": [ 2762 - "linux" 2763 - ], 2764 - "engines": { 2765 - "node": ">=18" 2766 - } 2767 - }, 2768 - "node_modules/wrangler/node_modules/@esbuild/linux-s390x": { 2769 - "version": "0.25.2", 2770 - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", 2771 - "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", 2772 - "cpu": [ 2773 - "s390x" 2774 - ], 2775 - "dev": true, 2776 - "license": "MIT", 2777 - "optional": true, 2778 - "os": [ 2779 - "linux" 2780 - ], 2781 - "engines": { 2782 - "node": ">=18" 2783 - } 2784 - }, 2785 - "node_modules/wrangler/node_modules/@esbuild/linux-x64": { 2786 - "version": "0.25.2", 2787 - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", 2788 - "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", 2789 - "cpu": [ 2790 - "x64" 2791 - ], 2792 - "dev": true, 2793 - "license": "MIT", 2794 - "optional": true, 2795 - "os": [ 2796 - "linux" 2797 - ], 2798 - "engines": { 2799 - "node": ">=18" 2800 - } 2801 - }, 2802 - "node_modules/wrangler/node_modules/@esbuild/netbsd-arm64": { 2803 - "version": "0.25.2", 2804 - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", 2805 - "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", 2806 - "cpu": [ 2807 - "arm64" 2808 - ], 2809 - "dev": true, 2810 - "license": "MIT", 2811 - "optional": true, 2812 - "os": [ 2813 - "netbsd" 2814 - ], 2815 - "engines": { 2816 - "node": ">=18" 2817 - } 2818 - }, 2819 - "node_modules/wrangler/node_modules/@esbuild/netbsd-x64": { 2820 - "version": "0.25.2", 2821 - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", 2822 - "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", 2823 - "cpu": [ 2824 - "x64" 2825 - ], 2826 - "dev": true, 2827 - "license": "MIT", 2828 - "optional": true, 2829 - "os": [ 2830 - "netbsd" 2831 - ], 2832 - "engines": { 2833 - "node": ">=18" 2834 - } 2835 - }, 2836 - "node_modules/wrangler/node_modules/@esbuild/openbsd-arm64": { 2837 - "version": "0.25.2", 2838 - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", 2839 - "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", 2840 - "cpu": [ 2841 - "arm64" 2842 - ], 2843 - "dev": true, 2844 - "license": "MIT", 2845 - "optional": true, 2846 - "os": [ 2847 - "openbsd" 2848 - ], 2849 - "engines": { 2850 - "node": ">=18" 2851 - } 2852 - }, 2853 - "node_modules/wrangler/node_modules/@esbuild/openbsd-x64": { 2854 - "version": "0.25.2", 2855 - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", 2856 - "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", 2857 - "cpu": [ 2858 - "x64" 2859 - ], 2860 - "dev": true, 2861 - "license": "MIT", 2862 - "optional": true, 2863 - "os": [ 2864 - "openbsd" 2865 - ], 2866 - "engines": { 2867 - "node": ">=18" 2868 - } 2869 - }, 2870 - "node_modules/wrangler/node_modules/@esbuild/sunos-x64": { 2871 - "version": "0.25.2", 2872 - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", 2873 - "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", 2874 - "cpu": [ 2875 - "x64" 2876 - ], 2877 - "dev": true, 2878 - "license": "MIT", 2879 - "optional": true, 2880 - "os": [ 2881 - "sunos" 2882 - ], 2883 - "engines": { 2884 - "node": ">=18" 2885 - } 2886 - }, 2887 - "node_modules/wrangler/node_modules/@esbuild/win32-arm64": { 2888 - "version": "0.25.2", 2889 - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", 2890 - "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", 2891 - "cpu": [ 2892 - "arm64" 2893 - ], 2894 - "dev": true, 2895 - "license": "MIT", 2896 - "optional": true, 2897 - "os": [ 2898 - "win32" 2899 - ], 2900 - "engines": { 2901 - "node": ">=18" 2902 - } 2903 - }, 2904 - "node_modules/wrangler/node_modules/@esbuild/win32-ia32": { 2905 - "version": "0.25.2", 2906 - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", 2907 - "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", 2908 - "cpu": [ 2909 - "ia32" 2910 - ], 2911 - "dev": true, 2912 - "license": "MIT", 2913 - "optional": true, 2914 - "os": [ 2915 - "win32" 2916 - ], 2917 - "engines": { 2918 - "node": ">=18" 2919 - } 2920 - }, 2921 - "node_modules/wrangler/node_modules/@esbuild/win32-x64": { 2922 - "version": "0.25.2", 2923 - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", 2924 - "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", 2925 - "cpu": [ 2926 - "x64" 2927 - ], 2928 - "dev": true, 2929 - "license": "MIT", 2930 - "optional": true, 2931 - "os": [ 2932 - "win32" 2933 - ], 2934 - "engines": { 2935 - "node": ">=18" 2936 - } 2937 - }, 2938 - "node_modules/wrangler/node_modules/esbuild": { 2939 - "version": "0.25.2", 2940 - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", 2941 - "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", 2942 - "dev": true, 2943 - "hasInstallScript": true, 2944 - "license": "MIT", 2945 - "bin": { 2946 - "esbuild": "bin/esbuild" 2947 - }, 2948 - "engines": { 2949 - "node": ">=18" 2950 - }, 2951 - "optionalDependencies": { 2952 - "@esbuild/aix-ppc64": "0.25.2", 2953 - "@esbuild/android-arm": "0.25.2", 2954 - "@esbuild/android-arm64": "0.25.2", 2955 - "@esbuild/android-x64": "0.25.2", 2956 - "@esbuild/darwin-arm64": "0.25.2", 2957 - "@esbuild/darwin-x64": "0.25.2", 2958 - "@esbuild/freebsd-arm64": "0.25.2", 2959 - "@esbuild/freebsd-x64": "0.25.2", 2960 - "@esbuild/linux-arm": "0.25.2", 2961 - "@esbuild/linux-arm64": "0.25.2", 2962 - "@esbuild/linux-ia32": "0.25.2", 2963 - "@esbuild/linux-loong64": "0.25.2", 2964 - "@esbuild/linux-mips64el": "0.25.2", 2965 - "@esbuild/linux-ppc64": "0.25.2", 2966 - "@esbuild/linux-riscv64": "0.25.2", 2967 - "@esbuild/linux-s390x": "0.25.2", 2968 - "@esbuild/linux-x64": "0.25.2", 2969 - "@esbuild/netbsd-arm64": "0.25.2", 2970 - "@esbuild/netbsd-x64": "0.25.2", 2971 - "@esbuild/openbsd-arm64": "0.25.2", 2972 - "@esbuild/openbsd-x64": "0.25.2", 2973 - "@esbuild/sunos-x64": "0.25.2", 2974 - "@esbuild/win32-arm64": "0.25.2", 2975 - "@esbuild/win32-ia32": "0.25.2", 2976 - "@esbuild/win32-x64": "0.25.2" 2977 - } 2978 - }, 2979 - "node_modules/ws": { 2980 - "version": "8.18.0", 2981 - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", 2982 - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", 2983 - "dev": true, 2984 - "license": "MIT", 2985 - "engines": { 2986 - "node": ">=10.0.0" 2987 - }, 2988 - "peerDependencies": { 2989 - "bufferutil": "^4.0.1", 2990 - "utf-8-validate": ">=5.0.2" 2991 - }, 2992 - "peerDependenciesMeta": { 2993 - "bufferutil": { 2994 - "optional": true 2995 - }, 2996 - "utf-8-validate": { 2997 - "optional": true 2998 - } 2999 - } 3000 - }, 3001 - "node_modules/youch": { 3002 - "version": "3.3.4", 3003 - "resolved": "https://registry.npmjs.org/youch/-/youch-3.3.4.tgz", 3004 - "integrity": "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==", 3005 - "dev": true, 3006 - "license": "MIT", 3007 - "dependencies": { 3008 - "cookie": "^0.7.1", 3009 - "mustache": "^4.2.0", 3010 - "stacktracey": "^2.1.8" 3011 - } 3012 - }, 3013 - "node_modules/zod": { 3014 - "version": "3.24.3", 3015 - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", 3016 - "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", 3017 - "dev": true, 3018 - "license": "MIT", 3019 - "funding": { 3020 - "url": "https://github.com/sponsors/colinhacks" 3021 - } 3022 - } 3023 - } 3024 }
··· 1 { 2 + "name": "avatar", 3 + "version": "0.0.0", 4 + "lockfileVersion": 3, 5 + "requires": true, 6 + "packages": { 7 + "": { 8 + "name": "avatar", 9 + "version": "0.0.0", 10 + "dependencies": { 11 + "@atproto/identity": "^0.4.1" 12 + }, 13 + "devDependencies": { 14 + "@cloudflare/vitest-pool-workers": "^0.8.19", 15 + "vitest": "~3.0.7", 16 + "wrangler": "^4.14.1" 17 + } 18 + }, 19 + "node_modules/@atproto/common-web": { 20 + "version": "0.4.7", 21 + "resolved": "https://registry.npmjs.org/@atproto/common-web/-/common-web-0.4.7.tgz", 22 + "integrity": "sha512-vjw2+81KPo2/SAbbARGn64Ln+6JTI0FTI4xk8if0ebBfDxFRmHb2oSN1y77hzNq/ybGHqA2mecfhS03pxC5+lg==", 23 + "license": "MIT", 24 + "dependencies": { 25 + "@atproto/lex-data": "0.0.3", 26 + "@atproto/lex-json": "0.0.3", 27 + "zod": "^3.23.8" 28 + } 29 + }, 30 + "node_modules/@atproto/crypto": { 31 + "version": "0.4.5", 32 + "resolved": "https://registry.npmjs.org/@atproto/crypto/-/crypto-0.4.5.tgz", 33 + "integrity": "sha512-n40aKkMoCatP0u9Yvhrdk6fXyOHFDDbkdm4h4HCyWW+KlKl8iXfD5iV+ECq+w5BM+QH25aIpt3/j6EUNerhLxw==", 34 + "license": "MIT", 35 + "dependencies": { 36 + "@noble/curves": "^1.7.0", 37 + "@noble/hashes": "^1.6.1", 38 + "uint8arrays": "3.0.0" 39 + }, 40 + "engines": { 41 + "node": ">=18.7.0" 42 + } 43 + }, 44 + "node_modules/@atproto/identity": { 45 + "version": "0.4.10", 46 + "resolved": "https://registry.npmjs.org/@atproto/identity/-/identity-0.4.10.tgz", 47 + "integrity": "sha512-nQbzDLXOhM8p/wo0cTh5DfMSOSHzj6jizpodX37LJ4S1TZzumSxAjHEZa5Rev3JaoD5uSWMVE0MmKEGWkPPvfQ==", 48 + "license": "MIT", 49 + "dependencies": { 50 + "@atproto/common-web": "^0.4.4", 51 + "@atproto/crypto": "^0.4.4" 52 + }, 53 + "engines": { 54 + "node": ">=18.7.0" 55 + } 56 + }, 57 + "node_modules/@atproto/lex-data": { 58 + "version": "0.0.3", 59 + "resolved": "https://registry.npmjs.org/@atproto/lex-data/-/lex-data-0.0.3.tgz", 60 + "integrity": "sha512-ivo1IpY/EX+RIpxPgCf4cPhQo5bfu4nrpa1vJCt8hCm9SfoonJkDFGa0n4SMw4JnXZoUcGcrJ46L+D8bH6GI2g==", 61 + "license": "MIT", 62 + "dependencies": { 63 + "@atproto/syntax": "0.4.2", 64 + "multiformats": "^9.9.0", 65 + "tslib": "^2.8.1", 66 + "uint8arrays": "3.0.0", 67 + "unicode-segmenter": "^0.14.0" 68 + } 69 + }, 70 + "node_modules/@atproto/lex-json": { 71 + "version": "0.0.3", 72 + "resolved": "https://registry.npmjs.org/@atproto/lex-json/-/lex-json-0.0.3.tgz", 73 + "integrity": "sha512-ZVcY7XlRfdPYvQQ2WroKUepee0+NCovrSXgXURM3Xv+n5jflJCoczguROeRr8sN0xvT0ZbzMrDNHCUYKNnxcjw==", 74 + "license": "MIT", 75 + "dependencies": { 76 + "@atproto/lex-data": "0.0.3", 77 + "tslib": "^2.8.1" 78 + } 79 + }, 80 + "node_modules/@atproto/syntax": { 81 + "version": "0.4.2", 82 + "resolved": "https://registry.npmjs.org/@atproto/syntax/-/syntax-0.4.2.tgz", 83 + "integrity": "sha512-X9XSRPinBy/0VQ677j8VXlBsYSsUXaiqxWVpGGxJYsAhugdQRb0jqaVKJFtm6RskeNkV6y9xclSUi9UYG/COrA==", 84 + "license": "MIT" 85 + }, 86 + "node_modules/@cloudflare/kv-asset-handler": { 87 + "version": "0.4.0", 88 + "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.0.tgz", 89 + "integrity": "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA==", 90 + "dev": true, 91 + "license": "MIT OR Apache-2.0", 92 + "dependencies": { 93 + "mime": "^3.0.0" 94 + }, 95 + "engines": { 96 + "node": ">=18.0.0" 97 + } 98 + }, 99 + "node_modules/@cloudflare/unenv-preset": { 100 + "version": "2.3.1", 101 + "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.3.1.tgz", 102 + "integrity": "sha512-Xq57Qd+ADpt6hibcVBO0uLG9zzRgyRhfCUgBT9s+g3+3Ivg5zDyVgLFy40ES1VdNcu8rPNSivm9A+kGP5IVaPg==", 103 + "dev": true, 104 + "license": "MIT OR Apache-2.0", 105 + "peerDependencies": { 106 + "unenv": "2.0.0-rc.15", 107 + "workerd": "^1.20250320.0" 108 + }, 109 + "peerDependenciesMeta": { 110 + "workerd": { 111 + "optional": true 112 + } 113 + } 114 + }, 115 + "node_modules/@cloudflare/vitest-pool-workers": { 116 + "version": "0.8.24", 117 + "resolved": "https://registry.npmjs.org/@cloudflare/vitest-pool-workers/-/vitest-pool-workers-0.8.24.tgz", 118 + "integrity": "sha512-wT2PABJQ9YLYWrVu4CRZOjvmjHkdbMyLTZPU9n/7JEMM3pgG8dY41F1Rj31UsXRQaXX39A/CTPGlk58dcMUysA==", 119 + "dev": true, 120 + "license": "MIT", 121 + "dependencies": { 122 + "birpc": "0.2.14", 123 + "cjs-module-lexer": "^1.2.3", 124 + "devalue": "^4.3.0", 125 + "miniflare": "4.20250428.1", 126 + "semver": "^7.7.1", 127 + "wrangler": "4.14.1", 128 + "zod": "^3.22.3" 129 + }, 130 + "peerDependencies": { 131 + "@vitest/runner": "2.0.x - 3.1.x", 132 + "@vitest/snapshot": "2.0.x - 3.1.x", 133 + "vitest": "2.0.x - 3.1.x" 134 + } 135 + }, 136 + "node_modules/@cloudflare/workerd-darwin-64": { 137 + "version": "1.20250428.0", 138 + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250428.0.tgz", 139 + "integrity": "sha512-6nVe9oV4Hdec6ctzMtW80TiDvNTd2oFPi3VsKqSDVaJSJbL+4b6seyJ7G/UEPI+si6JhHBSLV2/9lNXNGLjClA==", 140 + "cpu": [ 141 + "x64" 142 + ], 143 + "dev": true, 144 + "license": "Apache-2.0", 145 + "optional": true, 146 + "os": [ 147 + "darwin" 148 + ], 149 + "engines": { 150 + "node": ">=16" 151 + } 152 + }, 153 + "node_modules/@cloudflare/workerd-darwin-arm64": { 154 + "version": "1.20250428.0", 155 + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250428.0.tgz", 156 + "integrity": "sha512-/TB7bh7SIJ5f+6r4PHsAz7+9Qal/TK1cJuKFkUno1kqGlZbdrMwH0ATYwlWC/nBFeu2FB3NUolsTntEuy23hnQ==", 157 + "cpu": [ 158 + "arm64" 159 + ], 160 + "dev": true, 161 + "license": "Apache-2.0", 162 + "optional": true, 163 + "os": [ 164 + "darwin" 165 + ], 166 + "engines": { 167 + "node": ">=16" 168 + } 169 + }, 170 + "node_modules/@cloudflare/workerd-linux-64": { 171 + "version": "1.20250428.0", 172 + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250428.0.tgz", 173 + "integrity": "sha512-9eCbj+R3CKqpiXP6DfAA20DxKge+OTj7Hyw3ZewiEhWH9INIHiJwJQYybu4iq9kJEGjnGvxgguLFjSCWm26hgg==", 174 + "cpu": [ 175 + "x64" 176 + ], 177 + "dev": true, 178 + "license": "Apache-2.0", 179 + "optional": true, 180 + "os": [ 181 + "linux" 182 + ], 183 + "engines": { 184 + "node": ">=16" 185 + } 186 + }, 187 + "node_modules/@cloudflare/workerd-linux-arm64": { 188 + "version": "1.20250428.0", 189 + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250428.0.tgz", 190 + "integrity": "sha512-D9NRBnW46nl1EQsP13qfkYb5lbt4C6nxl38SBKY/NOcZAUoHzNB5K0GaK8LxvpkM7X/97ySojlMfR5jh5DNXYQ==", 191 + "cpu": [ 192 + "arm64" 193 + ], 194 + "dev": true, 195 + "license": "Apache-2.0", 196 + "optional": true, 197 + "os": [ 198 + "linux" 199 + ], 200 + "engines": { 201 + "node": ">=16" 202 + } 203 + }, 204 + "node_modules/@cloudflare/workerd-windows-64": { 205 + "version": "1.20250428.0", 206 + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250428.0.tgz", 207 + "integrity": "sha512-RQCRj28eitjKD0tmei6iFOuWqMuHMHdNGEigRmbkmuTlpbWHNAoHikgCzZQ/dkKDdatA76TmcpbyECNf31oaTA==", 208 + "cpu": [ 209 + "x64" 210 + ], 211 + "dev": true, 212 + "license": "Apache-2.0", 213 + "optional": true, 214 + "os": [ 215 + "win32" 216 + ], 217 + "engines": { 218 + "node": ">=16" 219 + } 220 + }, 221 + "node_modules/@cspotcode/source-map-support": { 222 + "version": "0.8.1", 223 + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", 224 + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", 225 + "dev": true, 226 + "license": "MIT", 227 + "dependencies": { 228 + "@jridgewell/trace-mapping": "0.3.9" 229 + }, 230 + "engines": { 231 + "node": ">=12" 232 + } 233 + }, 234 + "node_modules/@emnapi/runtime": { 235 + "version": "1.4.3", 236 + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", 237 + "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", 238 + "dev": true, 239 + "license": "MIT", 240 + "optional": true, 241 + "dependencies": { 242 + "tslib": "^2.4.0" 243 + } 244 + }, 245 + "node_modules/@esbuild/aix-ppc64": { 246 + "version": "0.25.3", 247 + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.3.tgz", 248 + "integrity": "sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==", 249 + "cpu": [ 250 + "ppc64" 251 + ], 252 + "dev": true, 253 + "license": "MIT", 254 + "optional": true, 255 + "os": [ 256 + "aix" 257 + ], 258 + "engines": { 259 + "node": ">=18" 260 + } 261 + }, 262 + "node_modules/@esbuild/android-arm": { 263 + "version": "0.25.3", 264 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.3.tgz", 265 + "integrity": "sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==", 266 + "cpu": [ 267 + "arm" 268 + ], 269 + "dev": true, 270 + "license": "MIT", 271 + "optional": true, 272 + "os": [ 273 + "android" 274 + ], 275 + "engines": { 276 + "node": ">=18" 277 + } 278 + }, 279 + "node_modules/@esbuild/android-arm64": { 280 + "version": "0.25.3", 281 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.3.tgz", 282 + "integrity": "sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==", 283 + "cpu": [ 284 + "arm64" 285 + ], 286 + "dev": true, 287 + "license": "MIT", 288 + "optional": true, 289 + "os": [ 290 + "android" 291 + ], 292 + "engines": { 293 + "node": ">=18" 294 + } 295 + }, 296 + "node_modules/@esbuild/android-x64": { 297 + "version": "0.25.3", 298 + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.3.tgz", 299 + "integrity": "sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==", 300 + "cpu": [ 301 + "x64" 302 + ], 303 + "dev": true, 304 + "license": "MIT", 305 + "optional": true, 306 + "os": [ 307 + "android" 308 + ], 309 + "engines": { 310 + "node": ">=18" 311 + } 312 + }, 313 + "node_modules/@esbuild/darwin-arm64": { 314 + "version": "0.25.3", 315 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.3.tgz", 316 + "integrity": "sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==", 317 + "cpu": [ 318 + "arm64" 319 + ], 320 + "dev": true, 321 + "license": "MIT", 322 + "optional": true, 323 + "os": [ 324 + "darwin" 325 + ], 326 + "engines": { 327 + "node": ">=18" 328 + } 329 + }, 330 + "node_modules/@esbuild/darwin-x64": { 331 + "version": "0.25.3", 332 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.3.tgz", 333 + "integrity": "sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==", 334 + "cpu": [ 335 + "x64" 336 + ], 337 + "dev": true, 338 + "license": "MIT", 339 + "optional": true, 340 + "os": [ 341 + "darwin" 342 + ], 343 + "engines": { 344 + "node": ">=18" 345 + } 346 + }, 347 + "node_modules/@esbuild/freebsd-arm64": { 348 + "version": "0.25.3", 349 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.3.tgz", 350 + "integrity": "sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==", 351 + "cpu": [ 352 + "arm64" 353 + ], 354 + "dev": true, 355 + "license": "MIT", 356 + "optional": true, 357 + "os": [ 358 + "freebsd" 359 + ], 360 + "engines": { 361 + "node": ">=18" 362 + } 363 + }, 364 + "node_modules/@esbuild/freebsd-x64": { 365 + "version": "0.25.3", 366 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.3.tgz", 367 + "integrity": "sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==", 368 + "cpu": [ 369 + "x64" 370 + ], 371 + "dev": true, 372 + "license": "MIT", 373 + "optional": true, 374 + "os": [ 375 + "freebsd" 376 + ], 377 + "engines": { 378 + "node": ">=18" 379 + } 380 + }, 381 + "node_modules/@esbuild/linux-arm": { 382 + "version": "0.25.3", 383 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.3.tgz", 384 + "integrity": "sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==", 385 + "cpu": [ 386 + "arm" 387 + ], 388 + "dev": true, 389 + "license": "MIT", 390 + "optional": true, 391 + "os": [ 392 + "linux" 393 + ], 394 + "engines": { 395 + "node": ">=18" 396 + } 397 + }, 398 + "node_modules/@esbuild/linux-arm64": { 399 + "version": "0.25.3", 400 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.3.tgz", 401 + "integrity": "sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==", 402 + "cpu": [ 403 + "arm64" 404 + ], 405 + "dev": true, 406 + "license": "MIT", 407 + "optional": true, 408 + "os": [ 409 + "linux" 410 + ], 411 + "engines": { 412 + "node": ">=18" 413 + } 414 + }, 415 + "node_modules/@esbuild/linux-ia32": { 416 + "version": "0.25.3", 417 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.3.tgz", 418 + "integrity": "sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==", 419 + "cpu": [ 420 + "ia32" 421 + ], 422 + "dev": true, 423 + "license": "MIT", 424 + "optional": true, 425 + "os": [ 426 + "linux" 427 + ], 428 + "engines": { 429 + "node": ">=18" 430 + } 431 + }, 432 + "node_modules/@esbuild/linux-loong64": { 433 + "version": "0.25.3", 434 + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.3.tgz", 435 + "integrity": "sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==", 436 + "cpu": [ 437 + "loong64" 438 + ], 439 + "dev": true, 440 + "license": "MIT", 441 + "optional": true, 442 + "os": [ 443 + "linux" 444 + ], 445 + "engines": { 446 + "node": ">=18" 447 + } 448 + }, 449 + "node_modules/@esbuild/linux-mips64el": { 450 + "version": "0.25.3", 451 + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.3.tgz", 452 + "integrity": "sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==", 453 + "cpu": [ 454 + "mips64el" 455 + ], 456 + "dev": true, 457 + "license": "MIT", 458 + "optional": true, 459 + "os": [ 460 + "linux" 461 + ], 462 + "engines": { 463 + "node": ">=18" 464 + } 465 + }, 466 + "node_modules/@esbuild/linux-ppc64": { 467 + "version": "0.25.3", 468 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.3.tgz", 469 + "integrity": "sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==", 470 + "cpu": [ 471 + "ppc64" 472 + ], 473 + "dev": true, 474 + "license": "MIT", 475 + "optional": true, 476 + "os": [ 477 + "linux" 478 + ], 479 + "engines": { 480 + "node": ">=18" 481 + } 482 + }, 483 + "node_modules/@esbuild/linux-riscv64": { 484 + "version": "0.25.3", 485 + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.3.tgz", 486 + "integrity": "sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==", 487 + "cpu": [ 488 + "riscv64" 489 + ], 490 + "dev": true, 491 + "license": "MIT", 492 + "optional": true, 493 + "os": [ 494 + "linux" 495 + ], 496 + "engines": { 497 + "node": ">=18" 498 + } 499 + }, 500 + "node_modules/@esbuild/linux-s390x": { 501 + "version": "0.25.3", 502 + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.3.tgz", 503 + "integrity": "sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==", 504 + "cpu": [ 505 + "s390x" 506 + ], 507 + "dev": true, 508 + "license": "MIT", 509 + "optional": true, 510 + "os": [ 511 + "linux" 512 + ], 513 + "engines": { 514 + "node": ">=18" 515 + } 516 + }, 517 + "node_modules/@esbuild/linux-x64": { 518 + "version": "0.25.3", 519 + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.3.tgz", 520 + "integrity": "sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==", 521 + "cpu": [ 522 + "x64" 523 + ], 524 + "dev": true, 525 + "license": "MIT", 526 + "optional": true, 527 + "os": [ 528 + "linux" 529 + ], 530 + "engines": { 531 + "node": ">=18" 532 + } 533 + }, 534 + "node_modules/@esbuild/netbsd-arm64": { 535 + "version": "0.25.3", 536 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.3.tgz", 537 + "integrity": "sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==", 538 + "cpu": [ 539 + "arm64" 540 + ], 541 + "dev": true, 542 + "license": "MIT", 543 + "optional": true, 544 + "os": [ 545 + "netbsd" 546 + ], 547 + "engines": { 548 + "node": ">=18" 549 + } 550 + }, 551 + "node_modules/@esbuild/netbsd-x64": { 552 + "version": "0.25.3", 553 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.3.tgz", 554 + "integrity": "sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==", 555 + "cpu": [ 556 + "x64" 557 + ], 558 + "dev": true, 559 + "license": "MIT", 560 + "optional": true, 561 + "os": [ 562 + "netbsd" 563 + ], 564 + "engines": { 565 + "node": ">=18" 566 + } 567 + }, 568 + "node_modules/@esbuild/openbsd-arm64": { 569 + "version": "0.25.3", 570 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.3.tgz", 571 + "integrity": "sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==", 572 + "cpu": [ 573 + "arm64" 574 + ], 575 + "dev": true, 576 + "license": "MIT", 577 + "optional": true, 578 + "os": [ 579 + "openbsd" 580 + ], 581 + "engines": { 582 + "node": ">=18" 583 + } 584 + }, 585 + "node_modules/@esbuild/openbsd-x64": { 586 + "version": "0.25.3", 587 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.3.tgz", 588 + "integrity": "sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==", 589 + "cpu": [ 590 + "x64" 591 + ], 592 + "dev": true, 593 + "license": "MIT", 594 + "optional": true, 595 + "os": [ 596 + "openbsd" 597 + ], 598 + "engines": { 599 + "node": ">=18" 600 + } 601 + }, 602 + "node_modules/@esbuild/sunos-x64": { 603 + "version": "0.25.3", 604 + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.3.tgz", 605 + "integrity": "sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==", 606 + "cpu": [ 607 + "x64" 608 + ], 609 + "dev": true, 610 + "license": "MIT", 611 + "optional": true, 612 + "os": [ 613 + "sunos" 614 + ], 615 + "engines": { 616 + "node": ">=18" 617 + } 618 + }, 619 + "node_modules/@esbuild/win32-arm64": { 620 + "version": "0.25.3", 621 + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.3.tgz", 622 + "integrity": "sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==", 623 + "cpu": [ 624 + "arm64" 625 + ], 626 + "dev": true, 627 + "license": "MIT", 628 + "optional": true, 629 + "os": [ 630 + "win32" 631 + ], 632 + "engines": { 633 + "node": ">=18" 634 + } 635 + }, 636 + "node_modules/@esbuild/win32-ia32": { 637 + "version": "0.25.3", 638 + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.3.tgz", 639 + "integrity": "sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==", 640 + "cpu": [ 641 + "ia32" 642 + ], 643 + "dev": true, 644 + "license": "MIT", 645 + "optional": true, 646 + "os": [ 647 + "win32" 648 + ], 649 + "engines": { 650 + "node": ">=18" 651 + } 652 + }, 653 + "node_modules/@esbuild/win32-x64": { 654 + "version": "0.25.3", 655 + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.3.tgz", 656 + "integrity": "sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==", 657 + "cpu": [ 658 + "x64" 659 + ], 660 + "dev": true, 661 + "license": "MIT", 662 + "optional": true, 663 + "os": [ 664 + "win32" 665 + ], 666 + "engines": { 667 + "node": ">=18" 668 + } 669 + }, 670 + "node_modules/@fastify/busboy": { 671 + "version": "2.1.1", 672 + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", 673 + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", 674 + "dev": true, 675 + "license": "MIT", 676 + "engines": { 677 + "node": ">=14" 678 + } 679 + }, 680 + "node_modules/@img/sharp-darwin-arm64": { 681 + "version": "0.33.5", 682 + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", 683 + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", 684 + "cpu": [ 685 + "arm64" 686 + ], 687 + "dev": true, 688 + "license": "Apache-2.0", 689 + "optional": true, 690 + "os": [ 691 + "darwin" 692 + ], 693 + "engines": { 694 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 695 + }, 696 + "funding": { 697 + "url": "https://opencollective.com/libvips" 698 + }, 699 + "optionalDependencies": { 700 + "@img/sharp-libvips-darwin-arm64": "1.0.4" 701 + } 702 + }, 703 + "node_modules/@img/sharp-darwin-x64": { 704 + "version": "0.33.5", 705 + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", 706 + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", 707 + "cpu": [ 708 + "x64" 709 + ], 710 + "dev": true, 711 + "license": "Apache-2.0", 712 + "optional": true, 713 + "os": [ 714 + "darwin" 715 + ], 716 + "engines": { 717 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 718 + }, 719 + "funding": { 720 + "url": "https://opencollective.com/libvips" 721 + }, 722 + "optionalDependencies": { 723 + "@img/sharp-libvips-darwin-x64": "1.0.4" 724 + } 725 + }, 726 + "node_modules/@img/sharp-libvips-darwin-arm64": { 727 + "version": "1.0.4", 728 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", 729 + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", 730 + "cpu": [ 731 + "arm64" 732 + ], 733 + "dev": true, 734 + "license": "LGPL-3.0-or-later", 735 + "optional": true, 736 + "os": [ 737 + "darwin" 738 + ], 739 + "funding": { 740 + "url": "https://opencollective.com/libvips" 741 + } 742 + }, 743 + "node_modules/@img/sharp-libvips-darwin-x64": { 744 + "version": "1.0.4", 745 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", 746 + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", 747 + "cpu": [ 748 + "x64" 749 + ], 750 + "dev": true, 751 + "license": "LGPL-3.0-or-later", 752 + "optional": true, 753 + "os": [ 754 + "darwin" 755 + ], 756 + "funding": { 757 + "url": "https://opencollective.com/libvips" 758 + } 759 + }, 760 + "node_modules/@img/sharp-libvips-linux-arm": { 761 + "version": "1.0.5", 762 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", 763 + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", 764 + "cpu": [ 765 + "arm" 766 + ], 767 + "dev": true, 768 + "license": "LGPL-3.0-or-later", 769 + "optional": true, 770 + "os": [ 771 + "linux" 772 + ], 773 + "funding": { 774 + "url": "https://opencollective.com/libvips" 775 + } 776 + }, 777 + "node_modules/@img/sharp-libvips-linux-arm64": { 778 + "version": "1.0.4", 779 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", 780 + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", 781 + "cpu": [ 782 + "arm64" 783 + ], 784 + "dev": true, 785 + "license": "LGPL-3.0-or-later", 786 + "optional": true, 787 + "os": [ 788 + "linux" 789 + ], 790 + "funding": { 791 + "url": "https://opencollective.com/libvips" 792 + } 793 + }, 794 + "node_modules/@img/sharp-libvips-linux-s390x": { 795 + "version": "1.0.4", 796 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", 797 + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", 798 + "cpu": [ 799 + "s390x" 800 + ], 801 + "dev": true, 802 + "license": "LGPL-3.0-or-later", 803 + "optional": true, 804 + "os": [ 805 + "linux" 806 + ], 807 + "funding": { 808 + "url": "https://opencollective.com/libvips" 809 + } 810 + }, 811 + "node_modules/@img/sharp-libvips-linux-x64": { 812 + "version": "1.0.4", 813 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", 814 + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", 815 + "cpu": [ 816 + "x64" 817 + ], 818 + "dev": true, 819 + "license": "LGPL-3.0-or-later", 820 + "optional": true, 821 + "os": [ 822 + "linux" 823 + ], 824 + "funding": { 825 + "url": "https://opencollective.com/libvips" 826 + } 827 + }, 828 + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { 829 + "version": "1.0.4", 830 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", 831 + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", 832 + "cpu": [ 833 + "arm64" 834 + ], 835 + "dev": true, 836 + "license": "LGPL-3.0-or-later", 837 + "optional": true, 838 + "os": [ 839 + "linux" 840 + ], 841 + "funding": { 842 + "url": "https://opencollective.com/libvips" 843 + } 844 + }, 845 + "node_modules/@img/sharp-libvips-linuxmusl-x64": { 846 + "version": "1.0.4", 847 + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", 848 + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", 849 + "cpu": [ 850 + "x64" 851 + ], 852 + "dev": true, 853 + "license": "LGPL-3.0-or-later", 854 + "optional": true, 855 + "os": [ 856 + "linux" 857 + ], 858 + "funding": { 859 + "url": "https://opencollective.com/libvips" 860 + } 861 + }, 862 + "node_modules/@img/sharp-linux-arm": { 863 + "version": "0.33.5", 864 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", 865 + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", 866 + "cpu": [ 867 + "arm" 868 + ], 869 + "dev": true, 870 + "license": "Apache-2.0", 871 + "optional": true, 872 + "os": [ 873 + "linux" 874 + ], 875 + "engines": { 876 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 877 + }, 878 + "funding": { 879 + "url": "https://opencollective.com/libvips" 880 + }, 881 + "optionalDependencies": { 882 + "@img/sharp-libvips-linux-arm": "1.0.5" 883 + } 884 + }, 885 + "node_modules/@img/sharp-linux-arm64": { 886 + "version": "0.33.5", 887 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", 888 + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", 889 + "cpu": [ 890 + "arm64" 891 + ], 892 + "dev": true, 893 + "license": "Apache-2.0", 894 + "optional": true, 895 + "os": [ 896 + "linux" 897 + ], 898 + "engines": { 899 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 900 + }, 901 + "funding": { 902 + "url": "https://opencollective.com/libvips" 903 + }, 904 + "optionalDependencies": { 905 + "@img/sharp-libvips-linux-arm64": "1.0.4" 906 + } 907 + }, 908 + "node_modules/@img/sharp-linux-s390x": { 909 + "version": "0.33.5", 910 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", 911 + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", 912 + "cpu": [ 913 + "s390x" 914 + ], 915 + "dev": true, 916 + "license": "Apache-2.0", 917 + "optional": true, 918 + "os": [ 919 + "linux" 920 + ], 921 + "engines": { 922 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 923 + }, 924 + "funding": { 925 + "url": "https://opencollective.com/libvips" 926 + }, 927 + "optionalDependencies": { 928 + "@img/sharp-libvips-linux-s390x": "1.0.4" 929 + } 930 + }, 931 + "node_modules/@img/sharp-linux-x64": { 932 + "version": "0.33.5", 933 + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", 934 + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", 935 + "cpu": [ 936 + "x64" 937 + ], 938 + "dev": true, 939 + "license": "Apache-2.0", 940 + "optional": true, 941 + "os": [ 942 + "linux" 943 + ], 944 + "engines": { 945 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 946 + }, 947 + "funding": { 948 + "url": "https://opencollective.com/libvips" 949 + }, 950 + "optionalDependencies": { 951 + "@img/sharp-libvips-linux-x64": "1.0.4" 952 + } 953 + }, 954 + "node_modules/@img/sharp-linuxmusl-arm64": { 955 + "version": "0.33.5", 956 + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", 957 + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", 958 + "cpu": [ 959 + "arm64" 960 + ], 961 + "dev": true, 962 + "license": "Apache-2.0", 963 + "optional": true, 964 + "os": [ 965 + "linux" 966 + ], 967 + "engines": { 968 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 969 + }, 970 + "funding": { 971 + "url": "https://opencollective.com/libvips" 972 + }, 973 + "optionalDependencies": { 974 + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" 975 + } 976 + }, 977 + "node_modules/@img/sharp-linuxmusl-x64": { 978 + "version": "0.33.5", 979 + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", 980 + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", 981 + "cpu": [ 982 + "x64" 983 + ], 984 + "dev": true, 985 + "license": "Apache-2.0", 986 + "optional": true, 987 + "os": [ 988 + "linux" 989 + ], 990 + "engines": { 991 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 992 + }, 993 + "funding": { 994 + "url": "https://opencollective.com/libvips" 995 + }, 996 + "optionalDependencies": { 997 + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" 998 + } 999 + }, 1000 + "node_modules/@img/sharp-wasm32": { 1001 + "version": "0.33.5", 1002 + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", 1003 + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", 1004 + "cpu": [ 1005 + "wasm32" 1006 + ], 1007 + "dev": true, 1008 + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", 1009 + "optional": true, 1010 + "dependencies": { 1011 + "@emnapi/runtime": "^1.2.0" 1012 + }, 1013 + "engines": { 1014 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1015 + }, 1016 + "funding": { 1017 + "url": "https://opencollective.com/libvips" 1018 + } 1019 + }, 1020 + "node_modules/@img/sharp-win32-ia32": { 1021 + "version": "0.33.5", 1022 + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", 1023 + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", 1024 + "cpu": [ 1025 + "ia32" 1026 + ], 1027 + "dev": true, 1028 + "license": "Apache-2.0 AND LGPL-3.0-or-later", 1029 + "optional": true, 1030 + "os": [ 1031 + "win32" 1032 + ], 1033 + "engines": { 1034 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1035 + }, 1036 + "funding": { 1037 + "url": "https://opencollective.com/libvips" 1038 + } 1039 + }, 1040 + "node_modules/@img/sharp-win32-x64": { 1041 + "version": "0.33.5", 1042 + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", 1043 + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", 1044 + "cpu": [ 1045 + "x64" 1046 + ], 1047 + "dev": true, 1048 + "license": "Apache-2.0 AND LGPL-3.0-or-later", 1049 + "optional": true, 1050 + "os": [ 1051 + "win32" 1052 + ], 1053 + "engines": { 1054 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 1055 + }, 1056 + "funding": { 1057 + "url": "https://opencollective.com/libvips" 1058 + } 1059 + }, 1060 + "node_modules/@jridgewell/resolve-uri": { 1061 + "version": "3.1.2", 1062 + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 1063 + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 1064 + "dev": true, 1065 + "license": "MIT", 1066 + "engines": { 1067 + "node": ">=6.0.0" 1068 + } 1069 + }, 1070 + "node_modules/@jridgewell/sourcemap-codec": { 1071 + "version": "1.5.0", 1072 + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", 1073 + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", 1074 + "dev": true, 1075 + "license": "MIT" 1076 + }, 1077 + "node_modules/@jridgewell/trace-mapping": { 1078 + "version": "0.3.9", 1079 + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", 1080 + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", 1081 + "dev": true, 1082 + "license": "MIT", 1083 + "dependencies": { 1084 + "@jridgewell/resolve-uri": "^3.0.3", 1085 + "@jridgewell/sourcemap-codec": "^1.4.10" 1086 + } 1087 + }, 1088 + "node_modules/@noble/curves": { 1089 + "version": "1.9.7", 1090 + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", 1091 + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", 1092 + "license": "MIT", 1093 + "dependencies": { 1094 + "@noble/hashes": "1.8.0" 1095 + }, 1096 + "engines": { 1097 + "node": "^14.21.3 || >=16" 1098 + }, 1099 + "funding": { 1100 + "url": "https://paulmillr.com/funding/" 1101 + } 1102 + }, 1103 + "node_modules/@noble/hashes": { 1104 + "version": "1.8.0", 1105 + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", 1106 + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", 1107 + "license": "MIT", 1108 + "engines": { 1109 + "node": "^14.21.3 || >=16" 1110 + }, 1111 + "funding": { 1112 + "url": "https://paulmillr.com/funding/" 1113 + } 1114 + }, 1115 + "node_modules/@rollup/rollup-android-arm-eabi": { 1116 + "version": "4.40.1", 1117 + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.1.tgz", 1118 + "integrity": "sha512-kxz0YeeCrRUHz3zyqvd7n+TVRlNyTifBsmnmNPtk3hQURUyG9eAB+usz6DAwagMusjx/zb3AjvDUvhFGDAexGw==", 1119 + "cpu": [ 1120 + "arm" 1121 + ], 1122 + "dev": true, 1123 + "license": "MIT", 1124 + "optional": true, 1125 + "os": [ 1126 + "android" 1127 + ] 1128 + }, 1129 + "node_modules/@rollup/rollup-android-arm64": { 1130 + "version": "4.40.1", 1131 + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.1.tgz", 1132 + "integrity": "sha512-PPkxTOisoNC6TpnDKatjKkjRMsdaWIhyuMkA4UsBXT9WEZY4uHezBTjs6Vl4PbqQQeu6oION1w2voYZv9yquCw==", 1133 + "cpu": [ 1134 + "arm64" 1135 + ], 1136 + "dev": true, 1137 + "license": "MIT", 1138 + "optional": true, 1139 + "os": [ 1140 + "android" 1141 + ] 1142 + }, 1143 + "node_modules/@rollup/rollup-darwin-arm64": { 1144 + "version": "4.40.1", 1145 + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.1.tgz", 1146 + "integrity": "sha512-VWXGISWFY18v/0JyNUy4A46KCFCb9NVsH+1100XP31lud+TzlezBbz24CYzbnA4x6w4hx+NYCXDfnvDVO6lcAA==", 1147 + "cpu": [ 1148 + "arm64" 1149 + ], 1150 + "dev": true, 1151 + "license": "MIT", 1152 + "optional": true, 1153 + "os": [ 1154 + "darwin" 1155 + ] 1156 + }, 1157 + "node_modules/@rollup/rollup-darwin-x64": { 1158 + "version": "4.40.1", 1159 + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.1.tgz", 1160 + "integrity": "sha512-nIwkXafAI1/QCS7pxSpv/ZtFW6TXcNUEHAIA9EIyw5OzxJZQ1YDrX+CL6JAIQgZ33CInl1R6mHet9Y/UZTg2Bw==", 1161 + "cpu": [ 1162 + "x64" 1163 + ], 1164 + "dev": true, 1165 + "license": "MIT", 1166 + "optional": true, 1167 + "os": [ 1168 + "darwin" 1169 + ] 1170 + }, 1171 + "node_modules/@rollup/rollup-freebsd-arm64": { 1172 + "version": "4.40.1", 1173 + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.1.tgz", 1174 + "integrity": "sha512-BdrLJ2mHTrIYdaS2I99mriyJfGGenSaP+UwGi1kB9BLOCu9SR8ZpbkmmalKIALnRw24kM7qCN0IOm6L0S44iWw==", 1175 + "cpu": [ 1176 + "arm64" 1177 + ], 1178 + "dev": true, 1179 + "license": "MIT", 1180 + "optional": true, 1181 + "os": [ 1182 + "freebsd" 1183 + ] 1184 + }, 1185 + "node_modules/@rollup/rollup-freebsd-x64": { 1186 + "version": "4.40.1", 1187 + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.1.tgz", 1188 + "integrity": "sha512-VXeo/puqvCG8JBPNZXZf5Dqq7BzElNJzHRRw3vjBE27WujdzuOPecDPc/+1DcdcTptNBep3861jNq0mYkT8Z6Q==", 1189 + "cpu": [ 1190 + "x64" 1191 + ], 1192 + "dev": true, 1193 + "license": "MIT", 1194 + "optional": true, 1195 + "os": [ 1196 + "freebsd" 1197 + ] 1198 + }, 1199 + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { 1200 + "version": "4.40.1", 1201 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.1.tgz", 1202 + "integrity": "sha512-ehSKrewwsESPt1TgSE/na9nIhWCosfGSFqv7vwEtjyAqZcvbGIg4JAcV7ZEh2tfj/IlfBeZjgOXm35iOOjadcg==", 1203 + "cpu": [ 1204 + "arm" 1205 + ], 1206 + "dev": true, 1207 + "license": "MIT", 1208 + "optional": true, 1209 + "os": [ 1210 + "linux" 1211 + ] 1212 + }, 1213 + "node_modules/@rollup/rollup-linux-arm-musleabihf": { 1214 + "version": "4.40.1", 1215 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.1.tgz", 1216 + "integrity": "sha512-m39iO/aaurh5FVIu/F4/Zsl8xppd76S4qoID8E+dSRQvTyZTOI2gVk3T4oqzfq1PtcvOfAVlwLMK3KRQMaR8lg==", 1217 + "cpu": [ 1218 + "arm" 1219 + ], 1220 + "dev": true, 1221 + "license": "MIT", 1222 + "optional": true, 1223 + "os": [ 1224 + "linux" 1225 + ] 1226 + }, 1227 + "node_modules/@rollup/rollup-linux-arm64-gnu": { 1228 + "version": "4.40.1", 1229 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.1.tgz", 1230 + "integrity": "sha512-Y+GHnGaku4aVLSgrT0uWe2o2Rq8te9hi+MwqGF9r9ORgXhmHK5Q71N757u0F8yU1OIwUIFy6YiJtKjtyktk5hg==", 1231 + "cpu": [ 1232 + "arm64" 1233 + ], 1234 + "dev": true, 1235 + "license": "MIT", 1236 + "optional": true, 1237 + "os": [ 1238 + "linux" 1239 + ] 1240 + }, 1241 + "node_modules/@rollup/rollup-linux-arm64-musl": { 1242 + "version": "4.40.1", 1243 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.1.tgz", 1244 + "integrity": "sha512-jEwjn3jCA+tQGswK3aEWcD09/7M5wGwc6+flhva7dsQNRZZTe30vkalgIzV4tjkopsTS9Jd7Y1Bsj6a4lzz8gQ==", 1245 + "cpu": [ 1246 + "arm64" 1247 + ], 1248 + "dev": true, 1249 + "license": "MIT", 1250 + "optional": true, 1251 + "os": [ 1252 + "linux" 1253 + ] 1254 + }, 1255 + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { 1256 + "version": "4.40.1", 1257 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.1.tgz", 1258 + "integrity": "sha512-ySyWikVhNzv+BV/IDCsrraOAZ3UaC8SZB67FZlqVwXwnFhPihOso9rPOxzZbjp81suB1O2Topw+6Ug3JNegejQ==", 1259 + "cpu": [ 1260 + "loong64" 1261 + ], 1262 + "dev": true, 1263 + "license": "MIT", 1264 + "optional": true, 1265 + "os": [ 1266 + "linux" 1267 + ] 1268 + }, 1269 + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { 1270 + "version": "4.40.1", 1271 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.1.tgz", 1272 + "integrity": "sha512-BvvA64QxZlh7WZWqDPPdt0GH4bznuL6uOO1pmgPnnv86rpUpc8ZxgZwcEgXvo02GRIZX1hQ0j0pAnhwkhwPqWg==", 1273 + "cpu": [ 1274 + "ppc64" 1275 + ], 1276 + "dev": true, 1277 + "license": "MIT", 1278 + "optional": true, 1279 + "os": [ 1280 + "linux" 1281 + ] 1282 + }, 1283 + "node_modules/@rollup/rollup-linux-riscv64-gnu": { 1284 + "version": "4.40.1", 1285 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.1.tgz", 1286 + "integrity": "sha512-EQSP+8+1VuSulm9RKSMKitTav89fKbHymTf25n5+Yr6gAPZxYWpj3DzAsQqoaHAk9YX2lwEyAf9S4W8F4l3VBQ==", 1287 + "cpu": [ 1288 + "riscv64" 1289 + ], 1290 + "dev": true, 1291 + "license": "MIT", 1292 + "optional": true, 1293 + "os": [ 1294 + "linux" 1295 + ] 1296 + }, 1297 + "node_modules/@rollup/rollup-linux-riscv64-musl": { 1298 + "version": "4.40.1", 1299 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.1.tgz", 1300 + "integrity": "sha512-n/vQ4xRZXKuIpqukkMXZt9RWdl+2zgGNx7Uda8NtmLJ06NL8jiHxUawbwC+hdSq1rrw/9CghCpEONor+l1e2gA==", 1301 + "cpu": [ 1302 + "riscv64" 1303 + ], 1304 + "dev": true, 1305 + "license": "MIT", 1306 + "optional": true, 1307 + "os": [ 1308 + "linux" 1309 + ] 1310 + }, 1311 + "node_modules/@rollup/rollup-linux-s390x-gnu": { 1312 + "version": "4.40.1", 1313 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.1.tgz", 1314 + "integrity": "sha512-h8d28xzYb98fMQKUz0w2fMc1XuGzLLjdyxVIbhbil4ELfk5/orZlSTpF/xdI9C8K0I8lCkq+1En2RJsawZekkg==", 1315 + "cpu": [ 1316 + "s390x" 1317 + ], 1318 + "dev": true, 1319 + "license": "MIT", 1320 + "optional": true, 1321 + "os": [ 1322 + "linux" 1323 + ] 1324 + }, 1325 + "node_modules/@rollup/rollup-linux-x64-gnu": { 1326 + "version": "4.40.1", 1327 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.1.tgz", 1328 + "integrity": "sha512-XiK5z70PEFEFqcNj3/zRSz/qX4bp4QIraTy9QjwJAb/Z8GM7kVUsD0Uk8maIPeTyPCP03ChdI+VVmJriKYbRHQ==", 1329 + "cpu": [ 1330 + "x64" 1331 + ], 1332 + "dev": true, 1333 + "license": "MIT", 1334 + "optional": true, 1335 + "os": [ 1336 + "linux" 1337 + ] 1338 + }, 1339 + "node_modules/@rollup/rollup-linux-x64-musl": { 1340 + "version": "4.40.1", 1341 + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.1.tgz", 1342 + "integrity": "sha512-2BRORitq5rQ4Da9blVovzNCMaUlyKrzMSvkVR0D4qPuOy/+pMCrh1d7o01RATwVy+6Fa1WBw+da7QPeLWU/1mQ==", 1343 + "cpu": [ 1344 + "x64" 1345 + ], 1346 + "dev": true, 1347 + "license": "MIT", 1348 + "optional": true, 1349 + "os": [ 1350 + "linux" 1351 + ] 1352 + }, 1353 + "node_modules/@rollup/rollup-win32-arm64-msvc": { 1354 + "version": "4.40.1", 1355 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.1.tgz", 1356 + "integrity": "sha512-b2bcNm9Kbde03H+q+Jjw9tSfhYkzrDUf2d5MAd1bOJuVplXvFhWz7tRtWvD8/ORZi7qSCy0idW6tf2HgxSXQSg==", 1357 + "cpu": [ 1358 + "arm64" 1359 + ], 1360 + "dev": true, 1361 + "license": "MIT", 1362 + "optional": true, 1363 + "os": [ 1364 + "win32" 1365 + ] 1366 + }, 1367 + "node_modules/@rollup/rollup-win32-ia32-msvc": { 1368 + "version": "4.40.1", 1369 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.1.tgz", 1370 + "integrity": "sha512-DfcogW8N7Zg7llVEfpqWMZcaErKfsj9VvmfSyRjCyo4BI3wPEfrzTtJkZG6gKP/Z92wFm6rz2aDO7/JfiR/whA==", 1371 + "cpu": [ 1372 + "ia32" 1373 + ], 1374 + "dev": true, 1375 + "license": "MIT", 1376 + "optional": true, 1377 + "os": [ 1378 + "win32" 1379 + ] 1380 + }, 1381 + "node_modules/@rollup/rollup-win32-x64-msvc": { 1382 + "version": "4.40.1", 1383 + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.1.tgz", 1384 + "integrity": "sha512-ECyOuDeH3C1I8jH2MK1RtBJW+YPMvSfT0a5NN0nHfQYnDSJ6tUiZH3gzwVP5/Kfh/+Tt7tpWVF9LXNTnhTJ3kA==", 1385 + "cpu": [ 1386 + "x64" 1387 + ], 1388 + "dev": true, 1389 + "license": "MIT", 1390 + "optional": true, 1391 + "os": [ 1392 + "win32" 1393 + ] 1394 + }, 1395 + "node_modules/@types/estree": { 1396 + "version": "1.0.7", 1397 + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", 1398 + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", 1399 + "dev": true, 1400 + "license": "MIT" 1401 + }, 1402 + "node_modules/@vitest/expect": { 1403 + "version": "3.0.9", 1404 + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.0.9.tgz", 1405 + "integrity": "sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig==", 1406 + "dev": true, 1407 + "license": "MIT", 1408 + "dependencies": { 1409 + "@vitest/spy": "3.0.9", 1410 + "@vitest/utils": "3.0.9", 1411 + "chai": "^5.2.0", 1412 + "tinyrainbow": "^2.0.0" 1413 + }, 1414 + "funding": { 1415 + "url": "https://opencollective.com/vitest" 1416 + } 1417 + }, 1418 + "node_modules/@vitest/mocker": { 1419 + "version": "3.0.9", 1420 + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.0.9.tgz", 1421 + "integrity": "sha512-ryERPIBOnvevAkTq+L1lD+DTFBRcjueL9lOUfXsLfwP92h4e+Heb+PjiqS3/OURWPtywfafK0kj++yDFjWUmrA==", 1422 + "dev": true, 1423 + "license": "MIT", 1424 + "dependencies": { 1425 + "@vitest/spy": "3.0.9", 1426 + "estree-walker": "^3.0.3", 1427 + "magic-string": "^0.30.17" 1428 + }, 1429 + "funding": { 1430 + "url": "https://opencollective.com/vitest" 1431 + }, 1432 + "peerDependencies": { 1433 + "msw": "^2.4.9", 1434 + "vite": "^5.0.0 || ^6.0.0" 1435 + }, 1436 + "peerDependenciesMeta": { 1437 + "msw": { 1438 + "optional": true 1439 + }, 1440 + "vite": { 1441 + "optional": true 1442 + } 1443 + } 1444 + }, 1445 + "node_modules/@vitest/pretty-format": { 1446 + "version": "3.1.2", 1447 + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.2.tgz", 1448 + "integrity": "sha512-R0xAiHuWeDjTSB3kQ3OQpT8Rx3yhdOAIm/JM4axXxnG7Q/fS8XUwggv/A4xzbQA+drYRjzkMnpYnOGAc4oeq8w==", 1449 + "dev": true, 1450 + "license": "MIT", 1451 + "dependencies": { 1452 + "tinyrainbow": "^2.0.0" 1453 + }, 1454 + "funding": { 1455 + "url": "https://opencollective.com/vitest" 1456 + } 1457 + }, 1458 + "node_modules/@vitest/runner": { 1459 + "version": "3.0.9", 1460 + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.0.9.tgz", 1461 + "integrity": "sha512-NX9oUXgF9HPfJSwl8tUZCMP1oGx2+Sf+ru6d05QjzQz4OwWg0psEzwY6VexP2tTHWdOkhKHUIZH+fS6nA7jfOw==", 1462 + "dev": true, 1463 + "license": "MIT", 1464 + "dependencies": { 1465 + "@vitest/utils": "3.0.9", 1466 + "pathe": "^2.0.3" 1467 + }, 1468 + "funding": { 1469 + "url": "https://opencollective.com/vitest" 1470 + } 1471 + }, 1472 + "node_modules/@vitest/snapshot": { 1473 + "version": "3.0.9", 1474 + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.0.9.tgz", 1475 + "integrity": "sha512-AiLUiuZ0FuA+/8i19mTYd+re5jqjEc2jZbgJ2up0VY0Ddyyxg/uUtBDpIFAy4uzKaQxOW8gMgBdAJJ2ydhu39A==", 1476 + "dev": true, 1477 + "license": "MIT", 1478 + "dependencies": { 1479 + "@vitest/pretty-format": "3.0.9", 1480 + "magic-string": "^0.30.17", 1481 + "pathe": "^2.0.3" 1482 + }, 1483 + "funding": { 1484 + "url": "https://opencollective.com/vitest" 1485 + } 1486 + }, 1487 + "node_modules/@vitest/snapshot/node_modules/@vitest/pretty-format": { 1488 + "version": "3.0.9", 1489 + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.9.tgz", 1490 + "integrity": "sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==", 1491 + "dev": true, 1492 + "license": "MIT", 1493 + "dependencies": { 1494 + "tinyrainbow": "^2.0.0" 1495 + }, 1496 + "funding": { 1497 + "url": "https://opencollective.com/vitest" 1498 + } 1499 + }, 1500 + "node_modules/@vitest/spy": { 1501 + "version": "3.0.9", 1502 + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.0.9.tgz", 1503 + "integrity": "sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ==", 1504 + "dev": true, 1505 + "license": "MIT", 1506 + "dependencies": { 1507 + "tinyspy": "^3.0.2" 1508 + }, 1509 + "funding": { 1510 + "url": "https://opencollective.com/vitest" 1511 + } 1512 + }, 1513 + "node_modules/@vitest/utils": { 1514 + "version": "3.0.9", 1515 + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.0.9.tgz", 1516 + "integrity": "sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==", 1517 + "dev": true, 1518 + "license": "MIT", 1519 + "dependencies": { 1520 + "@vitest/pretty-format": "3.0.9", 1521 + "loupe": "^3.1.3", 1522 + "tinyrainbow": "^2.0.0" 1523 + }, 1524 + "funding": { 1525 + "url": "https://opencollective.com/vitest" 1526 + } 1527 + }, 1528 + "node_modules/@vitest/utils/node_modules/@vitest/pretty-format": { 1529 + "version": "3.0.9", 1530 + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.0.9.tgz", 1531 + "integrity": "sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==", 1532 + "dev": true, 1533 + "license": "MIT", 1534 + "dependencies": { 1535 + "tinyrainbow": "^2.0.0" 1536 + }, 1537 + "funding": { 1538 + "url": "https://opencollective.com/vitest" 1539 + } 1540 + }, 1541 + "node_modules/acorn": { 1542 + "version": "8.14.0", 1543 + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", 1544 + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", 1545 + "dev": true, 1546 + "license": "MIT", 1547 + "bin": { 1548 + "acorn": "bin/acorn" 1549 + }, 1550 + "engines": { 1551 + "node": ">=0.4.0" 1552 + } 1553 + }, 1554 + "node_modules/acorn-walk": { 1555 + "version": "8.3.2", 1556 + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", 1557 + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", 1558 + "dev": true, 1559 + "license": "MIT", 1560 + "engines": { 1561 + "node": ">=0.4.0" 1562 + } 1563 + }, 1564 + "node_modules/as-table": { 1565 + "version": "1.0.55", 1566 + "resolved": "https://registry.npmjs.org/as-table/-/as-table-1.0.55.tgz", 1567 + "integrity": "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==", 1568 + "dev": true, 1569 + "license": "MIT", 1570 + "dependencies": { 1571 + "printable-characters": "^1.0.42" 1572 + } 1573 + }, 1574 + "node_modules/assertion-error": { 1575 + "version": "2.0.1", 1576 + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", 1577 + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", 1578 + "dev": true, 1579 + "license": "MIT", 1580 + "engines": { 1581 + "node": ">=12" 1582 + } 1583 + }, 1584 + "node_modules/birpc": { 1585 + "version": "0.2.14", 1586 + "resolved": "https://registry.npmjs.org/birpc/-/birpc-0.2.14.tgz", 1587 + "integrity": "sha512-37FHE8rqsYM5JEKCnXFyHpBCzvgHEExwVVTq+nUmloInU7l8ezD1TpOhKpS8oe1DTYFqEK27rFZVKG43oTqXRA==", 1588 + "dev": true, 1589 + "license": "MIT", 1590 + "funding": { 1591 + "url": "https://github.com/sponsors/antfu" 1592 + } 1593 + }, 1594 + "node_modules/blake3-wasm": { 1595 + "version": "2.1.5", 1596 + "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", 1597 + "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", 1598 + "dev": true, 1599 + "license": "MIT" 1600 + }, 1601 + "node_modules/cac": { 1602 + "version": "6.7.14", 1603 + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", 1604 + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", 1605 + "dev": true, 1606 + "license": "MIT", 1607 + "engines": { 1608 + "node": ">=8" 1609 + } 1610 + }, 1611 + "node_modules/chai": { 1612 + "version": "5.2.0", 1613 + "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", 1614 + "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", 1615 + "dev": true, 1616 + "license": "MIT", 1617 + "dependencies": { 1618 + "assertion-error": "^2.0.1", 1619 + "check-error": "^2.1.1", 1620 + "deep-eql": "^5.0.1", 1621 + "loupe": "^3.1.0", 1622 + "pathval": "^2.0.0" 1623 + }, 1624 + "engines": { 1625 + "node": ">=12" 1626 + } 1627 + }, 1628 + "node_modules/check-error": { 1629 + "version": "2.1.1", 1630 + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", 1631 + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", 1632 + "dev": true, 1633 + "license": "MIT", 1634 + "engines": { 1635 + "node": ">= 16" 1636 + } 1637 + }, 1638 + "node_modules/cjs-module-lexer": { 1639 + "version": "1.4.3", 1640 + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", 1641 + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", 1642 + "dev": true, 1643 + "license": "MIT" 1644 + }, 1645 + "node_modules/color": { 1646 + "version": "4.2.3", 1647 + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", 1648 + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", 1649 + "dev": true, 1650 + "license": "MIT", 1651 + "optional": true, 1652 + "dependencies": { 1653 + "color-convert": "^2.0.1", 1654 + "color-string": "^1.9.0" 1655 + }, 1656 + "engines": { 1657 + "node": ">=12.5.0" 1658 + } 1659 + }, 1660 + "node_modules/color-convert": { 1661 + "version": "2.0.1", 1662 + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", 1663 + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", 1664 + "dev": true, 1665 + "license": "MIT", 1666 + "optional": true, 1667 + "dependencies": { 1668 + "color-name": "~1.1.4" 1669 + }, 1670 + "engines": { 1671 + "node": ">=7.0.0" 1672 + } 1673 + }, 1674 + "node_modules/color-name": { 1675 + "version": "1.1.4", 1676 + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", 1677 + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", 1678 + "dev": true, 1679 + "license": "MIT", 1680 + "optional": true 1681 + }, 1682 + "node_modules/color-string": { 1683 + "version": "1.9.1", 1684 + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", 1685 + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", 1686 + "dev": true, 1687 + "license": "MIT", 1688 + "optional": true, 1689 + "dependencies": { 1690 + "color-name": "^1.0.0", 1691 + "simple-swizzle": "^0.2.2" 1692 + } 1693 + }, 1694 + "node_modules/cookie": { 1695 + "version": "0.7.2", 1696 + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", 1697 + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", 1698 + "dev": true, 1699 + "license": "MIT", 1700 + "engines": { 1701 + "node": ">= 0.6" 1702 + } 1703 + }, 1704 + "node_modules/data-uri-to-buffer": { 1705 + "version": "2.0.2", 1706 + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz", 1707 + "integrity": "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==", 1708 + "dev": true, 1709 + "license": "MIT" 1710 + }, 1711 + "node_modules/debug": { 1712 + "version": "4.4.0", 1713 + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 1714 + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 1715 + "dev": true, 1716 + "license": "MIT", 1717 + "dependencies": { 1718 + "ms": "^2.1.3" 1719 + }, 1720 + "engines": { 1721 + "node": ">=6.0" 1722 + }, 1723 + "peerDependenciesMeta": { 1724 + "supports-color": { 1725 + "optional": true 1726 + } 1727 + } 1728 + }, 1729 + "node_modules/deep-eql": { 1730 + "version": "5.0.2", 1731 + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", 1732 + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", 1733 + "dev": true, 1734 + "license": "MIT", 1735 + "engines": { 1736 + "node": ">=6" 1737 + } 1738 + }, 1739 + "node_modules/defu": { 1740 + "version": "6.1.4", 1741 + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", 1742 + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", 1743 + "dev": true, 1744 + "license": "MIT" 1745 + }, 1746 + "node_modules/detect-libc": { 1747 + "version": "2.0.4", 1748 + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", 1749 + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", 1750 + "dev": true, 1751 + "license": "Apache-2.0", 1752 + "optional": true, 1753 + "engines": { 1754 + "node": ">=8" 1755 + } 1756 + }, 1757 + "node_modules/devalue": { 1758 + "version": "4.3.3", 1759 + "resolved": "https://registry.npmjs.org/devalue/-/devalue-4.3.3.tgz", 1760 + "integrity": "sha512-UH8EL6H2ifcY8TbD2QsxwCC/pr5xSwPvv85LrLXVihmHVC3T3YqTCIwnR5ak0yO1KYqlxrPVOA/JVZJYPy2ATg==", 1761 + "dev": true, 1762 + "license": "MIT" 1763 + }, 1764 + "node_modules/es-module-lexer": { 1765 + "version": "1.7.0", 1766 + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", 1767 + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", 1768 + "dev": true, 1769 + "license": "MIT" 1770 + }, 1771 + "node_modules/esbuild": { 1772 + "version": "0.25.3", 1773 + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.3.tgz", 1774 + "integrity": "sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==", 1775 + "dev": true, 1776 + "hasInstallScript": true, 1777 + "license": "MIT", 1778 + "bin": { 1779 + "esbuild": "bin/esbuild" 1780 + }, 1781 + "engines": { 1782 + "node": ">=18" 1783 + }, 1784 + "optionalDependencies": { 1785 + "@esbuild/aix-ppc64": "0.25.3", 1786 + "@esbuild/android-arm": "0.25.3", 1787 + "@esbuild/android-arm64": "0.25.3", 1788 + "@esbuild/android-x64": "0.25.3", 1789 + "@esbuild/darwin-arm64": "0.25.3", 1790 + "@esbuild/darwin-x64": "0.25.3", 1791 + "@esbuild/freebsd-arm64": "0.25.3", 1792 + "@esbuild/freebsd-x64": "0.25.3", 1793 + "@esbuild/linux-arm": "0.25.3", 1794 + "@esbuild/linux-arm64": "0.25.3", 1795 + "@esbuild/linux-ia32": "0.25.3", 1796 + "@esbuild/linux-loong64": "0.25.3", 1797 + "@esbuild/linux-mips64el": "0.25.3", 1798 + "@esbuild/linux-ppc64": "0.25.3", 1799 + "@esbuild/linux-riscv64": "0.25.3", 1800 + "@esbuild/linux-s390x": "0.25.3", 1801 + "@esbuild/linux-x64": "0.25.3", 1802 + "@esbuild/netbsd-arm64": "0.25.3", 1803 + "@esbuild/netbsd-x64": "0.25.3", 1804 + "@esbuild/openbsd-arm64": "0.25.3", 1805 + "@esbuild/openbsd-x64": "0.25.3", 1806 + "@esbuild/sunos-x64": "0.25.3", 1807 + "@esbuild/win32-arm64": "0.25.3", 1808 + "@esbuild/win32-ia32": "0.25.3", 1809 + "@esbuild/win32-x64": "0.25.3" 1810 + } 1811 + }, 1812 + "node_modules/estree-walker": { 1813 + "version": "3.0.3", 1814 + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", 1815 + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", 1816 + "dev": true, 1817 + "license": "MIT", 1818 + "dependencies": { 1819 + "@types/estree": "^1.0.0" 1820 + } 1821 + }, 1822 + "node_modules/exit-hook": { 1823 + "version": "2.2.1", 1824 + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz", 1825 + "integrity": "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==", 1826 + "dev": true, 1827 + "license": "MIT", 1828 + "engines": { 1829 + "node": ">=6" 1830 + }, 1831 + "funding": { 1832 + "url": "https://github.com/sponsors/sindresorhus" 1833 + } 1834 + }, 1835 + "node_modules/expect-type": { 1836 + "version": "1.2.1", 1837 + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz", 1838 + "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==", 1839 + "dev": true, 1840 + "license": "Apache-2.0", 1841 + "engines": { 1842 + "node": ">=12.0.0" 1843 + } 1844 + }, 1845 + "node_modules/exsolve": { 1846 + "version": "1.0.5", 1847 + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.5.tgz", 1848 + "integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==", 1849 + "dev": true, 1850 + "license": "MIT" 1851 + }, 1852 + "node_modules/fdir": { 1853 + "version": "6.4.4", 1854 + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", 1855 + "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", 1856 + "dev": true, 1857 + "license": "MIT", 1858 + "peerDependencies": { 1859 + "picomatch": "^3 || ^4" 1860 + }, 1861 + "peerDependenciesMeta": { 1862 + "picomatch": { 1863 + "optional": true 1864 + } 1865 + } 1866 + }, 1867 + "node_modules/fsevents": { 1868 + "version": "2.3.3", 1869 + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 1870 + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 1871 + "dev": true, 1872 + "hasInstallScript": true, 1873 + "license": "MIT", 1874 + "optional": true, 1875 + "os": [ 1876 + "darwin" 1877 + ], 1878 + "engines": { 1879 + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1880 + } 1881 + }, 1882 + "node_modules/get-source": { 1883 + "version": "2.0.12", 1884 + "resolved": "https://registry.npmjs.org/get-source/-/get-source-2.0.12.tgz", 1885 + "integrity": "sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==", 1886 + "dev": true, 1887 + "license": "Unlicense", 1888 + "dependencies": { 1889 + "data-uri-to-buffer": "^2.0.0", 1890 + "source-map": "^0.6.1" 1891 + } 1892 + }, 1893 + "node_modules/glob-to-regexp": { 1894 + "version": "0.4.1", 1895 + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", 1896 + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", 1897 + "dev": true, 1898 + "license": "BSD-2-Clause" 1899 + }, 1900 + "node_modules/is-arrayish": { 1901 + "version": "0.3.2", 1902 + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", 1903 + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", 1904 + "dev": true, 1905 + "license": "MIT", 1906 + "optional": true 1907 + }, 1908 + "node_modules/loupe": { 1909 + "version": "3.1.3", 1910 + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz", 1911 + "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==", 1912 + "dev": true, 1913 + "license": "MIT" 1914 + }, 1915 + "node_modules/magic-string": { 1916 + "version": "0.30.17", 1917 + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", 1918 + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", 1919 + "dev": true, 1920 + "license": "MIT", 1921 + "dependencies": { 1922 + "@jridgewell/sourcemap-codec": "^1.5.0" 1923 + } 1924 + }, 1925 + "node_modules/mime": { 1926 + "version": "3.0.0", 1927 + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", 1928 + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", 1929 + "dev": true, 1930 + "license": "MIT", 1931 + "bin": { 1932 + "mime": "cli.js" 1933 + }, 1934 + "engines": { 1935 + "node": ">=10.0.0" 1936 + } 1937 + }, 1938 + "node_modules/miniflare": { 1939 + "version": "4.20250428.1", 1940 + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20250428.1.tgz", 1941 + "integrity": "sha512-M3qcJXjeAEimHrEeWXEhrJiC3YHB5M3QSqqK67pOTI+lHn0QyVG/2iFUjVJ/nv+i10uxeAEva8GRGeu+tKRCmQ==", 1942 + "dev": true, 1943 + "license": "MIT", 1944 + "dependencies": { 1945 + "@cspotcode/source-map-support": "0.8.1", 1946 + "acorn": "8.14.0", 1947 + "acorn-walk": "8.3.2", 1948 + "exit-hook": "2.2.1", 1949 + "glob-to-regexp": "0.4.1", 1950 + "stoppable": "1.1.0", 1951 + "undici": "^5.28.5", 1952 + "workerd": "1.20250428.0", 1953 + "ws": "8.18.0", 1954 + "youch": "3.3.4", 1955 + "zod": "3.22.3" 1956 + }, 1957 + "bin": { 1958 + "miniflare": "bootstrap.js" 1959 + }, 1960 + "engines": { 1961 + "node": ">=18.0.0" 1962 + } 1963 + }, 1964 + "node_modules/miniflare/node_modules/zod": { 1965 + "version": "3.22.3", 1966 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz", 1967 + "integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==", 1968 + "dev": true, 1969 + "license": "MIT", 1970 + "funding": { 1971 + "url": "https://github.com/sponsors/colinhacks" 1972 + } 1973 + }, 1974 + "node_modules/ms": { 1975 + "version": "2.1.3", 1976 + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1977 + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1978 + "dev": true, 1979 + "license": "MIT" 1980 + }, 1981 + "node_modules/multiformats": { 1982 + "version": "9.9.0", 1983 + "resolved": "https://registry.npmjs.org/multiformats/-/multiformats-9.9.0.tgz", 1984 + "integrity": "sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==", 1985 + "license": "(Apache-2.0 AND MIT)" 1986 + }, 1987 + "node_modules/mustache": { 1988 + "version": "4.2.0", 1989 + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", 1990 + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", 1991 + "dev": true, 1992 + "license": "MIT", 1993 + "bin": { 1994 + "mustache": "bin/mustache" 1995 + } 1996 + }, 1997 + "node_modules/nanoid": { 1998 + "version": "3.3.11", 1999 + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", 2000 + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", 2001 + "dev": true, 2002 + "funding": [ 2003 + { 2004 + "type": "github", 2005 + "url": "https://github.com/sponsors/ai" 2006 + } 2007 + ], 2008 + "license": "MIT", 2009 + "bin": { 2010 + "nanoid": "bin/nanoid.cjs" 2011 + }, 2012 + "engines": { 2013 + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 2014 + } 2015 + }, 2016 + "node_modules/ohash": { 2017 + "version": "2.0.11", 2018 + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", 2019 + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", 2020 + "dev": true, 2021 + "license": "MIT" 2022 + }, 2023 + "node_modules/path-to-regexp": { 2024 + "version": "6.3.0", 2025 + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", 2026 + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", 2027 + "dev": true, 2028 + "license": "MIT" 2029 + }, 2030 + "node_modules/pathe": { 2031 + "version": "2.0.3", 2032 + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", 2033 + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", 2034 + "dev": true, 2035 + "license": "MIT" 2036 + }, 2037 + "node_modules/pathval": { 2038 + "version": "2.0.0", 2039 + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", 2040 + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", 2041 + "dev": true, 2042 + "license": "MIT", 2043 + "engines": { 2044 + "node": ">= 14.16" 2045 + } 2046 + }, 2047 + "node_modules/picocolors": { 2048 + "version": "1.1.1", 2049 + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", 2050 + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", 2051 + "dev": true, 2052 + "license": "ISC" 2053 + }, 2054 + "node_modules/picomatch": { 2055 + "version": "4.0.2", 2056 + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", 2057 + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", 2058 + "dev": true, 2059 + "license": "MIT", 2060 + "engines": { 2061 + "node": ">=12" 2062 + }, 2063 + "funding": { 2064 + "url": "https://github.com/sponsors/jonschlinkert" 2065 + } 2066 + }, 2067 + "node_modules/postcss": { 2068 + "version": "8.5.3", 2069 + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", 2070 + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", 2071 + "dev": true, 2072 + "funding": [ 2073 + { 2074 + "type": "opencollective", 2075 + "url": "https://opencollective.com/postcss/" 2076 + }, 2077 + { 2078 + "type": "tidelift", 2079 + "url": "https://tidelift.com/funding/github/npm/postcss" 2080 + }, 2081 + { 2082 + "type": "github", 2083 + "url": "https://github.com/sponsors/ai" 2084 + } 2085 + ], 2086 + "license": "MIT", 2087 + "dependencies": { 2088 + "nanoid": "^3.3.8", 2089 + "picocolors": "^1.1.1", 2090 + "source-map-js": "^1.2.1" 2091 + }, 2092 + "engines": { 2093 + "node": "^10 || ^12 || >=14" 2094 + } 2095 + }, 2096 + "node_modules/printable-characters": { 2097 + "version": "1.0.42", 2098 + "resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz", 2099 + "integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==", 2100 + "dev": true, 2101 + "license": "Unlicense" 2102 + }, 2103 + "node_modules/rollup": { 2104 + "version": "4.40.1", 2105 + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.1.tgz", 2106 + "integrity": "sha512-C5VvvgCCyfyotVITIAv+4efVytl5F7wt+/I2i9q9GZcEXW9BP52YYOXC58igUi+LFZVHukErIIqQSWwv/M3WRw==", 2107 + "dev": true, 2108 + "license": "MIT", 2109 + "dependencies": { 2110 + "@types/estree": "1.0.7" 2111 + }, 2112 + "bin": { 2113 + "rollup": "dist/bin/rollup" 2114 + }, 2115 + "engines": { 2116 + "node": ">=18.0.0", 2117 + "npm": ">=8.0.0" 2118 + }, 2119 + "optionalDependencies": { 2120 + "@rollup/rollup-android-arm-eabi": "4.40.1", 2121 + "@rollup/rollup-android-arm64": "4.40.1", 2122 + "@rollup/rollup-darwin-arm64": "4.40.1", 2123 + "@rollup/rollup-darwin-x64": "4.40.1", 2124 + "@rollup/rollup-freebsd-arm64": "4.40.1", 2125 + "@rollup/rollup-freebsd-x64": "4.40.1", 2126 + "@rollup/rollup-linux-arm-gnueabihf": "4.40.1", 2127 + "@rollup/rollup-linux-arm-musleabihf": "4.40.1", 2128 + "@rollup/rollup-linux-arm64-gnu": "4.40.1", 2129 + "@rollup/rollup-linux-arm64-musl": "4.40.1", 2130 + "@rollup/rollup-linux-loongarch64-gnu": "4.40.1", 2131 + "@rollup/rollup-linux-powerpc64le-gnu": "4.40.1", 2132 + "@rollup/rollup-linux-riscv64-gnu": "4.40.1", 2133 + "@rollup/rollup-linux-riscv64-musl": "4.40.1", 2134 + "@rollup/rollup-linux-s390x-gnu": "4.40.1", 2135 + "@rollup/rollup-linux-x64-gnu": "4.40.1", 2136 + "@rollup/rollup-linux-x64-musl": "4.40.1", 2137 + "@rollup/rollup-win32-arm64-msvc": "4.40.1", 2138 + "@rollup/rollup-win32-ia32-msvc": "4.40.1", 2139 + "@rollup/rollup-win32-x64-msvc": "4.40.1", 2140 + "fsevents": "~2.3.2" 2141 + } 2142 + }, 2143 + "node_modules/semver": { 2144 + "version": "7.7.1", 2145 + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", 2146 + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", 2147 + "dev": true, 2148 + "license": "ISC", 2149 + "bin": { 2150 + "semver": "bin/semver.js" 2151 + }, 2152 + "engines": { 2153 + "node": ">=10" 2154 + } 2155 + }, 2156 + "node_modules/sharp": { 2157 + "version": "0.33.5", 2158 + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", 2159 + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", 2160 + "dev": true, 2161 + "hasInstallScript": true, 2162 + "license": "Apache-2.0", 2163 + "optional": true, 2164 + "dependencies": { 2165 + "color": "^4.2.3", 2166 + "detect-libc": "^2.0.3", 2167 + "semver": "^7.6.3" 2168 + }, 2169 + "engines": { 2170 + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" 2171 + }, 2172 + "funding": { 2173 + "url": "https://opencollective.com/libvips" 2174 + }, 2175 + "optionalDependencies": { 2176 + "@img/sharp-darwin-arm64": "0.33.5", 2177 + "@img/sharp-darwin-x64": "0.33.5", 2178 + "@img/sharp-libvips-darwin-arm64": "1.0.4", 2179 + "@img/sharp-libvips-darwin-x64": "1.0.4", 2180 + "@img/sharp-libvips-linux-arm": "1.0.5", 2181 + "@img/sharp-libvips-linux-arm64": "1.0.4", 2182 + "@img/sharp-libvips-linux-s390x": "1.0.4", 2183 + "@img/sharp-libvips-linux-x64": "1.0.4", 2184 + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", 2185 + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", 2186 + "@img/sharp-linux-arm": "0.33.5", 2187 + "@img/sharp-linux-arm64": "0.33.5", 2188 + "@img/sharp-linux-s390x": "0.33.5", 2189 + "@img/sharp-linux-x64": "0.33.5", 2190 + "@img/sharp-linuxmusl-arm64": "0.33.5", 2191 + "@img/sharp-linuxmusl-x64": "0.33.5", 2192 + "@img/sharp-wasm32": "0.33.5", 2193 + "@img/sharp-win32-ia32": "0.33.5", 2194 + "@img/sharp-win32-x64": "0.33.5" 2195 + } 2196 + }, 2197 + "node_modules/siginfo": { 2198 + "version": "2.0.0", 2199 + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", 2200 + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", 2201 + "dev": true, 2202 + "license": "ISC" 2203 + }, 2204 + "node_modules/simple-swizzle": { 2205 + "version": "0.2.2", 2206 + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", 2207 + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", 2208 + "dev": true, 2209 + "license": "MIT", 2210 + "optional": true, 2211 + "dependencies": { 2212 + "is-arrayish": "^0.3.1" 2213 + } 2214 + }, 2215 + "node_modules/source-map": { 2216 + "version": "0.6.1", 2217 + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 2218 + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 2219 + "dev": true, 2220 + "license": "BSD-3-Clause", 2221 + "engines": { 2222 + "node": ">=0.10.0" 2223 + } 2224 + }, 2225 + "node_modules/source-map-js": { 2226 + "version": "1.2.1", 2227 + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", 2228 + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", 2229 + "dev": true, 2230 + "license": "BSD-3-Clause", 2231 + "engines": { 2232 + "node": ">=0.10.0" 2233 + } 2234 + }, 2235 + "node_modules/stackback": { 2236 + "version": "0.0.2", 2237 + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", 2238 + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", 2239 + "dev": true, 2240 + "license": "MIT" 2241 + }, 2242 + "node_modules/stacktracey": { 2243 + "version": "2.1.8", 2244 + "resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-2.1.8.tgz", 2245 + "integrity": "sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==", 2246 + "dev": true, 2247 + "license": "Unlicense", 2248 + "dependencies": { 2249 + "as-table": "^1.0.36", 2250 + "get-source": "^2.0.12" 2251 + } 2252 + }, 2253 + "node_modules/std-env": { 2254 + "version": "3.9.0", 2255 + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz", 2256 + "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==", 2257 + "dev": true, 2258 + "license": "MIT" 2259 + }, 2260 + "node_modules/stoppable": { 2261 + "version": "1.1.0", 2262 + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", 2263 + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", 2264 + "dev": true, 2265 + "license": "MIT", 2266 + "engines": { 2267 + "node": ">=4", 2268 + "npm": ">=6" 2269 + } 2270 + }, 2271 + "node_modules/tinybench": { 2272 + "version": "2.9.0", 2273 + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", 2274 + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", 2275 + "dev": true, 2276 + "license": "MIT" 2277 + }, 2278 + "node_modules/tinyexec": { 2279 + "version": "0.3.2", 2280 + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", 2281 + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", 2282 + "dev": true, 2283 + "license": "MIT" 2284 + }, 2285 + "node_modules/tinyglobby": { 2286 + "version": "0.2.13", 2287 + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", 2288 + "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", 2289 + "dev": true, 2290 + "license": "MIT", 2291 + "dependencies": { 2292 + "fdir": "^6.4.4", 2293 + "picomatch": "^4.0.2" 2294 + }, 2295 + "engines": { 2296 + "node": ">=12.0.0" 2297 + }, 2298 + "funding": { 2299 + "url": "https://github.com/sponsors/SuperchupuDev" 2300 + } 2301 + }, 2302 + "node_modules/tinypool": { 2303 + "version": "1.0.2", 2304 + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz", 2305 + "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==", 2306 + "dev": true, 2307 + "license": "MIT", 2308 + "engines": { 2309 + "node": "^18.0.0 || >=20.0.0" 2310 + } 2311 + }, 2312 + "node_modules/tinyrainbow": { 2313 + "version": "2.0.0", 2314 + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", 2315 + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", 2316 + "dev": true, 2317 + "license": "MIT", 2318 + "engines": { 2319 + "node": ">=14.0.0" 2320 + } 2321 + }, 2322 + "node_modules/tinyspy": { 2323 + "version": "3.0.2", 2324 + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", 2325 + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", 2326 + "dev": true, 2327 + "license": "MIT", 2328 + "engines": { 2329 + "node": ">=14.0.0" 2330 + } 2331 + }, 2332 + "node_modules/tslib": { 2333 + "version": "2.8.1", 2334 + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", 2335 + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", 2336 + "license": "0BSD" 2337 + }, 2338 + "node_modules/ufo": { 2339 + "version": "1.6.1", 2340 + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", 2341 + "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", 2342 + "dev": true, 2343 + "license": "MIT" 2344 + }, 2345 + "node_modules/uint8arrays": { 2346 + "version": "3.0.0", 2347 + "resolved": "https://registry.npmjs.org/uint8arrays/-/uint8arrays-3.0.0.tgz", 2348 + "integrity": "sha512-HRCx0q6O9Bfbp+HHSfQQKD7wU70+lydKVt4EghkdOvlK/NlrF90z+eXV34mUd48rNvVJXwkrMSPpCATkct8fJA==", 2349 + "license": "MIT", 2350 + "dependencies": { 2351 + "multiformats": "^9.4.2" 2352 + } 2353 + }, 2354 + "node_modules/undici": { 2355 + "version": "5.29.0", 2356 + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", 2357 + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", 2358 + "dev": true, 2359 + "license": "MIT", 2360 + "dependencies": { 2361 + "@fastify/busboy": "^2.0.0" 2362 + }, 2363 + "engines": { 2364 + "node": ">=14.0" 2365 + } 2366 + }, 2367 + "node_modules/unenv": { 2368 + "version": "2.0.0-rc.15", 2369 + "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.15.tgz", 2370 + "integrity": "sha512-J/rEIZU8w6FOfLNz/hNKsnY+fFHWnu9MH4yRbSZF3xbbGHovcetXPs7sD+9p8L6CeNC//I9bhRYAOsBt2u7/OA==", 2371 + "dev": true, 2372 + "license": "MIT", 2373 + "dependencies": { 2374 + "defu": "^6.1.4", 2375 + "exsolve": "^1.0.4", 2376 + "ohash": "^2.0.11", 2377 + "pathe": "^2.0.3", 2378 + "ufo": "^1.5.4" 2379 + } 2380 + }, 2381 + "node_modules/unicode-segmenter": { 2382 + "version": "0.14.4", 2383 + "resolved": "https://registry.npmjs.org/unicode-segmenter/-/unicode-segmenter-0.14.4.tgz", 2384 + "integrity": "sha512-pR5VCiCrLrKOL6FRW61jnk9+wyMtKKowq+jyFY9oc6uHbWKhDL4yVRiI4YZPksGMK72Pahh8m0cn/0JvbDDyJg==", 2385 + "license": "MIT" 2386 + }, 2387 + "node_modules/vite": { 2388 + "version": "6.3.4", 2389 + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.4.tgz", 2390 + "integrity": "sha512-BiReIiMS2fyFqbqNT/Qqt4CVITDU9M9vE+DKcVAsB+ZV0wvTKd+3hMbkpxz1b+NmEDMegpVbisKiAZOnvO92Sw==", 2391 + "dev": true, 2392 + "license": "MIT", 2393 + "dependencies": { 2394 + "esbuild": "^0.25.0", 2395 + "fdir": "^6.4.4", 2396 + "picomatch": "^4.0.2", 2397 + "postcss": "^8.5.3", 2398 + "rollup": "^4.34.9", 2399 + "tinyglobby": "^0.2.13" 2400 + }, 2401 + "bin": { 2402 + "vite": "bin/vite.js" 2403 + }, 2404 + "engines": { 2405 + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" 2406 + }, 2407 + "funding": { 2408 + "url": "https://github.com/vitejs/vite?sponsor=1" 2409 + }, 2410 + "optionalDependencies": { 2411 + "fsevents": "~2.3.3" 2412 + }, 2413 + "peerDependencies": { 2414 + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", 2415 + "jiti": ">=1.21.0", 2416 + "less": "*", 2417 + "lightningcss": "^1.21.0", 2418 + "sass": "*", 2419 + "sass-embedded": "*", 2420 + "stylus": "*", 2421 + "sugarss": "*", 2422 + "terser": "^5.16.0", 2423 + "tsx": "^4.8.1", 2424 + "yaml": "^2.4.2" 2425 + }, 2426 + "peerDependenciesMeta": { 2427 + "@types/node": { 2428 + "optional": true 2429 + }, 2430 + "jiti": { 2431 + "optional": true 2432 + }, 2433 + "less": { 2434 + "optional": true 2435 + }, 2436 + "lightningcss": { 2437 + "optional": true 2438 + }, 2439 + "sass": { 2440 + "optional": true 2441 + }, 2442 + "sass-embedded": { 2443 + "optional": true 2444 + }, 2445 + "stylus": { 2446 + "optional": true 2447 + }, 2448 + "sugarss": { 2449 + "optional": true 2450 + }, 2451 + "terser": { 2452 + "optional": true 2453 + }, 2454 + "tsx": { 2455 + "optional": true 2456 + }, 2457 + "yaml": { 2458 + "optional": true 2459 + } 2460 + } 2461 + }, 2462 + "node_modules/vite-node": { 2463 + "version": "3.0.9", 2464 + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.0.9.tgz", 2465 + "integrity": "sha512-w3Gdx7jDcuT9cNn9jExXgOyKmf5UOTb6WMHz8LGAm54eS1Elf5OuBhCxl6zJxGhEeIkgsE1WbHuoL0mj/UXqXg==", 2466 + "dev": true, 2467 + "license": "MIT", 2468 + "dependencies": { 2469 + "cac": "^6.7.14", 2470 + "debug": "^4.4.0", 2471 + "es-module-lexer": "^1.6.0", 2472 + "pathe": "^2.0.3", 2473 + "vite": "^5.0.0 || ^6.0.0" 2474 + }, 2475 + "bin": { 2476 + "vite-node": "vite-node.mjs" 2477 + }, 2478 + "engines": { 2479 + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" 2480 + }, 2481 + "funding": { 2482 + "url": "https://opencollective.com/vitest" 2483 + } 2484 + }, 2485 + "node_modules/vitest": { 2486 + "version": "3.0.9", 2487 + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.0.9.tgz", 2488 + "integrity": "sha512-BbcFDqNyBlfSpATmTtXOAOj71RNKDDvjBM/uPfnxxVGrG+FSH2RQIwgeEngTaTkuU/h0ScFvf+tRcKfYXzBybQ==", 2489 + "dev": true, 2490 + "license": "MIT", 2491 + "dependencies": { 2492 + "@vitest/expect": "3.0.9", 2493 + "@vitest/mocker": "3.0.9", 2494 + "@vitest/pretty-format": "^3.0.9", 2495 + "@vitest/runner": "3.0.9", 2496 + "@vitest/snapshot": "3.0.9", 2497 + "@vitest/spy": "3.0.9", 2498 + "@vitest/utils": "3.0.9", 2499 + "chai": "^5.2.0", 2500 + "debug": "^4.4.0", 2501 + "expect-type": "^1.1.0", 2502 + "magic-string": "^0.30.17", 2503 + "pathe": "^2.0.3", 2504 + "std-env": "^3.8.0", 2505 + "tinybench": "^2.9.0", 2506 + "tinyexec": "^0.3.2", 2507 + "tinypool": "^1.0.2", 2508 + "tinyrainbow": "^2.0.0", 2509 + "vite": "^5.0.0 || ^6.0.0", 2510 + "vite-node": "3.0.9", 2511 + "why-is-node-running": "^2.3.0" 2512 + }, 2513 + "bin": { 2514 + "vitest": "vitest.mjs" 2515 + }, 2516 + "engines": { 2517 + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" 2518 + }, 2519 + "funding": { 2520 + "url": "https://opencollective.com/vitest" 2521 + }, 2522 + "peerDependencies": { 2523 + "@edge-runtime/vm": "*", 2524 + "@types/debug": "^4.1.12", 2525 + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", 2526 + "@vitest/browser": "3.0.9", 2527 + "@vitest/ui": "3.0.9", 2528 + "happy-dom": "*", 2529 + "jsdom": "*" 2530 + }, 2531 + "peerDependenciesMeta": { 2532 + "@edge-runtime/vm": { 2533 + "optional": true 2534 + }, 2535 + "@types/debug": { 2536 + "optional": true 2537 + }, 2538 + "@types/node": { 2539 + "optional": true 2540 + }, 2541 + "@vitest/browser": { 2542 + "optional": true 2543 + }, 2544 + "@vitest/ui": { 2545 + "optional": true 2546 + }, 2547 + "happy-dom": { 2548 + "optional": true 2549 + }, 2550 + "jsdom": { 2551 + "optional": true 2552 + } 2553 + } 2554 + }, 2555 + "node_modules/why-is-node-running": { 2556 + "version": "2.3.0", 2557 + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", 2558 + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", 2559 + "dev": true, 2560 + "license": "MIT", 2561 + "dependencies": { 2562 + "siginfo": "^2.0.0", 2563 + "stackback": "0.0.2" 2564 + }, 2565 + "bin": { 2566 + "why-is-node-running": "cli.js" 2567 + }, 2568 + "engines": { 2569 + "node": ">=8" 2570 + } 2571 + }, 2572 + "node_modules/workerd": { 2573 + "version": "1.20250428.0", 2574 + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250428.0.tgz", 2575 + "integrity": "sha512-JJNWkHkwPQKQdvtM9UORijgYdcdJsihA4SfYjwh02IUQsdMyZ9jizV1sX9yWi9B9ptlohTW8UNHJEATuphGgdg==", 2576 + "dev": true, 2577 + "hasInstallScript": true, 2578 + "license": "Apache-2.0", 2579 + "bin": { 2580 + "workerd": "bin/workerd" 2581 + }, 2582 + "engines": { 2583 + "node": ">=16" 2584 + }, 2585 + "optionalDependencies": { 2586 + "@cloudflare/workerd-darwin-64": "1.20250428.0", 2587 + "@cloudflare/workerd-darwin-arm64": "1.20250428.0", 2588 + "@cloudflare/workerd-linux-64": "1.20250428.0", 2589 + "@cloudflare/workerd-linux-arm64": "1.20250428.0", 2590 + "@cloudflare/workerd-windows-64": "1.20250428.0" 2591 + } 2592 + }, 2593 + "node_modules/wrangler": { 2594 + "version": "4.14.1", 2595 + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.14.1.tgz", 2596 + "integrity": "sha512-EU7IThP7i68TBftJJSveogvWZ5k/WRijcJh3UclDWiWWhDZTPbL6LOJEFhHKqFzHOaC4Y2Aewt48rfTz0e7oCw==", 2597 + "dev": true, 2598 + "license": "MIT OR Apache-2.0", 2599 + "dependencies": { 2600 + "@cloudflare/kv-asset-handler": "0.4.0", 2601 + "@cloudflare/unenv-preset": "2.3.1", 2602 + "blake3-wasm": "2.1.5", 2603 + "esbuild": "0.25.2", 2604 + "miniflare": "4.20250428.1", 2605 + "path-to-regexp": "6.3.0", 2606 + "unenv": "2.0.0-rc.15", 2607 + "workerd": "1.20250428.0" 2608 + }, 2609 + "bin": { 2610 + "wrangler": "bin/wrangler.js", 2611 + "wrangler2": "bin/wrangler.js" 2612 + }, 2613 + "engines": { 2614 + "node": ">=18.0.0" 2615 + }, 2616 + "optionalDependencies": { 2617 + "fsevents": "~2.3.2", 2618 + "sharp": "^0.33.5" 2619 + }, 2620 + "peerDependencies": { 2621 + "@cloudflare/workers-types": "^4.20250428.0" 2622 + }, 2623 + "peerDependenciesMeta": { 2624 + "@cloudflare/workers-types": { 2625 + "optional": true 2626 + } 2627 + } 2628 + }, 2629 + "node_modules/wrangler/node_modules/@esbuild/aix-ppc64": { 2630 + "version": "0.25.2", 2631 + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", 2632 + "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", 2633 + "cpu": [ 2634 + "ppc64" 2635 + ], 2636 + "dev": true, 2637 + "license": "MIT", 2638 + "optional": true, 2639 + "os": [ 2640 + "aix" 2641 + ], 2642 + "engines": { 2643 + "node": ">=18" 2644 + } 2645 + }, 2646 + "node_modules/wrangler/node_modules/@esbuild/android-arm": { 2647 + "version": "0.25.2", 2648 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", 2649 + "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", 2650 + "cpu": [ 2651 + "arm" 2652 + ], 2653 + "dev": true, 2654 + "license": "MIT", 2655 + "optional": true, 2656 + "os": [ 2657 + "android" 2658 + ], 2659 + "engines": { 2660 + "node": ">=18" 2661 + } 2662 + }, 2663 + "node_modules/wrangler/node_modules/@esbuild/android-arm64": { 2664 + "version": "0.25.2", 2665 + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", 2666 + "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", 2667 + "cpu": [ 2668 + "arm64" 2669 + ], 2670 + "dev": true, 2671 + "license": "MIT", 2672 + "optional": true, 2673 + "os": [ 2674 + "android" 2675 + ], 2676 + "engines": { 2677 + "node": ">=18" 2678 + } 2679 + }, 2680 + "node_modules/wrangler/node_modules/@esbuild/android-x64": { 2681 + "version": "0.25.2", 2682 + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", 2683 + "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", 2684 + "cpu": [ 2685 + "x64" 2686 + ], 2687 + "dev": true, 2688 + "license": "MIT", 2689 + "optional": true, 2690 + "os": [ 2691 + "android" 2692 + ], 2693 + "engines": { 2694 + "node": ">=18" 2695 + } 2696 + }, 2697 + "node_modules/wrangler/node_modules/@esbuild/darwin-arm64": { 2698 + "version": "0.25.2", 2699 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", 2700 + "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", 2701 + "cpu": [ 2702 + "arm64" 2703 + ], 2704 + "dev": true, 2705 + "license": "MIT", 2706 + "optional": true, 2707 + "os": [ 2708 + "darwin" 2709 + ], 2710 + "engines": { 2711 + "node": ">=18" 2712 + } 2713 + }, 2714 + "node_modules/wrangler/node_modules/@esbuild/darwin-x64": { 2715 + "version": "0.25.2", 2716 + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", 2717 + "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", 2718 + "cpu": [ 2719 + "x64" 2720 + ], 2721 + "dev": true, 2722 + "license": "MIT", 2723 + "optional": true, 2724 + "os": [ 2725 + "darwin" 2726 + ], 2727 + "engines": { 2728 + "node": ">=18" 2729 + } 2730 + }, 2731 + "node_modules/wrangler/node_modules/@esbuild/freebsd-arm64": { 2732 + "version": "0.25.2", 2733 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", 2734 + "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", 2735 + "cpu": [ 2736 + "arm64" 2737 + ], 2738 + "dev": true, 2739 + "license": "MIT", 2740 + "optional": true, 2741 + "os": [ 2742 + "freebsd" 2743 + ], 2744 + "engines": { 2745 + "node": ">=18" 2746 + } 2747 + }, 2748 + "node_modules/wrangler/node_modules/@esbuild/freebsd-x64": { 2749 + "version": "0.25.2", 2750 + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", 2751 + "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", 2752 + "cpu": [ 2753 + "x64" 2754 + ], 2755 + "dev": true, 2756 + "license": "MIT", 2757 + "optional": true, 2758 + "os": [ 2759 + "freebsd" 2760 + ], 2761 + "engines": { 2762 + "node": ">=18" 2763 + } 2764 + }, 2765 + "node_modules/wrangler/node_modules/@esbuild/linux-arm": { 2766 + "version": "0.25.2", 2767 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", 2768 + "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", 2769 + "cpu": [ 2770 + "arm" 2771 + ], 2772 + "dev": true, 2773 + "license": "MIT", 2774 + "optional": true, 2775 + "os": [ 2776 + "linux" 2777 + ], 2778 + "engines": { 2779 + "node": ">=18" 2780 + } 2781 + }, 2782 + "node_modules/wrangler/node_modules/@esbuild/linux-arm64": { 2783 + "version": "0.25.2", 2784 + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", 2785 + "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", 2786 + "cpu": [ 2787 + "arm64" 2788 + ], 2789 + "dev": true, 2790 + "license": "MIT", 2791 + "optional": true, 2792 + "os": [ 2793 + "linux" 2794 + ], 2795 + "engines": { 2796 + "node": ">=18" 2797 + } 2798 + }, 2799 + "node_modules/wrangler/node_modules/@esbuild/linux-ia32": { 2800 + "version": "0.25.2", 2801 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", 2802 + "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", 2803 + "cpu": [ 2804 + "ia32" 2805 + ], 2806 + "dev": true, 2807 + "license": "MIT", 2808 + "optional": true, 2809 + "os": [ 2810 + "linux" 2811 + ], 2812 + "engines": { 2813 + "node": ">=18" 2814 + } 2815 + }, 2816 + "node_modules/wrangler/node_modules/@esbuild/linux-loong64": { 2817 + "version": "0.25.2", 2818 + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", 2819 + "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", 2820 + "cpu": [ 2821 + "loong64" 2822 + ], 2823 + "dev": true, 2824 + "license": "MIT", 2825 + "optional": true, 2826 + "os": [ 2827 + "linux" 2828 + ], 2829 + "engines": { 2830 + "node": ">=18" 2831 + } 2832 + }, 2833 + "node_modules/wrangler/node_modules/@esbuild/linux-mips64el": { 2834 + "version": "0.25.2", 2835 + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", 2836 + "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", 2837 + "cpu": [ 2838 + "mips64el" 2839 + ], 2840 + "dev": true, 2841 + "license": "MIT", 2842 + "optional": true, 2843 + "os": [ 2844 + "linux" 2845 + ], 2846 + "engines": { 2847 + "node": ">=18" 2848 + } 2849 + }, 2850 + "node_modules/wrangler/node_modules/@esbuild/linux-ppc64": { 2851 + "version": "0.25.2", 2852 + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", 2853 + "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", 2854 + "cpu": [ 2855 + "ppc64" 2856 + ], 2857 + "dev": true, 2858 + "license": "MIT", 2859 + "optional": true, 2860 + "os": [ 2861 + "linux" 2862 + ], 2863 + "engines": { 2864 + "node": ">=18" 2865 + } 2866 + }, 2867 + "node_modules/wrangler/node_modules/@esbuild/linux-riscv64": { 2868 + "version": "0.25.2", 2869 + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", 2870 + "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", 2871 + "cpu": [ 2872 + "riscv64" 2873 + ], 2874 + "dev": true, 2875 + "license": "MIT", 2876 + "optional": true, 2877 + "os": [ 2878 + "linux" 2879 + ], 2880 + "engines": { 2881 + "node": ">=18" 2882 + } 2883 + }, 2884 + "node_modules/wrangler/node_modules/@esbuild/linux-s390x": { 2885 + "version": "0.25.2", 2886 + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", 2887 + "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", 2888 + "cpu": [ 2889 + "s390x" 2890 + ], 2891 + "dev": true, 2892 + "license": "MIT", 2893 + "optional": true, 2894 + "os": [ 2895 + "linux" 2896 + ], 2897 + "engines": { 2898 + "node": ">=18" 2899 + } 2900 + }, 2901 + "node_modules/wrangler/node_modules/@esbuild/linux-x64": { 2902 + "version": "0.25.2", 2903 + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", 2904 + "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", 2905 + "cpu": [ 2906 + "x64" 2907 + ], 2908 + "dev": true, 2909 + "license": "MIT", 2910 + "optional": true, 2911 + "os": [ 2912 + "linux" 2913 + ], 2914 + "engines": { 2915 + "node": ">=18" 2916 + } 2917 + }, 2918 + "node_modules/wrangler/node_modules/@esbuild/netbsd-arm64": { 2919 + "version": "0.25.2", 2920 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", 2921 + "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", 2922 + "cpu": [ 2923 + "arm64" 2924 + ], 2925 + "dev": true, 2926 + "license": "MIT", 2927 + "optional": true, 2928 + "os": [ 2929 + "netbsd" 2930 + ], 2931 + "engines": { 2932 + "node": ">=18" 2933 + } 2934 + }, 2935 + "node_modules/wrangler/node_modules/@esbuild/netbsd-x64": { 2936 + "version": "0.25.2", 2937 + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", 2938 + "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", 2939 + "cpu": [ 2940 + "x64" 2941 + ], 2942 + "dev": true, 2943 + "license": "MIT", 2944 + "optional": true, 2945 + "os": [ 2946 + "netbsd" 2947 + ], 2948 + "engines": { 2949 + "node": ">=18" 2950 + } 2951 + }, 2952 + "node_modules/wrangler/node_modules/@esbuild/openbsd-arm64": { 2953 + "version": "0.25.2", 2954 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", 2955 + "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", 2956 + "cpu": [ 2957 + "arm64" 2958 + ], 2959 + "dev": true, 2960 + "license": "MIT", 2961 + "optional": true, 2962 + "os": [ 2963 + "openbsd" 2964 + ], 2965 + "engines": { 2966 + "node": ">=18" 2967 + } 2968 + }, 2969 + "node_modules/wrangler/node_modules/@esbuild/openbsd-x64": { 2970 + "version": "0.25.2", 2971 + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", 2972 + "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", 2973 + "cpu": [ 2974 + "x64" 2975 + ], 2976 + "dev": true, 2977 + "license": "MIT", 2978 + "optional": true, 2979 + "os": [ 2980 + "openbsd" 2981 + ], 2982 + "engines": { 2983 + "node": ">=18" 2984 + } 2985 + }, 2986 + "node_modules/wrangler/node_modules/@esbuild/sunos-x64": { 2987 + "version": "0.25.2", 2988 + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", 2989 + "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", 2990 + "cpu": [ 2991 + "x64" 2992 + ], 2993 + "dev": true, 2994 + "license": "MIT", 2995 + "optional": true, 2996 + "os": [ 2997 + "sunos" 2998 + ], 2999 + "engines": { 3000 + "node": ">=18" 3001 + } 3002 + }, 3003 + "node_modules/wrangler/node_modules/@esbuild/win32-arm64": { 3004 + "version": "0.25.2", 3005 + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", 3006 + "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", 3007 + "cpu": [ 3008 + "arm64" 3009 + ], 3010 + "dev": true, 3011 + "license": "MIT", 3012 + "optional": true, 3013 + "os": [ 3014 + "win32" 3015 + ], 3016 + "engines": { 3017 + "node": ">=18" 3018 + } 3019 + }, 3020 + "node_modules/wrangler/node_modules/@esbuild/win32-ia32": { 3021 + "version": "0.25.2", 3022 + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", 3023 + "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", 3024 + "cpu": [ 3025 + "ia32" 3026 + ], 3027 + "dev": true, 3028 + "license": "MIT", 3029 + "optional": true, 3030 + "os": [ 3031 + "win32" 3032 + ], 3033 + "engines": { 3034 + "node": ">=18" 3035 + } 3036 + }, 3037 + "node_modules/wrangler/node_modules/@esbuild/win32-x64": { 3038 + "version": "0.25.2", 3039 + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", 3040 + "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", 3041 + "cpu": [ 3042 + "x64" 3043 + ], 3044 + "dev": true, 3045 + "license": "MIT", 3046 + "optional": true, 3047 + "os": [ 3048 + "win32" 3049 + ], 3050 + "engines": { 3051 + "node": ">=18" 3052 + } 3053 + }, 3054 + "node_modules/wrangler/node_modules/esbuild": { 3055 + "version": "0.25.2", 3056 + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", 3057 + "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", 3058 + "dev": true, 3059 + "hasInstallScript": true, 3060 + "license": "MIT", 3061 + "bin": { 3062 + "esbuild": "bin/esbuild" 3063 + }, 3064 + "engines": { 3065 + "node": ">=18" 3066 + }, 3067 + "optionalDependencies": { 3068 + "@esbuild/aix-ppc64": "0.25.2", 3069 + "@esbuild/android-arm": "0.25.2", 3070 + "@esbuild/android-arm64": "0.25.2", 3071 + "@esbuild/android-x64": "0.25.2", 3072 + "@esbuild/darwin-arm64": "0.25.2", 3073 + "@esbuild/darwin-x64": "0.25.2", 3074 + "@esbuild/freebsd-arm64": "0.25.2", 3075 + "@esbuild/freebsd-x64": "0.25.2", 3076 + "@esbuild/linux-arm": "0.25.2", 3077 + "@esbuild/linux-arm64": "0.25.2", 3078 + "@esbuild/linux-ia32": "0.25.2", 3079 + "@esbuild/linux-loong64": "0.25.2", 3080 + "@esbuild/linux-mips64el": "0.25.2", 3081 + "@esbuild/linux-ppc64": "0.25.2", 3082 + "@esbuild/linux-riscv64": "0.25.2", 3083 + "@esbuild/linux-s390x": "0.25.2", 3084 + "@esbuild/linux-x64": "0.25.2", 3085 + "@esbuild/netbsd-arm64": "0.25.2", 3086 + "@esbuild/netbsd-x64": "0.25.2", 3087 + "@esbuild/openbsd-arm64": "0.25.2", 3088 + "@esbuild/openbsd-x64": "0.25.2", 3089 + "@esbuild/sunos-x64": "0.25.2", 3090 + "@esbuild/win32-arm64": "0.25.2", 3091 + "@esbuild/win32-ia32": "0.25.2", 3092 + "@esbuild/win32-x64": "0.25.2" 3093 + } 3094 + }, 3095 + "node_modules/ws": { 3096 + "version": "8.18.0", 3097 + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", 3098 + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", 3099 + "dev": true, 3100 + "license": "MIT", 3101 + "engines": { 3102 + "node": ">=10.0.0" 3103 + }, 3104 + "peerDependencies": { 3105 + "bufferutil": "^4.0.1", 3106 + "utf-8-validate": ">=5.0.2" 3107 + }, 3108 + "peerDependenciesMeta": { 3109 + "bufferutil": { 3110 + "optional": true 3111 + }, 3112 + "utf-8-validate": { 3113 + "optional": true 3114 + } 3115 + } 3116 + }, 3117 + "node_modules/youch": { 3118 + "version": "3.3.4", 3119 + "resolved": "https://registry.npmjs.org/youch/-/youch-3.3.4.tgz", 3120 + "integrity": "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==", 3121 + "dev": true, 3122 + "license": "MIT", 3123 + "dependencies": { 3124 + "cookie": "^0.7.1", 3125 + "mustache": "^4.2.0", 3126 + "stacktracey": "^2.1.8" 3127 + } 3128 + }, 3129 + "node_modules/zod": { 3130 + "version": "3.24.3", 3131 + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", 3132 + "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", 3133 + "license": "MIT", 3134 + "funding": { 3135 + "url": "https://github.com/sponsors/colinhacks" 3136 + } 3137 + } 3138 + } 3139 }
+17 -14
avatar/package.json
··· 1 { 2 - "name": "avatar", 3 - "version": "0.0.0", 4 - "private": true, 5 - "scripts": { 6 - "deploy": "wrangler deploy", 7 - "dev": "wrangler dev", 8 - "start": "wrangler dev", 9 - "test": "vitest" 10 - }, 11 - "devDependencies": { 12 - "@cloudflare/vitest-pool-workers": "^0.8.19", 13 - "vitest": "~3.0.7", 14 - "wrangler": "^4.14.1" 15 - } 16 }
··· 1 { 2 + "name": "avatar", 3 + "version": "0.0.0", 4 + "private": true, 5 + "scripts": { 6 + "deploy": "wrangler deploy", 7 + "dev": "wrangler dev", 8 + "start": "wrangler dev", 9 + "test": "vitest" 10 + }, 11 + "dependencies": { 12 + "@atproto/identity": "^0.4.1" 13 + }, 14 + "devDependencies": { 15 + "@cloudflare/vitest-pool-workers": "^0.8.19", 16 + "vitest": "~3.0.7", 17 + "wrangler": "^4.14.1" 18 + } 19 }
+121 -9
avatar/src/index.js
··· 1 export default { 2 async fetch(request, env) { 3 // Helper function to generate a color from a string ··· 14 return color; 15 }; 16 17 const url = new URL(request.url); 18 const { pathname, searchParams } = url; 19 20 if (!pathname || pathname === "/") { 21 - return new Response(`This is Tangled's avatar service. It fetches your pretty avatar from Bluesky and caches it on Cloudflare. 22 - You can't use this directly unfortunately since all requests are signed and may only originate from the appview.`); 23 } 24 25 const size = searchParams.get("size"); ··· 68 } 69 70 try { 71 - const profileResponse = await fetch( 72 - `https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${actor}`, 73 - ); 74 - const profile = await profileResponse.json(); 75 - const avatar = profile.avatar; 76 77 - let avatarUrl = profile.avatar; 78 79 if (!avatarUrl) { 80 // Generate a random color based on the actor string 81 const bgColor = stringToColor(actor); 82 const size = resizeToTiny ? 32 : 128; 83 const svg = `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg"><rect width="${size}" height="${size}" fill="${bgColor}"/></svg>`; ··· 93 return response; 94 } 95 96 - // Resize if requested 97 let avatarResponse; 98 if (resizeToTiny) { 99 avatarResponse = await fetch(avatarUrl, {
··· 1 + import { IdResolver } from "@atproto/identity"; 2 + 3 export default { 4 async fetch(request, env) { 5 // Helper function to generate a color from a string ··· 16 return color; 17 }; 18 19 + // Helper function to fetch Tangled profile from PDS 20 + const getTangledAvatarFromPDS = async (actor, resolver) => { 21 + try { 22 + // Resolve the identity 23 + const identity = await resolver.resolve(actor); 24 + if (!identity) { 25 + console.log({ 26 + level: "debug", 27 + message: "failed to resolve identity", 28 + actor: actor, 29 + }); 30 + return null; 31 + } 32 + 33 + const did = identity.did; 34 + const pdsEndpoint = identity.pdsUrl; 35 + 36 + if (!pdsEndpoint) { 37 + console.log({ 38 + level: "debug", 39 + message: "no PDS endpoint found", 40 + actor: actor, 41 + did: did, 42 + }); 43 + return null; 44 + } 45 + 46 + console.log({ 47 + level: "debug", 48 + message: "fetching Tangled profile from PDS", 49 + actor: actor, 50 + did: did, 51 + pdsEndpoint: pdsEndpoint, 52 + }); 53 + 54 + // Fetch the Tangled profile record from PDS 55 + const profileResponse = await fetch( 56 + `${pdsEndpoint}/xrpc/com.atproto.repo.getRecord?repo=${did}&collection=org.tangled.actor.profile&rkey=self`, 57 + ); 58 + 59 + if (!profileResponse.ok) { 60 + console.log({ 61 + level: "debug", 62 + message: "no Tangled profile found on PDS", 63 + actor: actor, 64 + status: profileResponse.status, 65 + }); 66 + return null; 67 + } 68 + 69 + const profileData = await profileResponse.json(); 70 + const avatarCID = profileData?.value?.avatar; 71 + 72 + if (!avatarCID) { 73 + console.log({ 74 + level: "debug", 75 + message: "Tangled profile has no avatar CID", 76 + actor: actor, 77 + }); 78 + return null; 79 + } 80 + 81 + // Construct blob URL 82 + const blobUrl = `${pdsEndpoint}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${avatarCID}`; 83 + 84 + console.log({ 85 + level: "debug", 86 + message: "found Tangled avatar", 87 + actor: actor, 88 + avatarCID: avatarCID, 89 + }); 90 + 91 + return blobUrl; 92 + } catch (e) { 93 + console.log({ 94 + level: "warn", 95 + message: "error fetching Tangled avatar from PDS", 96 + actor: actor, 97 + error: e.message, 98 + }); 99 + return null; 100 + } 101 + }; 102 + 103 const url = new URL(request.url); 104 const { pathname, searchParams } = url; 105 106 if (!pathname || pathname === "/") { 107 + return new Response( 108 + `This is Tangled's avatar service. It fetches your pretty avatar from your PDS, Bluesky, or generates a placeholder. 109 + You can't use this directly unfortunately since all requests are signed and may only originate from the appview.`, 110 + ); 111 } 112 113 const size = searchParams.get("size"); ··· 156 } 157 158 try { 159 + let avatarUrl = null; 160 + 161 + // Create identity resolver 162 + const resolver = new IdResolver(); 163 + 164 + // Try to get Tangled avatar from user's PDS first 165 + avatarUrl = await getTangledAvatarFromPDS(actor, resolver); 166 167 + // If no Tangled avatar, fall back to Bluesky 168 + if (!avatarUrl) { 169 + console.log({ 170 + level: "debug", 171 + message: "no Tangled avatar, falling back to Bluesky", 172 + actor: actor, 173 + }); 174 + 175 + const profileResponse = await fetch( 176 + `https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=${actor}`, 177 + ); 178 + 179 + if (profileResponse.ok) { 180 + const profile = await profileResponse.json(); 181 + avatarUrl = profile.avatar; 182 + } 183 + } 184 185 if (!avatarUrl) { 186 // Generate a random color based on the actor string 187 + console.log({ 188 + level: "debug", 189 + message: "no avatar found, generating placeholder", 190 + actor: actor, 191 + }); 192 + 193 const bgColor = stringToColor(actor); 194 const size = resizeToTiny ? 32 : 128; 195 const svg = `<svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" xmlns="http://www.w3.org/2000/svg"><rect width="${size}" height="${size}" fill="${bgColor}"/></svg>`; ··· 205 return response; 206 } 207 208 + // Fetch and optionally resize the avatar 209 let avatarResponse; 210 if (resizeToTiny) { 211 avatarResponse = await fetch(avatarUrl, {
+6 -6
cmd/knot/main.go
··· 6 "os" 7 8 "github.com/urfave/cli/v3" 9 - "tangled.org/core/knot2/guard" 10 - "tangled.org/core/knot2/hook" 11 - "tangled.org/core/knot2/keys" 12 - "tangled.org/core/knot2/server" 13 tlog "tangled.org/core/log" 14 ) 15 ··· 19 Usage: "knot administration and operation tool", 20 Commands: []*cli.Command{ 21 guard.Command(), 22 - server.Command(), 23 - keys.Command(), 24 hook.Command(), 25 }, 26 }
··· 6 "os" 7 8 "github.com/urfave/cli/v3" 9 + "tangled.org/core/guard" 10 + "tangled.org/core/hook" 11 + "tangled.org/core/keyfetch" 12 + "tangled.org/core/knotserver" 13 tlog "tangled.org/core/log" 14 ) 15 ··· 19 Usage: "knot administration and operation tool", 20 Commands: []*cli.Command{ 21 guard.Command(), 22 + knotserver.Command(), 23 + keyfetch.Command(), 24 hook.Command(), 25 }, 26 }
-1
go.mod
··· 18 github.com/cloudflare/cloudflare-go v0.115.0 19 github.com/cyphar/filepath-securejoin v0.4.1 20 github.com/dgraph-io/ristretto v0.2.0 21 - github.com/did-method-plc/go-didplc v0.0.0-20250716171643-635da8b4e038 22 github.com/docker/docker v28.2.2+incompatible 23 github.com/dustin/go-humanize v1.0.1 24 github.com/gliderlabs/ssh v0.3.8
··· 18 github.com/cloudflare/cloudflare-go v0.115.0 19 github.com/cyphar/filepath-securejoin v0.4.1 20 github.com/dgraph-io/ristretto v0.2.0 21 github.com/docker/docker v28.2.2+incompatible 22 github.com/dustin/go-humanize v1.0.1 23 github.com/gliderlabs/ssh v0.3.8
-2
go.sum
··· 131 github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 132 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 133 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 134 - github.com/did-method-plc/go-didplc v0.0.0-20250716171643-635da8b4e038 h1:AGh+Vn9fXhf9eo8erG1CK4+LACduPo64P1OICQLDv88= 135 - github.com/did-method-plc/go-didplc v0.0.0-20250716171643-635da8b4e038/go.mod h1:ddIXqTTSXWtj5kMsHAPj8SvbIx2GZdAkBFgFa6e6+CM= 136 github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= 137 github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= 138 github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
··· 131 github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= 132 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= 133 github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= 134 github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= 135 github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= 136 github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
-101
knot2/config/config.go
··· 1 - package config 2 - 3 - import ( 4 - "context" 5 - "fmt" 6 - "net" 7 - "os" 8 - "path" 9 - 10 - "github.com/bluesky-social/indigo/atproto/syntax" 11 - "github.com/sethvargo/go-envconfig" 12 - "gopkg.in/yaml.v3" 13 - ) 14 - 15 - type Config struct { 16 - Dev bool `yaml:"dev"` 17 - HostName string `yaml:"hostname"` 18 - OwnerDid syntax.DID `yaml:"owner_did"` 19 - ListenHost string `yaml:"listen_host"` 20 - ListenPort string `yaml:"listen_port"` 21 - DataDir string `yaml:"data_dir"` 22 - RepoDir string `yaml:"repo_dir"` 23 - PlcUrl string `yaml:"plc_url"` 24 - JetstreamEndpoint string `yaml:"jetstream_endpoint"` 25 - AppviewEndpoint string `yaml:"appview_endpoint"` 26 - GitUserName string `yaml:"git_user_name"` 27 - GitUserEmail string `yaml:"git_user_email"` 28 - OAuth OAuthConfig 29 - } 30 - 31 - type OAuthConfig struct { 32 - CookieSecret string `env:"KNOT2_COOKIE_SECRET, default=00000000000000000000000000000000"` 33 - ClientSecret string `env:"KNOT2_OAUTH_CLIENT_SECRET"` 34 - ClientKid string `env:"KNOT2_OAUTH_CLIENT_KID"` 35 - } 36 - 37 - func (c *Config) Uri() string { 38 - // TODO: make port configurable 39 - if c.Dev { 40 - return "http://127.0.0.1:6444" 41 - } 42 - return "https://" + c.HostName 43 - } 44 - 45 - func (c *Config) ListenAddr() string { 46 - return net.JoinHostPort(c.ListenHost, c.ListenPort) 47 - } 48 - 49 - func (c *Config) DbPath() string { 50 - return path.Join(c.DataDir, "knot.db") 51 - } 52 - 53 - func (c *Config) GitMotdFilePath() string { 54 - return path.Join(c.DataDir, "motd") 55 - } 56 - 57 - func (c *Config) Validate() error { 58 - if c.HostName == "" { 59 - return fmt.Errorf("knot hostname cannot be empty") 60 - } 61 - if c.OwnerDid == "" { 62 - return fmt.Errorf("knot owner did cannot be empty") 63 - } 64 - return nil 65 - } 66 - 67 - func Load(ctx context.Context, path string) (Config, error) { 68 - // NOTE: yaml.v3 package doesn't support "default" struct tag 69 - cfg := Config{ 70 - Dev: true, 71 - ListenHost: "0.0.0.0", 72 - ListenPort: "5555", 73 - DataDir: "/home/git", 74 - RepoDir: "/home/git", 75 - PlcUrl: "https://plc.directory", 76 - JetstreamEndpoint: "wss://jetstream1.us-west.bsky.network/subscribe", 77 - AppviewEndpoint: "https://tangled.org", 78 - GitUserName: "Tangled", 79 - GitUserEmail: "noreply@tangled.org", 80 - } 81 - // load config from env vars 82 - err := envconfig.Process(ctx, &cfg.OAuth) 83 - if err != nil { 84 - return cfg, err 85 - } 86 - 87 - // load config from toml config file 88 - bytes, err := os.ReadFile(path) 89 - if err != nil { 90 - return cfg, err 91 - } 92 - if err := yaml.Unmarshal(bytes, &cfg); err != nil { 93 - return cfg, err 94 - } 95 - 96 - // validate the config 97 - if err = cfg.Validate(); err != nil { 98 - return cfg, err 99 - } 100 - return cfg, nil 101 - }
···
-52
knot2/db/db.go
··· 1 - package db 2 - 3 - import ( 4 - "database/sql" 5 - "strings" 6 - 7 - _ "github.com/mattn/go-sqlite3" 8 - ) 9 - 10 - func New(dbPath string) (*sql.DB, error) { 11 - // https://github.com/mattn/go-sqlite3#connection-string 12 - opts := []string{ 13 - "_foreign_keys=1", 14 - "_journal_mode=WAL", 15 - "_synchronous=NORMAL", 16 - "_auto_vacuum=incremental", 17 - } 18 - 19 - return sql.Open("sqlite3", dbPath+"?"+strings.Join(opts, "&")) 20 - } 21 - 22 - func Init(d *sql.DB) error { 23 - _, err := d.Exec(` 24 - create table if not exists _jetstream ( 25 - id integer primary key autoincrement, 26 - last_time_us integer not null 27 - ); 28 - 29 - create table if not exists events ( 30 - rkey text not null, 31 - nsid text not null, 32 - event text not null, -- json 33 - created integer not null -- unix nanos 34 - ); 35 - 36 - create table if not exists users ( 37 - id integer primary key autoincrement, 38 - did text not null unique, 39 - created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')) 40 - ); 41 - 42 - create table if not exists public_keys ( 43 - id integer primary key autoincrement, 44 - did text not null, 45 - key text not null, 46 - created text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')), 47 - unique(did, key) 48 - ); 49 - `) 50 - 51 - return err 52 - }
···
-10
knot2/db/pubkeys.go
··· 1 - package db 2 - 3 - import ( 4 - "database/sql" 5 - ) 6 - 7 - // GetPubkeyDidListMap returns a PubKey->[]DID map 8 - func GetPubkeyDidListMap(d *sql.DB) (map[string][]string, error) { 9 - return nil, nil 10 - }
···
-12
knot2/db/users.go
··· 1 - package db 2 - 3 - import ( 4 - "database/sql" 5 - 6 - "github.com/bluesky-social/indigo/atproto/syntax" 7 - ) 8 - 9 - func AddUser(tx *sql.Tx, did syntax.DID) error { 10 - _, err := tx.Exec(`insert into users (did) values (?)`, did) 11 - return err 12 - }
···
-31
knot2/guard/guard.go
··· 1 - package guard 2 - 3 - import ( 4 - "context" 5 - 6 - "github.com/urfave/cli/v3" 7 - "tangled.org/core/log" 8 - ) 9 - 10 - func Command() *cli.Command { 11 - return &cli.Command{ 12 - Name: "guard", 13 - Usage: "role-based access control for git over ssh (not for manual use)", 14 - Action: Run, 15 - Flags: []cli.Flag{ 16 - &cli.StringFlag{ 17 - Name: "user", 18 - Usage: "allowed git user", 19 - Required: true, 20 - }, 21 - }, 22 - } 23 - } 24 - 25 - func Run(ctx context.Context, cmd *cli.Command) error { 26 - l := log.FromContext(ctx) 27 - l = log.SubLogger(l, cmd.Name) 28 - ctx = log.IntoContext(ctx, l) 29 - 30 - panic("unimplemented") 31 - }
···
-27
knot2/hook/hook.go
··· 1 - package hook 2 - 3 - import ( 4 - "context" 5 - 6 - "github.com/urfave/cli/v3" 7 - "tangled.org/core/log" 8 - ) 9 - 10 - func Command() *cli.Command { 11 - return &cli.Command{ 12 - Name: "hook", 13 - Usage: "run git hooks", 14 - Action: Run, 15 - Flags: []cli.Flag{ 16 - // TODO: 17 - }, 18 - } 19 - } 20 - 21 - func Run(ctx context.Context, cmd *cli.Command) error { 22 - l := log.FromContext(ctx) 23 - l = log.SubLogger(l, cmd.Name) 24 - ctx = log.IntoContext(ctx, l) 25 - 26 - panic("unimplemented") 27 - }
···
-103
knot2/keys/keys.go
··· 1 - package keys 2 - 3 - import ( 4 - "context" 5 - "encoding/json" 6 - "fmt" 7 - "os" 8 - "strings" 9 - 10 - "github.com/urfave/cli/v3" 11 - "tangled.org/core/knot2/config" 12 - "tangled.org/core/knot2/db" 13 - "tangled.org/core/log" 14 - ) 15 - 16 - func Command() *cli.Command { 17 - return &cli.Command{ 18 - Name: "keys", 19 - Usage: "fetch public keys from the knot server", 20 - Action: Run, 21 - Flags: []cli.Flag{ 22 - &cli.StringFlag{ 23 - Name: "config", 24 - Aliases: []string{"c"}, 25 - Usage: "config path", 26 - Required: true, 27 - }, 28 - &cli.StringFlag{ 29 - Name: "output", 30 - Aliases: []string{"o"}, 31 - Usage: "output format (table, json, authorized-keys)", 32 - Value: "table", 33 - }, 34 - }, 35 - } 36 - } 37 - 38 - func Run(ctx context.Context, cmd *cli.Command) error { 39 - l := log.FromContext(ctx) 40 - l = log.SubLogger(l, cmd.Name) 41 - ctx = log.IntoContext(ctx, l) 42 - 43 - var ( 44 - output = cmd.String("output") 45 - configPath = cmd.String("config") 46 - ) 47 - 48 - cfg, err := config.Load(ctx, configPath) 49 - if err != nil { 50 - return fmt.Errorf("failed to load config: %w", err) 51 - } 52 - 53 - d, err := db.New(cfg.DbPath()) 54 - if err != nil { 55 - return fmt.Errorf("failed to load db: %w", err) 56 - } 57 - 58 - pubkeyDidListMap, err := db.GetPubkeyDidListMap(d) 59 - if err != nil { 60 - return err 61 - } 62 - 63 - switch output { 64 - case "json": 65 - prettyJSON, err := json.MarshalIndent(pubkeyDidListMap, "", " ") 66 - if err != nil { 67 - return err 68 - } 69 - if _, err := os.Stdout.Write(prettyJSON); err != nil { 70 - return err 71 - } 72 - case "table": 73 - fmt.Printf("%-40s %-40s\n", "KEY", "DID") 74 - fmt.Println(strings.Repeat("-", 80)) 75 - 76 - for key, didList := range pubkeyDidListMap { 77 - fmt.Printf("%-40s %-40s\n", key, strings.Join(didList, ",")) 78 - } 79 - case "authorized-keys": 80 - for key, didList := range pubkeyDidListMap { 81 - executablePath, err := os.Executable() 82 - if err != nil { 83 - l.Error("error getting path of executable", "error", err) 84 - return err 85 - } 86 - command := fmt.Sprintf("%s guard", executablePath) 87 - for _, did := range didList { 88 - command += fmt.Sprintf(" -user %s", did) 89 - } 90 - fmt.Printf( 91 - `command="%s",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty %s`+"\n", 92 - command, 93 - key, 94 - ) 95 - } 96 - if err != nil { 97 - l.Error("error writing to stdout", "error", err) 98 - return err 99 - } 100 - } 101 - 102 - return nil 103 - }
···
-8
knot2/models/pubkeys.go
··· 1 - package models 2 - 3 - import "tangled.org/core/api/tangled" 4 - 5 - type PublicKey struct { 6 - Did string 7 - tangled.PublicKey 8 - }
···
-18
knot2/server/handler/events.go
··· 1 - package handler 2 - 3 - import ( 4 - "net/http" 5 - 6 - "github.com/gorilla/websocket" 7 - ) 8 - 9 - var upgrader = websocket.Upgrader{ 10 - ReadBufferSize: 1024, 11 - WriteBufferSize: 1024, 12 - } 13 - 14 - func Events() http.HandlerFunc { 15 - return func(w http.ResponseWriter, r *http.Request) { 16 - panic("unimplemented") 17 - } 18 - }
···
-9
knot2/server/handler/git_receive_pack.go
··· 1 - package handler 2 - 3 - import "net/http" 4 - 5 - func GitReceivePack() http.HandlerFunc { 6 - return func(w http.ResponseWriter, r *http.Request) { 7 - panic("unimplemented") 8 - } 9 - }
···
-9
knot2/server/handler/git_upload_pack.go
··· 1 - package handler 2 - 3 - import "net/http" 4 - 5 - func GitUploadPack() http.HandlerFunc { 6 - return func(w http.ResponseWriter, r *http.Request) { 7 - panic("unimplemented") 8 - } 9 - }
···
-9
knot2/server/handler/info_refs.go
··· 1 - package handler 2 - 3 - import "net/http" 4 - 5 - func InfoRefs() http.HandlerFunc { 6 - return func(w http.ResponseWriter, r *http.Request) { 7 - panic("unimplemented") 8 - } 9 - }
···
-241
knot2/server/handler/register.go
··· 1 - package handler 2 - 3 - import ( 4 - "context" 5 - "database/sql" 6 - _ "embed" 7 - "encoding/json" 8 - "fmt" 9 - "html/template" 10 - "net/http" 11 - "strings" 12 - 13 - "github.com/bluesky-social/indigo/api/agnostic" 14 - "github.com/bluesky-social/indigo/api/atproto" 15 - "github.com/bluesky-social/indigo/atproto/auth/oauth" 16 - "github.com/bluesky-social/indigo/atproto/syntax" 17 - "github.com/did-method-plc/go-didplc" 18 - "github.com/gorilla/sessions" 19 - "tangled.org/core/knot2/config" 20 - "tangled.org/core/knot2/db" 21 - "tangled.org/core/log" 22 - ) 23 - 24 - const ( 25 - // atproto 26 - serviceId = "tangled_knot" 27 - serviceType = "TangledKnot" 28 - // cookies 29 - sessionName = "oauth-demo" 30 - sessionId = "sessionId" 31 - sessionDid = "sessionDID" 32 - ) 33 - 34 - //go:embed "templates/register.html" 35 - var tmplRegisgerText string 36 - var tmplRegister = template.Must(template.New("register.html").Parse(tmplRegisgerText)) 37 - 38 - func Register(jar *sessions.CookieStore) http.HandlerFunc { 39 - return func(w http.ResponseWriter, r *http.Request) { 40 - ctx := r.Context() 41 - l := log.FromContext(ctx).With("handler", "Register") 42 - 43 - sess, _ := jar.Get(r, sessionName) 44 - var data map[string]any 45 - 46 - if !sess.IsNew { 47 - // render Register { Handle, Web: true } 48 - did := syntax.DID(sess.Values[sessionDid].(string)) 49 - plcop := did.Method() == "plc" && r.URL.Query().Get("method") != "web" 50 - data = map[string]any{ 51 - "Did": did, 52 - "PlcOp": plcop, 53 - } 54 - } 55 - 56 - err := tmplRegister.Execute(w, data) 57 - if err != nil { 58 - l.Error("failed to render", "err", err) 59 - } 60 - } 61 - } 62 - 63 - func OauthClientMetadata(cfg *config.Config, clientApp *oauth.ClientApp) http.HandlerFunc { 64 - return func(w http.ResponseWriter, r *http.Request) { 65 - doc := clientApp.Config.ClientMetadata() 66 - var ( 67 - clientName = cfg.HostName 68 - clientUri = cfg.Uri() 69 - jwksUri = clientUri + "/oauth/jwks.json" 70 - ) 71 - doc.ClientName = &clientName 72 - doc.ClientURI = &clientUri 73 - doc.JWKSURI = &jwksUri 74 - 75 - w.Header().Set("Content-Type", "application/json") 76 - if err := json.NewEncoder(w).Encode(doc); err != nil { 77 - http.Error(w, err.Error(), http.StatusInternalServerError) 78 - return 79 - } 80 - } 81 - } 82 - 83 - func OauthJwks(clientApp *oauth.ClientApp) http.HandlerFunc { 84 - return func(w http.ResponseWriter, r *http.Request) { 85 - w.Header().Set("Content-Type", "application/json") 86 - body := clientApp.Config.PublicJWKS() 87 - if err := json.NewEncoder(w).Encode(body); err != nil { 88 - http.Error(w, err.Error(), http.StatusInternalServerError) 89 - return 90 - } 91 - } 92 - } 93 - 94 - func OauthLoginPost(clientApp *oauth.ClientApp) http.HandlerFunc { 95 - return func(w http.ResponseWriter, r *http.Request) { 96 - ctx := r.Context() 97 - l := log.FromContext(ctx).With("handler", "OauthLoginPost") 98 - 99 - handle := r.FormValue("handle") 100 - 101 - handle = strings.TrimPrefix(handle, "\u202a") 102 - handle = strings.TrimSuffix(handle, "\u202c") 103 - // `@` is harmless 104 - handle = strings.TrimPrefix(handle, "@") 105 - 106 - redirectURL, err := clientApp.StartAuthFlow(ctx, handle) 107 - if err != nil { 108 - l.Error("failed to start auth flow", "err", err) 109 - panic(err) 110 - } 111 - 112 - w.Header().Set("HX-Redirect", redirectURL) 113 - w.WriteHeader(http.StatusOK) 114 - } 115 - } 116 - 117 - func OauthCallback(oauth *oauth.ClientApp, jar *sessions.CookieStore) http.HandlerFunc { 118 - return func(w http.ResponseWriter, r *http.Request) { 119 - ctx := r.Context() 120 - l := log.FromContext(ctx).With("handler", "OauthCallback") 121 - 122 - data, err := oauth.ProcessCallback(ctx, r.URL.Query()) 123 - if err != nil { 124 - l.Error("failed to process oauth callback", "err", err) 125 - panic(err) 126 - } 127 - 128 - // store session data to cookie jar 129 - sess, _ := jar.Get(r, sessionName) 130 - sess.Values[sessionDid] = data.AccountDID.String() 131 - sess.Values[sessionId] = data.SessionID 132 - if err = sess.Save(r, w); err != nil { 133 - l.Error("failed to save session", "err", err) 134 - panic(err) 135 - } 136 - 137 - if data.AccountDID.Method() == "plc" { 138 - sess, err := oauth.ResumeSession(ctx, data.AccountDID, data.SessionID) 139 - if err != nil { 140 - l.Error("failed to resume atproto session", "err", err) 141 - panic(err) 142 - } 143 - client := sess.APIClient() 144 - err = atproto.IdentityRequestPlcOperationSignature(ctx, client) 145 - if err != nil { 146 - l.Error("failed to request plc operation signature", "err", err) 147 - panic(err) 148 - } 149 - } 150 - 151 - http.Redirect(w, r, "/register", http.StatusSeeOther) 152 - } 153 - } 154 - 155 - func RegisterPost(cfg *config.Config, d *sql.DB, clientApp *oauth.ClientApp, jar *sessions.CookieStore) http.HandlerFunc { 156 - plcop := func(ctx context.Context, did syntax.DID, sessId, token string) error { 157 - sess, err := clientApp.ResumeSession(ctx, did, sessId) 158 - if err != nil { 159 - return fmt.Errorf("failed to resume atproto session: %w", err) 160 - } 161 - client := sess.APIClient() 162 - 163 - identity, err := clientApp.Dir.LookupDID(ctx, did) 164 - services := make(map[string]didplc.OpService) 165 - for id, service := range identity.Services { 166 - services[id] = didplc.OpService{ 167 - Type: service.Type, 168 - Endpoint: service.URL, 169 - } 170 - } 171 - services[serviceId] = didplc.OpService{ 172 - Type: serviceType, 173 - Endpoint: cfg.Uri(), 174 - } 175 - 176 - rawServices, err := json.Marshal(services) 177 - if err != nil { 178 - return fmt.Errorf("failed to marshal services map: %w", err) 179 - } 180 - raw := json.RawMessage(rawServices) 181 - 182 - signed, err := agnostic.IdentitySignPlcOperation(ctx, client, &agnostic.IdentitySignPlcOperation_Input{ 183 - Services: &raw, 184 - Token: &token, 185 - }) 186 - if err != nil { 187 - return fmt.Errorf("failed to sign plc operatino: %w", err) 188 - } 189 - 190 - err = agnostic.IdentitySubmitPlcOperation(ctx, client, &agnostic.IdentitySubmitPlcOperation_Input{ 191 - Operation: signed.Operation, 192 - }) 193 - if err != nil { 194 - return fmt.Errorf("failed to submit plc operatino: %w", err) 195 - } 196 - 197 - return nil 198 - } 199 - return func(w http.ResponseWriter, r *http.Request) { 200 - ctx := r.Context() 201 - l := log.FromContext(ctx).With("handler", "RegisterPost") 202 - 203 - sess, _ := jar.Get(r, sessionName) 204 - 205 - var ( 206 - did = syntax.DID(sess.Values[sessionDid].(string)) 207 - sessId = sess.Values[sessionId].(string) 208 - token = r.FormValue("token") 209 - doPlcOp = r.FormValue("plcop") == "on" 210 - ) 211 - 212 - tx, err := d.BeginTx(ctx, nil) 213 - if err != nil { 214 - l.Error("failed to begin db tx", "err", err) 215 - panic(err) 216 - } 217 - defer tx.Rollback() 218 - 219 - if err := db.AddUser(tx, did); err != nil { 220 - l.Error("failed to add user", "err", err) 221 - http.Error(w, err.Error(), http.StatusInternalServerError) 222 - return 223 - } 224 - 225 - if doPlcOp { 226 - l.Debug("performing plc op", "did", did, "token", token) 227 - if err := plcop(ctx, did, sessId, token); err != nil { 228 - l.Error("failed to perform plc op", "err", err) 229 - http.Error(w, err.Error(), http.StatusInternalServerError) 230 - } 231 - } else { 232 - // TODO: check if did doc already include the knot service 233 - tx.Rollback() 234 - panic("unimplemented") 235 - } 236 - if err := tx.Commit(); err != nil { 237 - l.Error("failed to commit tx", "err", err) 238 - http.Error(w, err.Error(), http.StatusInternalServerError) 239 - } 240 - } 241 - }
···
-41
knot2/server/handler/templates/register.html
··· 1 - <!doctype html> 2 - <html lang="en" class="dark:bg-gray-900"> 3 - <head> 4 - <meta charset="UTF-8" /> 5 - <meta name="viewport" content="width=device-width, initial-scale=1.0"/> 6 - <meta name="description" content="knot server"/> 7 - <title>Register to Knot</title> 8 - <script src="https://cdn.jsdelivr.net/npm/htmx.org@2.0.8/dist/htmx.min.js" integrity="sha384-/TgkGk7p307TH7EXJDuUlgG3Ce1UVolAOFopFekQkkXihi5u/6OCvVKyz1W+idaz" crossorigin="anonymous"></script> 9 - </head> 10 - <body> 11 - {{ if (not .) }} 12 - {{/* step 1. login */}} 13 - <form hx-post="/oauth/login" hx-swap="none"> 14 - <input type="text" name="handle"> 15 - <button type="submit">Login</button> 16 - </form> 17 - {{ else }} 18 - {{/* step 2. register user with plc operation */}} 19 - <form hx-post="/register" hx-swap="none"> 20 - <input type="hidden" name="plcop" value="{{ if .PlcOp }}on{{ end }}"> 21 - 22 - <div> 23 - <label for="handle">User Handle:</label> 24 - <input type="text" name="handle" value="{{ .Did }}" readonly> 25 - </div> 26 - 27 - {{ if (not .Web) }} 28 - <h2>Please enter your PLC Token you received in an email</h2> 29 - <div> 30 - <label for="token">PLC Token:</label> 31 - <input type="text" name="token" required placeholder="XXXXX-XXXXX"> 32 - </div> 33 - 34 - <button type="submit">add Knot to identity</button> 35 - {{ else }} 36 - <button type="submit">register to Knot</button> 37 - {{ end }} 38 - </form> 39 - {{ end }} 40 - </body> 41 - </html>
···
-87
knot2/server/handler/xrpc_git_keep_commit.go
··· 1 - package handler 2 - 3 - import ( 4 - "encoding/json" 5 - "fmt" 6 - "net/http" 7 - "os/exec" 8 - "path" 9 - 10 - "github.com/bluesky-social/indigo/atproto/syntax" 11 - "github.com/go-git/go-git/v5" 12 - "github.com/go-git/go-git/v5/plumbing" 13 - "tangled.org/core/api/tangled" 14 - "tangled.org/core/knot2/config" 15 - "tangled.org/core/log" 16 - xrpcerr "tangled.org/core/xrpc/errors" 17 - ) 18 - 19 - func XrpcGitKeepCommit(cfg *config.Config) http.HandlerFunc { 20 - return func(w http.ResponseWriter, r *http.Request) { 21 - ctx := r.Context() 22 - l := log.FromContext(ctx).With("handler", "XrpcGitKeepCommit") 23 - 24 - // TODO: get session did 25 - actorDid := syntax.DID("") 26 - 27 - var input tangled.GitKeepCommit_Input 28 - if err := json.NewDecoder(r.Body).Decode(&input); err != nil { 29 - l.Error("failed to decode body", "err", err) 30 - panic("unimplemented") 31 - } 32 - 33 - repoAt, err := syntax.ParseATURI(input.Repo) 34 - if err != nil { 35 - l.Error("failed to decode body", "err", err) 36 - panic("unimplemented") 37 - } 38 - repoPath := repoPathFromAtUri(cfg, repoAt) 39 - 40 - // ensure repo exist (if not, clone it) 41 - repo, err := git.PlainOpen(repoPath) 42 - if err != nil { 43 - // TODO: clone the ref from source repo if repo doesn't exist in this knot yet 44 - l.Info("repo missing in knot", "err", err) 45 - panic("unimplemented") 46 - } 47 - 48 - commitId, err := repo.ResolveRevision(plumbing.Revision(input.Ref)) 49 - if err != nil { 50 - l.Error("failed to resolve revision", "ref", input.Ref, "err", err) 51 - panic("unimplemented") 52 - } 53 - 54 - // set keep-ref for given commit 55 - refspec := fmt.Sprintf("refs/knot/%s/keep/%s", actorDid, commitId) 56 - updateRefCmd := exec.Command("git", "-C", repoPath, "update-ref", refspec, commitId.String()) 57 - if err := updateRefCmd.Run(); err != nil { 58 - writeError(w, xrpcerr.GenericError(err), http.StatusBadRequest) 59 - return 60 - } 61 - 62 - output := tangled.GitKeepCommit_Output{ 63 - CommitId: commitId.String(), 64 - } 65 - 66 - w.WriteHeader(http.StatusOK) 67 - writeJson(w, output) 68 - } 69 - } 70 - 71 - func repoPathFromAtUri(cfg *config.Config, repoAt syntax.ATURI) string { 72 - return path.Join(cfg.RepoDir, repoAt.Authority().String(), repoAt.RecordKey().String()) 73 - } 74 - 75 - func writeError(w http.ResponseWriter, e xrpcerr.XrpcError, status int) { 76 - w.Header().Set("Content-Type", "application/json") 77 - w.WriteHeader(status) 78 - json.NewEncoder(w).Encode(e) 79 - } 80 - 81 - func writeJson(w http.ResponseWriter, response any) { 82 - w.Header().Set("Content-Type", "application/json") 83 - if err := json.NewEncoder(w).Encode(response); err != nil { 84 - writeError(w, xrpcerr.GenericError(err), http.StatusInternalServerError) 85 - return 86 - } 87 - }
···
-21
knot2/server/middleware/cors.go
··· 1 - package middleware 2 - 3 - import "net/http" 4 - 5 - func CORS(next http.Handler) http.Handler { 6 - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 7 - // Set CORS headers 8 - w.Header().Set("Access-Control-Allow-Origin", "*") 9 - w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS") 10 - w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization") 11 - w.Header().Set("Access-Control-Max-Age", "86400") 12 - 13 - // Handle preflight requests 14 - if r.Method == "OPTIONS" { 15 - w.WriteHeader(http.StatusOK) 16 - return 17 - } 18 - 19 - next.ServeHTTP(w, r) 20 - }) 21 - }
···
-40
knot2/server/middleware/requestlogger.go
··· 1 - package middleware 2 - 3 - import ( 4 - "log/slog" 5 - "net/http" 6 - "time" 7 - 8 - "tangled.org/core/log" 9 - ) 10 - 11 - func RequestLogger(next http.Handler) http.Handler { 12 - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 13 - ctx := r.Context() 14 - l := log.FromContext(ctx) 15 - 16 - start := time.Now() 17 - 18 - next.ServeHTTP(w, r) 19 - 20 - // Build query params as slog.Attrs for the group 21 - queryParams := r.URL.Query() 22 - queryAttrs := make([]any, 0, len(queryParams)) 23 - for key, values := range queryParams { 24 - if len(values) == 1 { 25 - queryAttrs = append(queryAttrs, slog.String(key, values[0])) 26 - } else { 27 - queryAttrs = append(queryAttrs, slog.Any(key, values)) 28 - } 29 - } 30 - 31 - l.LogAttrs(ctx, slog.LevelInfo, "", 32 - slog.Group("request", 33 - slog.String("method", r.Method), 34 - slog.String("path", r.URL.Path), 35 - slog.Group("query", queryAttrs...), 36 - slog.Duration("duration", time.Since(start)), 37 - ), 38 - ) 39 - }) 40 - }
···
-40
knot2/server/oauth.go
··· 1 - package server 2 - 3 - import ( 4 - "net/http" 5 - 6 - atcrypto "github.com/bluesky-social/indigo/atproto/crypto" 7 - "github.com/bluesky-social/indigo/atproto/auth/oauth" 8 - "tangled.org/core/idresolver" 9 - "tangled.org/core/knot2/config" 10 - ) 11 - 12 - func newAtClientApp(cfg *config.Config) *oauth.ClientApp { 13 - idResolver := idresolver.DefaultResolver(cfg.PlcUrl) 14 - scopes := []string{"atproto", "identity:*"} 15 - var oauthConfig oauth.ClientConfig 16 - if cfg.Dev { 17 - oauthConfig = oauth.NewLocalhostConfig( 18 - cfg.Uri()+"/oauth/callback", 19 - scopes, 20 - ) 21 - } else { 22 - oauthConfig = oauth.NewPublicConfig( 23 - cfg.Uri()+"/oauth/client-metadata.json", 24 - cfg.Uri()+"/oauth/callback", 25 - scopes, 26 - ) 27 - } 28 - priv, err := atcrypto.ParsePrivateMultibase(cfg.OAuth.ClientSecret) 29 - if err != nil { 30 - panic(err) 31 - } 32 - if err := oauthConfig.SetClientSecret(priv, cfg.OAuth.ClientKid); err != nil { 33 - panic(err) 34 - } 35 - // we can just use in-memory auth store 36 - clientApp := oauth.NewClientApp(&oauthConfig, oauth.NewMemStore()) 37 - clientApp.Dir = idResolver.Directory() 38 - clientApp.Resolver.Client.Transport = http.DefaultTransport 39 - return clientApp 40 - }
···
-52
knot2/server/routes.go
··· 1 - package server 2 - 3 - import ( 4 - "database/sql" 5 - "net/http" 6 - 7 - "github.com/bluesky-social/indigo/atproto/auth/oauth" 8 - "github.com/go-chi/chi/v5" 9 - "github.com/gorilla/sessions" 10 - "tangled.org/core/api/tangled" 11 - "tangled.org/core/knot2/config" 12 - "tangled.org/core/knot2/server/handler" 13 - "tangled.org/core/knot2/server/middleware" 14 - ) 15 - 16 - func Routes( 17 - cfg *config.Config, 18 - d *sql.DB, 19 - clientApp *oauth.ClientApp, 20 - ) http.Handler { 21 - r := chi.NewRouter() 22 - 23 - r.Use(middleware.CORS) 24 - r.Use(middleware.RequestLogger) 25 - 26 - r.Get("/", func(w http.ResponseWriter, r *http.Request) { 27 - w.Write([]byte("This is a knot server. More info at https://tangled.sh")) 28 - }) 29 - 30 - jar := sessions.NewCookieStore([]byte(cfg.OAuth.CookieSecret)) 31 - 32 - r.Get("/register", handler.Register(jar)) 33 - r.Post("/register", handler.RegisterPost(cfg, d, clientApp, jar)) 34 - r.Post("/oauth/login", handler.OauthLoginPost(clientApp)) 35 - r.Get("/oauth/client-metadata.json", handler.OauthClientMetadata(cfg, clientApp)) 36 - r.Get("/oauth/jwks.json", handler.OauthJwks(clientApp)) 37 - r.Get("/oauth/callback", handler.OauthCallback(clientApp, jar)) 38 - 39 - r.Route("/{did}/{name}", func(r chi.Router) { 40 - r.Get("/info/refs", handler.InfoRefs()) 41 - r.Post("/git-upload-pack", handler.GitUploadPack()) 42 - r.Post("/git-receive-pack", handler.GitReceivePack()) 43 - }) 44 - 45 - r.Get("/events", handler.Events()) 46 - 47 - r.Route("/xrpc", func(r chi.Router) { 48 - r.Post("/"+tangled.GitKeepCommitNSID, handler.XrpcGitKeepCommit(cfg)) 49 - }) 50 - 51 - return r 52 - }
···
-65
knot2/server/server.go
··· 1 - package server 2 - 3 - import ( 4 - "context" 5 - "fmt" 6 - "net/http" 7 - 8 - "github.com/urfave/cli/v3" 9 - "tangled.org/core/knot2/config" 10 - "tangled.org/core/knot2/db" 11 - "tangled.org/core/log" 12 - ) 13 - 14 - func Command() *cli.Command { 15 - return &cli.Command{ 16 - Name: "server", 17 - Usage: "run a knot server", 18 - Action: Run, 19 - Flags: []cli.Flag{ 20 - &cli.StringFlag{ 21 - Name: "config", 22 - Aliases: []string{"c"}, 23 - Usage: "config path", 24 - Required: true, 25 - }, 26 - }, 27 - } 28 - } 29 - 30 - func Run(ctx context.Context, cmd *cli.Command) error { 31 - l := log.FromContext(ctx) 32 - l = log.SubLogger(l, cmd.Name) 33 - ctx = log.IntoContext(ctx, l) 34 - 35 - configPath := cmd.String("config") 36 - 37 - cfg, err := config.Load(ctx, configPath) 38 - if err != nil { 39 - return fmt.Errorf("failed to load config: %w", err) 40 - } 41 - fmt.Println("config:", cfg) 42 - 43 - // TODO: start listening to jetstream 44 - 45 - d, err := db.New(cfg.DbPath()) 46 - if err != nil { 47 - panic(err) 48 - } 49 - err = db.Init(d) 50 - if err != nil { 51 - panic(err) 52 - } 53 - 54 - clientApp := newAtClientApp(&cfg) 55 - 56 - mux := Routes(&cfg, d, clientApp) 57 - 58 - l.Info("starting knot server", "address", cfg.ListenAddr()) 59 - err = http.ListenAndServe(cfg.ListenAddr(), mux) 60 - if err != nil { 61 - l.Error("server error", "err", err) 62 - } 63 - 64 - return nil 65 - }
···
+7 -3
lexicons/actor/profile.json
··· 8 "key": "literal:self", 9 "record": { 10 "type": "object", 11 - "required": [ 12 - "bluesky" 13 - ], 14 "properties": { 15 "description": { 16 "type": "string", 17 "description": "Free-form profile description text.",
··· 8 "key": "literal:self", 9 "record": { 10 "type": "object", 11 + "required": ["bluesky"], 12 "properties": { 13 + "avatar": { 14 + "type": "blob", 15 + "description": "Small image to be displayed next to posts from account. AKA, 'profile picture'", 16 + "accept": ["image/png", "image/jpeg"], 17 + "maxSize": 1000000 18 + }, 19 "description": { 20 "type": "string", 21 "description": "Free-form profile description text.",
-46
lexicons/git/keepCommit.json
··· 1 - { 2 - "lexicon": 1, 3 - "id": "sh.tangled.git.keepCommit", 4 - "defs": { 5 - "main": { 6 - "type": "procedure", 7 - "input": { 8 - "encoding": "application/json", 9 - "schema": { 10 - "type": "object", 11 - "required": ["repo", "ref"], 12 - "properties": { 13 - "repo": { 14 - "type": "string", 15 - "format": "at-uri", 16 - "description": "AT-URI of the repository" 17 - }, 18 - "ref": { 19 - "type": "string", 20 - "description": "ref to keep" 21 - } 22 - } 23 - } 24 - }, 25 - "output": { 26 - "encoding": "application/json", 27 - "schema": { 28 - "type": "object", 29 - "required": ["commitId"], 30 - "properties": { 31 - "commitId": { 32 - "type": "string", 33 - "description": "Keeped commit hash" 34 - } 35 - } 36 - } 37 - }, 38 - "errors": [ 39 - { 40 - "name": "InternalServerError", 41 - "description": "Failed to keep commit" 42 - } 43 - ] 44 - } 45 - } 46 - }
···
-3
nix/gomod2nix.toml
··· 171 [mod."github.com/dgryski/go-rendezvous"] 172 version = "v0.0.0-20200823014737-9f7001d12a5f" 173 hash = "sha256-n/7xo5CQqo4yLaWMSzSN1Muk/oqK6O5dgDOFWapeDUI=" 174 - [mod."github.com/did-method-plc/go-didplc"] 175 - version = "v0.0.0-20250716171643-635da8b4e038" 176 - hash = "sha256-o0uB/5tryjdB44ssALFr49PtfY3nRJnEENmE187md1w=" 177 [mod."github.com/distribution/reference"] 178 version = "v0.6.0" 179 hash = "sha256-gr4tL+qz4jKyAtl8LINcxMSanztdt+pybj1T+2ulQv4="
··· 171 [mod."github.com/dgryski/go-rendezvous"] 172 version = "v0.0.0-20200823014737-9f7001d12a5f" 173 hash = "sha256-n/7xo5CQqo4yLaWMSzSN1Muk/oqK6O5dgDOFWapeDUI=" 174 [mod."github.com/distribution/reference"] 175 version = "v0.6.0" 176 hash = "sha256-gr4tL+qz4jKyAtl8LINcxMSanztdt+pybj1T+2ulQv4="
+5 -18
nix/modules/knot.nix
··· 170 description = "Enable development mode (disables signature verification)"; 171 }; 172 }; 173 - 174 - environmentFile = mkOption { 175 - type = with types; nullOr path; 176 - default = null; 177 - example = "/etc/appview.env"; 178 - description = '' 179 - Additional environment file as defined in {manpage}`systemd.exec(5)`. 180 - 181 - Sensitive secrets such as {env}`KNOT_COOKIE_SECRET`, 182 - {env}`KNOT_OAUTH_CLIENT_SECRET`, and {env}`KNOT_OAUTH_CLIENT_KID` 183 - may be passed to the service without making them world readable in the nix store. 184 - ''; 185 - }; 186 }; 187 }; 188 ··· 218 text = '' 219 #!${pkgs.stdenv.shell} 220 ${cfg.package}/bin/knot keys \ 221 - -config ${cfg.stateDir}/config.yml \ 222 - -output authorized-keys 223 ''; 224 }; 225 ··· 284 else "false" 285 }" 286 ]; 287 - EnvironmentFile = mkIf (cfg.environmentFile != null) cfg.environmentFile; 288 - ExecStart = "${cfg.package}/bin/knot server -config ${cfg.stateDir}/config.yml"; 289 Restart = "always"; 290 - RestartSec = 5; 291 }; 292 }; 293
··· 170 description = "Enable development mode (disables signature verification)"; 171 }; 172 }; 173 }; 174 }; 175 ··· 205 text = '' 206 #!${pkgs.stdenv.shell} 207 ${cfg.package}/bin/knot keys \ 208 + -output authorized-keys \ 209 + -internal-api "http://${cfg.server.internalListenAddr}" \ 210 + -git-dir "${cfg.repo.scanPath}" \ 211 + -log-path /tmp/knotguard.log 212 ''; 213 }; 214 ··· 273 else "false" 274 }" 275 ]; 276 + ExecStart = "${cfg.package}/bin/knot server"; 277 Restart = "always"; 278 }; 279 }; 280
-1
nix/vm.nix
··· 92 jetstreamEndpoint = jetstream; 93 listenAddr = "0.0.0.0:6444"; 94 }; 95 - environmentFile = "${config.services.tangled.knot.stateDir}/.env"; 96 }; 97 services.tangled.spindle = { 98 enable = true;
··· 92 jetstreamEndpoint = jetstream; 93 listenAddr = "0.0.0.0:6444"; 94 }; 95 }; 96 services.tangled.spindle = { 97 enable = true;
+122
orm/orm.go
···
··· 1 + package orm 2 + 3 + import ( 4 + "context" 5 + "database/sql" 6 + "fmt" 7 + "log/slog" 8 + "reflect" 9 + "strings" 10 + ) 11 + 12 + type migrationFn = func(*sql.Tx) error 13 + 14 + func RunMigration(c *sql.Conn, logger *slog.Logger, name string, migrationFn migrationFn) error { 15 + logger = logger.With("migration", name) 16 + 17 + tx, err := c.BeginTx(context.Background(), nil) 18 + if err != nil { 19 + return err 20 + } 21 + defer tx.Rollback() 22 + 23 + var exists bool 24 + err = tx.QueryRow("select exists (select 1 from migrations where name = ?)", name).Scan(&exists) 25 + if err != nil { 26 + return err 27 + } 28 + 29 + if !exists { 30 + // run migration 31 + err = migrationFn(tx) 32 + if err != nil { 33 + logger.Error("failed to run migration", "err", err) 34 + return err 35 + } 36 + 37 + // mark migration as complete 38 + _, err = tx.Exec("insert into migrations (name) values (?)", name) 39 + if err != nil { 40 + logger.Error("failed to mark migration as complete", "err", err) 41 + return err 42 + } 43 + 44 + // commit the transaction 45 + if err := tx.Commit(); err != nil { 46 + return err 47 + } 48 + 49 + logger.Info("migration applied successfully") 50 + } else { 51 + logger.Warn("skipped migration, already applied") 52 + } 53 + 54 + return nil 55 + } 56 + 57 + type Filter struct { 58 + Key string 59 + arg any 60 + Cmp string 61 + } 62 + 63 + func newFilter(key, cmp string, arg any) Filter { 64 + return Filter{ 65 + Key: key, 66 + arg: arg, 67 + Cmp: cmp, 68 + } 69 + } 70 + 71 + func FilterEq(key string, arg any) Filter { return newFilter(key, "=", arg) } 72 + func FilterNotEq(key string, arg any) Filter { return newFilter(key, "<>", arg) } 73 + func FilterGte(key string, arg any) Filter { return newFilter(key, ">=", arg) } 74 + func FilterLte(key string, arg any) Filter { return newFilter(key, "<=", arg) } 75 + func FilterIs(key string, arg any) Filter { return newFilter(key, "is", arg) } 76 + func FilterIsNot(key string, arg any) Filter { return newFilter(key, "is not", arg) } 77 + func FilterIn(key string, arg any) Filter { return newFilter(key, "in", arg) } 78 + func FilterLike(key string, arg any) Filter { return newFilter(key, "like", arg) } 79 + func FilterNotLike(key string, arg any) Filter { return newFilter(key, "not like", arg) } 80 + func FilterContains(key string, arg any) Filter { 81 + return newFilter(key, "like", fmt.Sprintf("%%%v%%", arg)) 82 + } 83 + 84 + func (f Filter) Condition() string { 85 + rv := reflect.ValueOf(f.arg) 86 + kind := rv.Kind() 87 + 88 + // if we have `FilterIn(k, [1, 2, 3])`, compile it down to `k in (?, ?, ?)` 89 + if (kind == reflect.Slice && rv.Type().Elem().Kind() != reflect.Uint8) || kind == reflect.Array { 90 + if rv.Len() == 0 { 91 + // always false 92 + return "1 = 0" 93 + } 94 + 95 + placeholders := make([]string, rv.Len()) 96 + for i := range placeholders { 97 + placeholders[i] = "?" 98 + } 99 + 100 + return fmt.Sprintf("%s %s (%s)", f.Key, f.Cmp, strings.Join(placeholders, ", ")) 101 + } 102 + 103 + return fmt.Sprintf("%s %s ?", f.Key, f.Cmp) 104 + } 105 + 106 + func (f Filter) Arg() []any { 107 + rv := reflect.ValueOf(f.arg) 108 + kind := rv.Kind() 109 + if (kind == reflect.Slice && rv.Type().Elem().Kind() != reflect.Uint8) || kind == reflect.Array { 110 + if rv.Len() == 0 { 111 + return nil 112 + } 113 + 114 + out := make([]any, rv.Len()) 115 + for i := range rv.Len() { 116 + out[i] = rv.Index(i).Interface() 117 + } 118 + return out 119 + } 120 + 121 + return []any{f.arg} 122 + }