diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index ab5c9ed9..17b50690 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -21,7 +21,7 @@ jobs: env: TINYGO_VERSION: '0.39.0' steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-go@v6 with: go-version: '1.25' diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index d93f171f..a9c5ac5b 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -18,7 +18,7 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Set Version Output id: get_version @@ -37,7 +37,7 @@ jobs: needs: publish runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Setup Go uses: actions/setup-go@v6 with: @@ -77,7 +77,7 @@ jobs: env: TINYGO_VERSION: '0.39.0' steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Setup Go uses: actions/setup-go@v6 diff --git a/.github/workflows/release_dev.yaml b/.github/workflows/release_dev.yaml index 72453049..1fcbcdf0 100644 --- a/.github/workflows/release_dev.yaml +++ b/.github/workflows/release_dev.yaml @@ -12,7 +12,7 @@ jobs: contents: read packages: write steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: fetch-depth: 0 diff --git a/.github/workflows/release_request.yaml b/.github/workflows/release_request.yaml index 4e0c89f0..5715a217 100644 --- a/.github/workflows/release_request.yaml +++ b/.github/workflows/release_request.yaml @@ -15,7 +15,7 @@ jobs: pull-requests: write steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Setup Node.js uses: actions/setup-node@v6 diff --git a/.github/workflows/web.yaml b/.github/workflows/web.yaml index 1ce1fd46..8e271c48 100644 --- a/.github/workflows/web.yaml +++ b/.github/workflows/web.yaml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-node@v6 with: diff --git a/db/federation/create.go b/db/federation/create.go index 0cf686e9..5bb97d7f 100644 --- a/db/federation/create.go +++ b/db/federation/create.go @@ -682,17 +682,21 @@ func processCreateOrUpdateSummitLogActivity(activity pub.Activity, app core.App, } } - if len(photoURLs) > 0 { - photos := make([]*filesystem.File, len(photoURLs)) - for i, purl := range photoURLs { + if len(photoURLs) == 0 { + record.Set("photos", []*filesystem.File{}) + } else { + photos := []*filesystem.File{} + for _, purl := range photoURLs { photo, err := filesystem.NewFileFromURL(context.Background(), purl) if err != nil { continue } - photos[i] = photo + photos = append(photos, photo) } - record.Set("photos", photos) + if len(photos) > 0 { + record.Set("photos", photos) + } } if gpxURL != "" { diff --git a/db/go.mod b/db/go.mod index 35113081..0ac162eb 100644 --- a/db/go.mod +++ b/db/go.mod @@ -3,7 +3,7 @@ module pocketbase go 1.25.0 require ( - github.com/doyensec/safeurl v0.2.3 + github.com/doyensec/safeurl v0.2.5 github.com/extism/go-sdk v1.7.1 github.com/go-ap/jsonld v0.0.0-20250905102310-8480b0fe24d9 github.com/meilisearch/meilisearch-go v0.36.2 @@ -53,13 +53,13 @@ require ( github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 // indirect github.com/twpayne/go-polyline v1.1.1 - golang.org/x/crypto v0.51.0 // indirect + golang.org/x/crypto v0.53.0 // indirect golang.org/x/image v0.39.0 // indirect - golang.org/x/net v0.55.0 + golang.org/x/net v0.56.0 golang.org/x/oauth2 v0.36.0 // indirect - golang.org/x/sync v0.20.0 - golang.org/x/sys v0.45.0 // indirect - golang.org/x/text v0.37.0 // indirect + golang.org/x/sync v0.21.0 + golang.org/x/sys v0.46.0 // indirect + golang.org/x/text v0.38.0 modernc.org/libc v1.72.0 // indirect modernc.org/mathutil v1.7.1 // indirect modernc.org/memory v1.11.0 // indirect diff --git a/db/go.sum b/db/go.sum index e23a8ad8..3e0bd3ec 100644 --- a/db/go.sum +++ b/db/go.sum @@ -17,8 +17,8 @@ github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1 github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4= github.com/domodwyer/mailyak/v3 v3.6.2 h1:x3tGMsyFhTCaxp6ycgR0FE/bu5QiNp+hetUuCOBXMn8= github.com/domodwyer/mailyak/v3 v3.6.2/go.mod h1:lOm/u9CyCVWHeaAmHIdF4RiKVxKUT/H5XX10lIKAL6c= -github.com/doyensec/safeurl v0.2.3 h1:KJZHxTUMI17yUSy5umKmDLtzYBUxN6MkdSIyRI81DvY= -github.com/doyensec/safeurl v0.2.3/go.mod h1:3H0cgRpPYPSpgxRRn5yGD35Ns/LgGX/BVWSBbzUqXtY= +github.com/doyensec/safeurl v0.2.5 h1:kKu0JNQy0tJ8jkDyB5h6Aml9vWWniq+mpoa12EGLcOQ= +github.com/doyensec/safeurl v0.2.5/go.mod h1:3H0cgRpPYPSpgxRRn5yGD35Ns/LgGX/BVWSBbzUqXtY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= @@ -124,39 +124,39 @@ go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI= -golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= +golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= +golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.39.0 h1:skVYidAEVKgn8lZ602XO75asgXBgLj9G/FE3RbuPFww= golang.org/x/image v0.39.0/go.mod h1:sIbmppfU+xFLPIG0FoVUTvyBMmgng1/XAMhQ2ft0hpA= -golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM= -golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU= +golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4= +golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8= -golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww= +golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o= +golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec= golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= -golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= -golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= +golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM= +golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY= -golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= +golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= -golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc= +golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc= -golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38= +golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE= +golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c= -golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI= +golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8= +golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= diff --git a/db/hooks/categories.go b/db/hooks/categories.go new file mode 100644 index 00000000..f3d061d2 --- /dev/null +++ b/db/hooks/categories.go @@ -0,0 +1,125 @@ +package hooks + +import ( + "pocketbase/util" + + "github.com/pocketbase/pocketbase/apis" + "github.com/pocketbase/pocketbase/core" +) + +func ValidateCategoryHandler() func(e *core.RecordRequestEvent) error { + return func(e *core.RecordRequestEvent) error { + if err := util.ValidateCategoryRecord(e.App, e.Record); err != nil { + return apis.NewBadRequestError(err.Error(), err) + } + + return e.Next() + } +} + +func ValidateSubcategoryHandler() func(e *core.RecordRequestEvent) error { + return func(e *core.RecordRequestEvent) error { + if err := util.ValidateSubcategoryRecord(e.App, e.Record); err != nil { + return apis.NewBadRequestError(err.Error(), err) + } + + return e.Next() + } +} + +func BackfillRemoteTrailCategoryHandler() func(e *core.RecordEvent) error { + return func(e *core.RecordEvent) error { + if e.Record.Original().Id != "" && e.Record.GetString("name") == e.Record.Original().GetString("name") { + return e.Next() + } + + if err := util.BackfillRemoteTrailCategory(e.App, e.Record); err != nil { + e.App.Logger().Warn("failed to backfill remote trail categories after category save", "category", e.Record.Id, "error", err) + } + + return e.Next() + } +} + +func BackfillRemoteTrailSubcategoryHandler() func(e *core.RecordEvent) error { + return func(e *core.RecordEvent) error { + original := e.Record.Original() + if original.Id != "" && + e.Record.GetString("name") == original.GetString("name") && + e.Record.GetString("category") == original.GetString("category") { + return e.Next() + } + + if err := util.BackfillRemoteTrailSubcategory(e.App, e.Record); err != nil { + e.App.Logger().Warn("failed to backfill remote trail subcategories after subcategory save", "subcategory", e.Record.Id, "error", err) + } + + return e.Next() + } +} + +func ValidateUserCategoryPreferenceHandler() func(e *core.RecordRequestEvent) error { + return func(e *core.RecordRequestEvent) error { + requestInfo, err := e.RequestInfo() + if err != nil { + return err + } + + if err := util.ValidateUserCategoryPreferenceRequest(requestBodyHasField(requestInfo.Body, "priority")); err != nil { + return apis.NewBadRequestError(err.Error(), err) + } + + return e.Next() + } +} + +func ValidateUserSubcategoryPreferenceHandler() func(e *core.RecordRequestEvent) error { + return func(e *core.RecordRequestEvent) error { + requestInfo, err := e.RequestInfo() + if err != nil { + return err + } + + if err := util.ValidateUserSubcategoryPreferenceRequest(requestBodyHasField(requestInfo.Body, "priority")); err != nil { + return apis.NewBadRequestError(err.Error(), err) + } + + return e.Next() + } +} + +func ValidateTrailSubcategoryHandler() func(e *core.RecordRequestEvent) error { + return func(e *core.RecordRequestEvent) error { + requestInfo, err := e.RequestInfo() + if err != nil { + return err + } + + subcategoryExplicit := requestBodyHasField(requestInfo.Body, "subcategory") + if err := util.ValidateTrailSubcategoryRecord(e.App, e.Record, subcategoryExplicit); err != nil { + return apis.NewBadRequestError(err.Error(), err) + } + + return e.Next() + } +} + +func requestBodyHasField(body map[string]any, field string) bool { + _, ok := body[field] + if ok { + return true + } + + _, ok = body[field+"+"] + if ok { + return true + } + + _, ok = body["+"+field] + if ok { + return true + } + + _, ok = body[field+"-"] + return ok +} diff --git a/db/hooks/users.go b/db/hooks/users.go index 638167b1..43e0585c 100644 --- a/db/hooks/users.go +++ b/db/hooks/users.go @@ -17,6 +17,10 @@ func CreateUserHandler(client meilisearch.ServiceManager) func(e *core.RecordEve return err } + if err := util.EnsureUserCategoryPriority(e.App, e.Record.Id, ""); err != nil { + return err + } + _, err = util.ActorFromUser(e.App, e.Record) if err != nil { return err diff --git a/db/main.go b/db/main.go index 08cc9e92..fad75242 100644 --- a/db/main.go +++ b/db/main.go @@ -5,14 +5,12 @@ import ( "fmt" "log" "os" - "strings" "github.com/meilisearch/meilisearch-go" "github.com/pocketbase/dbx" "github.com/pocketbase/pocketbase" "github.com/pocketbase/pocketbase/core" "github.com/pocketbase/pocketbase/plugins/migratecmd" - "github.com/pocketbase/pocketbase/tools/filesystem" "pocketbase/commands" "pocketbase/hooks" @@ -96,6 +94,22 @@ func setupEventHandlers(app *pocketbase.PocketBase, client meilisearch.ServiceMa app.OnRecordAfterUpdateSuccess("activitypub_actors").BindFunc(hooks.UpdateActorHandler(client)) app.OnRecordAfterDeleteSuccess("activitypub_actors").BindFunc(hooks.DeleteActorHandler(client)) + app.OnRecordCreateRequest("categories").BindFunc(hooks.ValidateCategoryHandler()) + app.OnRecordUpdateRequest("categories").BindFunc(hooks.ValidateCategoryHandler()) + app.OnRecordAfterCreateSuccess("categories").BindFunc(hooks.BackfillRemoteTrailCategoryHandler()) + app.OnRecordAfterUpdateSuccess("categories").BindFunc(hooks.BackfillRemoteTrailCategoryHandler()) + app.OnRecordCreateRequest("subcategories").BindFunc(hooks.ValidateSubcategoryHandler()) + app.OnRecordUpdateRequest("subcategories").BindFunc(hooks.ValidateSubcategoryHandler()) + app.OnRecordAfterCreateSuccess("subcategories").BindFunc(hooks.BackfillRemoteTrailSubcategoryHandler()) + app.OnRecordAfterUpdateSuccess("subcategories").BindFunc(hooks.BackfillRemoteTrailSubcategoryHandler()) + + app.OnRecordCreateRequest("user_category_preferences").BindFunc(hooks.ValidateUserCategoryPreferenceHandler()) + app.OnRecordUpdateRequest("user_category_preferences").BindFunc(hooks.ValidateUserCategoryPreferenceHandler()) + app.OnRecordCreateRequest("user_subcategory_preferences").BindFunc(hooks.ValidateUserSubcategoryPreferenceHandler()) + app.OnRecordUpdateRequest("user_subcategory_preferences").BindFunc(hooks.ValidateUserSubcategoryPreferenceHandler()) + + app.OnRecordCreateRequest("trails").BindFunc(hooks.ValidateTrailSubcategoryHandler()) + app.OnRecordUpdateRequest("trails").BindFunc(hooks.ValidateTrailSubcategoryHandler()) app.OnRecordAfterCreateSuccess("trails").BindFunc(hooks.CreateTrailHandler(client)) app.OnRecordAfterUpdateSuccess("trails").BindFunc(hooks.UpdateTrailHandler(client)) app.OnRecordAfterDeleteSuccess("trails").BindFunc(hooks.DeleteTrailHandler(client)) @@ -166,6 +180,8 @@ func registerRoutes(se *core.ServeEvent, client meilisearch.ServiceManager) { se.Router.POST("/auth/token", routes.AuthToken) se.Router.POST("/user/email", routes.UserEmailChange) se.Router.POST("/waypoint/cluster", routes.WaypointCluster) + se.Router.POST("/category-preferences/reorder", routes.CategoryPreferencesReorder) + se.Router.POST("/subcategory-preferences/reorder", routes.SubcategoryPreferencesReorder) se.Router.POST("/trail-merge/suggest", routes.TrailMergeSuggest) se.Router.POST("/trail-merge", routes.TrailMerge(client)) @@ -213,6 +229,9 @@ func registerCronJobs(app core.App, client meilisearch.ServiceManager) { func initData(app core.App, client meilisearch.ServiceManager) error { initCategories(app) + if err := util.SeedDefaultSubcategories(app); err != nil { + return err + } initPlugins(app) initMeilisearchConfig(client) go func() { @@ -280,31 +299,29 @@ func initCategories(app core.App) error { if err := query.All(&records); err != nil { return err } - if len(records) != 0 { - return nil - } - collection, err := app.FindCollectionByNameOrId("categories") if err != nil { return err } - categories := []string{"Hiking", "Walking", "Climbing", "Skiing", "Canoeing", "Biking", "Other"} - for _, element := range categories { - record := core.NewRecord(collection) - record.Set("name", element) - record.Set("settings", map[string]any{ - "wp_merge_enabled": true, - "wp_merge_radius": 50, - }) - if f, err := filesystem.NewFileFromPath("migrations/initial_data/" + strings.ToLower(element) + ".jpg"); err == nil { - record.Set("img", f) - } - if err := app.Save(record); err != nil { - return err + if len(records) == 0 { + for _, element := range util.DefaultCategoryNames() { + record := core.NewRecord(collection) + record.Set("name", element) + record.Set("settings", map[string]any{ + "wp_merge_enabled": true, + "wp_merge_radius": 50, + }) + err := app.Save(record) + if err != nil { + return err + } } } - return nil + if err := util.PrepopulateDefaultCategoryTranslations(app); err != nil { + return err + } + return util.PrepopulateDefaultCategoryIcons(app) } func initMeilisearchConfig(client meilisearch.ServiceManager) { @@ -312,13 +329,15 @@ func initMeilisearchConfig(client meilisearch.ServiceManager) { "trails": { SearchableAttributes: []string{"author_name", "name", "description", "location", "tags"}, FilterableAttributes: []string{ - "id", "_geo", "author", "category", "completed", "date", "difficulty", - "distance", "elevation_gain", "elevation_loss", "likes", "public", - "shares", "tags", "min_lat", "max_lat", "min_lon", "max_lon", "bounding_box_diagonal", + "id", "_geo", "author", "category_id", "subcategory_id", + "is_federated", "completed", "date", "difficulty", "distance", + "elevation_gain", "elevation_loss", "likes", "public", "shares", + "tags", "min_lat", "max_lat", "min_lon", "max_lon", "bounding_box_diagonal", }, SortableAttributes: []string{ "author", "created", "date", "difficulty", "distance", "duration", "elevation_gain", "elevation_loss", "like_count", "name", + "min_lat", "max_lat", "min_lon", "max_lon", }, RankingRules: []string{"words", "typo", "proximity", "attribute", "sort", "exactness"}, }, diff --git a/db/migrations/1781000000_categories_redesign.go b/db/migrations/1781000000_categories_redesign.go new file mode 100644 index 00000000..c2b947b7 --- /dev/null +++ b/db/migrations/1781000000_categories_redesign.go @@ -0,0 +1,803 @@ +package migrations + +import ( + "encoding/json" + "errors" + "fmt" + "pocketbase/util" + "sort" + "strings" + + "github.com/pocketbase/pocketbase/core" + m "github.com/pocketbase/pocketbase/migrations" + "github.com/pocketbase/pocketbase/tools/filesystem" +) + +func init() { + m.Register(func(app core.App) error { + collection, err := app.FindCollectionByNameOrId("categories") + if err != nil { + return err + } + + if err := resolveCategoryNameCollisions(app); err != nil { + return fmt.Errorf("failed to resolve category name collisions: %w", err) + } + + if err := deleteCategoryImageFiles(app); err != nil { + return fmt.Errorf("failed to delete category image files: %w", err) + } + + collection.Fields.RemoveById("64dsnxtb") + + if err := collection.Fields.AddMarshaledJSONAt(len(collection.Fields), []byte(`{ + "hidden": false, + "id": "texti4ksx4gm", + "max": 0, + "min": 0, + "name": "short_name", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }`)); err != nil { + return err + } + + if err := collection.Fields.AddMarshaledJSONAt(len(collection.Fields), []byte(`{ + "hidden": false, + "id": "text0r6k2h4gi", + "max": 0, + "min": 0, + "name": "icon", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }`)); err != nil { + return err + } + + if err := collection.Fields.AddMarshaledJSONAt(len(collection.Fields), []byte(`{ + "hidden": false, + "id": "jsonvkf7o88i", + "maxSize": 0, + "name": "translations", + "presentable": false, + "required": false, + "system": false, + "type": "json" + }`)); err != nil { + return err + } + + if err := app.Save(collection); err != nil { + return err + } + + if err := ensureRunningCategory(app); err != nil { + return fmt.Errorf("failed to ensure running category: %w", err) + } + + if err := createSubcategoriesCollection(app); err != nil { + return err + } + + trailsCollection, err := app.FindCollectionByNameOrId("trails") + if err != nil { + return err + } + if err := trailsCollection.Fields.AddMarshaledJSONAt(len(trailsCollection.Fields), []byte(`{ + "cascadeDelete": false, + "collectionId": "pbc_1781100000", + "hidden": false, + "id": "relphase2subct", + "maxSelect": 1, + "minSelect": 0, + "name": "subcategory", + "presentable": false, + "required": false, + "system": false, + "type": "relation" + }`)); err != nil { + return err + } + if err := trailsCollection.Fields.AddMarshaledJSONAt(len(trailsCollection.Fields), []byte(`{ + "hidden": false, + "id": "textremotecat1", + "max": 0, + "min": 0, + "name": "federated_category_name", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }`)); err != nil { + return err + } + if err := trailsCollection.Fields.AddMarshaledJSONAt(len(trailsCollection.Fields), []byte(`{ + "hidden": false, + "id": "textremotesub1", + "max": 0, + "min": 0, + "name": "federated_subcategory_name", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }`)); err != nil { + return err + } + if err := app.Save(trailsCollection); err != nil { + return err + } + + if err := util.PrepopulateDefaultCategoryTranslations(app); err != nil { + return fmt.Errorf("failed to prepopulate default category translations: %w", err) + } + + if err := util.PrepopulateDefaultCategoryIcons(app); err != nil { + return fmt.Errorf("failed to prepopulate default category icons: %w", err) + } + + if err := util.SeedDefaultSubcategories(app); err != nil { + return fmt.Errorf("failed to seed default subcategories: %w", err) + } + + if err := createUserCategoryPreferencesCollection(app); err != nil { + return err + } + + if err := createUserSubcategoryPreferencesCollection(app); err != nil { + return err + } + + if err := migrateFavouriteSportToPriority(app); err != nil { + return fmt.Errorf("failed to migrate favourite sport to category priority: %w", err) + } + + if err := removeSettingsCategoryField(app); err != nil { + return fmt.Errorf("failed to remove settings.category field: %w", err) + } + + if err := util.ValidateCategoryCollectionState(app); err != nil { + return fmt.Errorf("categories redesign migration failed validation: %w", err) + } + + return nil + }, func(app core.App) error { + if collection, err := app.FindCollectionByNameOrId("pbc_1781250000"); err == nil { + if err := app.Delete(collection); err != nil { + return err + } + } + + if collection, err := app.FindCollectionByNameOrId("pbc_1781200000"); err == nil { + if err := app.Delete(collection); err != nil { + return err + } + } + + if trailsCollection, err := app.FindCollectionByNameOrId("trails"); err == nil { + trailsCollection.Fields.RemoveById("relphase2subct") + trailsCollection.Fields.RemoveById("textremotecat1") + trailsCollection.Fields.RemoveById("textremotesub1") + if err := app.Save(trailsCollection); err != nil { + return err + } + } + + if collection, err := app.FindCollectionByNameOrId("pbc_1781100000"); err == nil { + if err := app.Delete(collection); err != nil { + return err + } + } + + if settingsCollection, err := app.FindCollectionByNameOrId("settings"); err == nil { + if err := settingsCollection.Fields.AddMarshaledJSONAt(len(settingsCollection.Fields), []byte(`{ + "cascadeDelete": false, + "collectionId": "kjxvi8asj2igqwf", + "hidden": false, + "id": "owlyzl1x", + "maxSelect": 1, + "minSelect": 0, + "name": "category", + "presentable": false, + "required": false, + "system": false, + "type": "relation" + }`)); err != nil { + return err + } + if err := app.Save(settingsCollection); err != nil { + return err + } + } + + collection, err := app.FindCollectionByNameOrId("categories") + if err != nil { + return err + } + + collection.Fields.RemoveById("texti4ksx4gm") + collection.Fields.RemoveById("text0r6k2h4gi") + collection.Fields.RemoveById("jsonvkf7o88i") + + if err := collection.Fields.AddMarshaledJSONAt(2, []byte(`{ + "hidden": false, + "id": "64dsnxtb", + "maxSelect": 1, + "maxSize": 5242880, + "mimeTypes": null, + "name": "img", + "presentable": false, + "protected": false, + "required": false, + "system": false, + "thumbs": null, + "type": "file" + }`)); err != nil { + return err + } + + return app.Save(collection) + }) +} + +type categoryCollisionCandidate struct { + id string + name string + created string +} + +func resolveCategoryNameCollisions(app core.App) error { + allCategories, err := app.FindAllRecords("categories") + if err != nil { + return err + } + + candidates := make([]categoryCollisionCandidate, 0, len(allCategories)) + for _, category := range allCategories { + candidates = append(candidates, categoryCollisionCandidate{ + id: category.Id, + name: category.GetString("name"), + created: category.GetString("created"), + }) + } + resolvedNames := resolveCategoryNameCollisionCandidates(candidates) + + for _, category := range allCategories { + resolvedName, ok := resolvedNames[category.Id] + if !ok { + continue + } + + originalName := category.GetString("name") + category.Set("name", resolvedName) + if err := app.Save(category); err != nil { + return fmt.Errorf("failed to resolve category name collision for %q: %w", originalName, err) + } + } + + return nil +} + +func collisionResolvedCategoryName(name string, id string, seen map[string]struct{}) string { + baseName := strings.TrimSpace(name) + if baseName == "" { + baseName = "Category" + } + + for i := 0; ; i++ { + suffix := id + if i > 0 { + suffix = fmt.Sprintf("%s-%d", id, i+1) + } + + candidate := fmt.Sprintf("%s (%s)", baseName, suffix) + if _, ok := seen[util.NormalizeCategoryName(candidate)]; !ok { + return candidate + } + } +} + +func resolveCategoryNameCollisionCandidates(candidates []categoryCollisionCandidate) map[string]string { + sorted := append([]categoryCollisionCandidate(nil), candidates...) + sort.SliceStable(sorted, func(i, j int) bool { + left := sorted[i] + right := sorted[j] + + leftName := util.NormalizeCategoryName(left.name) + rightName := util.NormalizeCategoryName(right.name) + if leftName != rightName { + return leftName < rightName + } + + if left.created != right.created { + return left.created < right.created + } + + return left.id < right.id + }) + + seen := map[string]struct{}{} + resolved := map[string]string{} + for _, category := range sorted { + normalizedName := util.NormalizeCategoryName(category.name) + if _, ok := seen[normalizedName]; !ok { + seen[normalizedName] = struct{}{} + continue + } + + resolvedName := collisionResolvedCategoryName(category.name, category.id, seen) + resolved[category.id] = resolvedName + seen[util.NormalizeCategoryName(resolvedName)] = struct{}{} + } + + return resolved +} + +func ensureRunningCategory(app core.App) error { + allCategories, err := app.FindAllRecords("categories") + if err != nil { + return err + } + + if len(allCategories) == 0 { + return util.SeedDefaultCategories(app) + } + + for _, category := range allCategories { + if util.NormalizeCategoryName(category.GetString("name")) == util.NormalizeCategoryName("Running") { + return nil + } + } + + collection, err := app.FindCollectionByNameOrId("categories") + if err != nil { + return err + } + + record := core.NewRecord(collection) + record.Set("name", "Running") + record.Set("settings", map[string]any{ + "wp_merge_enabled": true, + "wp_merge_radius": 50, + }) + + return app.Save(record) +} + +func deleteCategoryImageFiles(app core.App) error { + allCategories, err := app.FindAllRecords("categories") + if err != nil { + return err + } + + fsys, err := app.NewFilesystem() + if err != nil { + return err + } + defer fsys.Close() + + var failures []error + for _, category := range allCategories { + for _, filename := range categoryImageFilenames(category) { + if filename == "" || strings.ContainsAny(filename, `/\`) { + continue + } + + path := category.BaseFilesPath() + "/" + filename + if err := fsys.Delete(path); err != nil && !errors.Is(err, filesystem.ErrNotFound) { + failures = append(failures, fmt.Errorf("failed to delete category image %q: %w", path, err)) + } + + if errs := fsys.DeletePrefix(category.BaseFilesPath() + "/thumbs_" + filename + "/"); len(errs) > 0 { + failures = append(failures, fmt.Errorf("failed to delete category image thumbs for %q: %w", path, errors.Join(errs...))) + } + } + } + + if len(failures) > 0 { + return errors.Join(failures...) + } + + return nil +} + +func categoryImageFilenames(record *core.Record) []string { + filenames := record.GetStringSlice("img") + if len(filenames) > 0 { + return filenames + } + + filename := record.GetString("img") + if filename == "" { + return nil + } + + return []string{filename} +} + +func createSubcategoriesCollection(app core.App) error { + jsonData := `{ + "createRule": null, + "deleteRule": null, + "fields": [ + { + "autogeneratePattern": "[a-z0-9]{15}", + "hidden": false, + "id": "text3208210256", + "max": 15, + "min": 15, + "name": "id", + "pattern": "^[a-z0-9]+$", + "presentable": false, + "primaryKey": true, + "required": true, + "system": true, + "type": "text" + }, + { + "cascadeDelete": true, + "collectionId": "kjxvi8asj2igqwf", + "hidden": false, + "id": "relphase2cat01", + "maxSelect": 1, + "minSelect": 0, + "name": "category", + "presentable": false, + "required": true, + "system": false, + "type": "relation" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "textphase2name", + "max": 0, + "min": 1, + "name": "name", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": true, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "textphase2shrt", + "max": 0, + "min": 0, + "name": "short_name", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "textphase2icon", + "max": 0, + "min": 0, + "name": "icon", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "textphase2badge", + "max": 0, + "min": 0, + "name": "badge_icon", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "hidden": false, + "id": "jsonphase2trns", + "maxSize": 0, + "name": "translations", + "presentable": false, + "required": false, + "system": false, + "type": "json" + }, + { + "hidden": false, + "id": "autodate2990389176", + "name": "created", + "onCreate": true, + "onUpdate": false, + "presentable": false, + "system": false, + "type": "autodate" + }, + { + "hidden": false, + "id": "autodate3332085495", + "name": "updated", + "onCreate": true, + "onUpdate": true, + "presentable": false, + "system": false, + "type": "autodate" + } + ], + "id": "pbc_1781100000", + "indexes": [], + "listRule": "", + "name": "subcategories", + "system": false, + "type": "base", + "updateRule": null, + "viewRule": "" + }` + + return saveCollectionFromJSON(app, jsonData) +} + +func createUserCategoryPreferencesCollection(app core.App) error { + jsonData := `{ + "createRule": "@request.auth.id != \"\" && user = @request.auth.id", + "deleteRule": "@request.auth.id != \"\" && user = @request.auth.id", + "fields": [ + { + "autogeneratePattern": "[a-z0-9]{15}", + "hidden": false, + "id": "text3208210256", + "max": 15, + "min": 15, + "name": "id", + "pattern": "^[a-z0-9]+$", + "presentable": false, + "primaryKey": true, + "required": true, + "system": true, + "type": "text" + }, + { + "cascadeDelete": true, + "collectionId": "_pb_users_auth_", + "hidden": false, + "id": "relphase3user", + "maxSelect": 1, + "minSelect": 0, + "name": "user", + "presentable": false, + "required": true, + "system": false, + "type": "relation" + }, + { + "cascadeDelete": true, + "collectionId": "kjxvi8asj2igqwf", + "hidden": false, + "id": "relphase3cat", + "maxSelect": 1, + "minSelect": 0, + "name": "category", + "presentable": false, + "required": true, + "system": false, + "type": "relation" + }, + { + "hidden": false, + "id": "boolphase3visible", + "name": "visible", + "presentable": false, + "required": false, + "system": false, + "type": "bool" + }, + { + "hidden": false, + "id": "numphase3prio", + "max": null, + "min": 1, + "name": "priority", + "onlyInt": true, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "autodate2990389176", + "name": "created", + "onCreate": true, + "onUpdate": false, + "presentable": false, + "system": false, + "type": "autodate" + }, + { + "hidden": false, + "id": "autodate3332085495", + "name": "updated", + "onCreate": true, + "onUpdate": true, + "presentable": false, + "system": false, + "type": "autodate" + } + ], + "id": "pbc_1781200000", + "indexes": [ + "CREATE UNIQUE INDEX ` + "`" + `idx_user_category_preferences_user_category` + "`" + ` ON ` + "`" + `user_category_preferences` + "`" + ` (` + "`" + `user` + "`" + `, ` + "`" + `category` + "`" + `)" + ], + "listRule": "@request.auth.id != \"\" && user = @request.auth.id", + "name": "user_category_preferences", + "system": false, + "type": "base", + "updateRule": "@request.auth.id != \"\" && user = @request.auth.id", + "viewRule": "@request.auth.id != \"\" && user = @request.auth.id" + }` + + return saveCollectionFromJSON(app, jsonData) +} + +func createUserSubcategoryPreferencesCollection(app core.App) error { + jsonData := `{ + "createRule": "@request.auth.id != \"\" && user = @request.auth.id", + "deleteRule": "@request.auth.id != \"\" && user = @request.auth.id", + "fields": [ + { + "autogeneratePattern": "[a-z0-9]{15}", + "hidden": false, + "id": "text178125id", + "max": 15, + "min": 15, + "name": "id", + "pattern": "^[a-z0-9]+$", + "presentable": false, + "primaryKey": true, + "required": true, + "system": true, + "type": "text" + }, + { + "cascadeDelete": true, + "collectionId": "_pb_users_auth_", + "hidden": false, + "id": "rel178125user", + "maxSelect": 1, + "minSelect": 0, + "name": "user", + "presentable": false, + "required": true, + "system": false, + "type": "relation" + }, + { + "cascadeDelete": true, + "collectionId": "pbc_1781100000", + "hidden": false, + "id": "rel178125subcat", + "maxSelect": 1, + "minSelect": 0, + "name": "subcategory", + "presentable": false, + "required": true, + "system": false, + "type": "relation" + }, + { + "hidden": false, + "id": "bool178125visible", + "name": "visible", + "presentable": false, + "required": false, + "system": false, + "type": "bool" + }, + { + "hidden": false, + "id": "num178125prio", + "max": null, + "min": 1, + "name": "priority", + "onlyInt": true, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "autodate178125created", + "name": "created", + "onCreate": true, + "onUpdate": false, + "presentable": false, + "system": false, + "type": "autodate" + }, + { + "hidden": false, + "id": "autodate178125updated", + "name": "updated", + "onCreate": true, + "onUpdate": true, + "presentable": false, + "system": false, + "type": "autodate" + } + ], + "id": "pbc_1781250000", + "indexes": [ + "CREATE UNIQUE INDEX ` + "`" + `idx_user_subcategory_preferences_user_subcategory` + "`" + ` ON ` + "`" + `user_subcategory_preferences` + "`" + ` (` + "`" + `user` + "`" + `, ` + "`" + `subcategory` + "`" + `)" + ], + "listRule": "@request.auth.id != \"\" && user = @request.auth.id", + "name": "user_subcategory_preferences", + "system": false, + "type": "base", + "updateRule": "@request.auth.id != \"\" && user = @request.auth.id", + "viewRule": "@request.auth.id != \"\" && user = @request.auth.id" + }` + + return saveCollectionFromJSON(app, jsonData) +} + +// migrateFavouriteSportToPriority carries the former per-user "favourite sport" +// (settings.category) over to the new category priority model: the favourite becomes +// the user's priority-1 category. Users without a favourite get a common default +// instead of falling back to category sort order. Users who already organized +// categories by priority are left untouched. +func migrateFavouriteSportToPriority(app core.App) error { + settingsRecords, err := app.FindAllRecords("settings") + if err != nil { + return err + } + + for _, settings := range settingsRecords { + if err := util.EnsureUserCategoryPriority(app, settings.GetString("user"), settings.GetString("category")); err != nil { + return err + } + } + + return nil +} + +func removeSettingsCategoryField(app core.App) error { + settings, err := app.FindCollectionByNameOrId("settings") + if err != nil { + return err + } + + settings.Fields.RemoveById("owlyzl1x") + return app.Save(settings) +} + +func saveCollectionFromJSON(app core.App, jsonData string) error { + collection := &core.Collection{} + if err := json.Unmarshal([]byte(jsonData), collection); err != nil { + return err + } + + return app.Save(collection) +} diff --git a/db/migrations/initial_data/biking.jpg b/db/migrations/initial_data/biking.jpg deleted file mode 100644 index 18a00b40..00000000 Binary files a/db/migrations/initial_data/biking.jpg and /dev/null differ diff --git a/db/migrations/initial_data/canoeing.jpg b/db/migrations/initial_data/canoeing.jpg deleted file mode 100644 index 3809cdfc..00000000 Binary files a/db/migrations/initial_data/canoeing.jpg and /dev/null differ diff --git a/db/migrations/initial_data/climbing.jpg b/db/migrations/initial_data/climbing.jpg deleted file mode 100644 index 2ebef9b9..00000000 Binary files a/db/migrations/initial_data/climbing.jpg and /dev/null differ diff --git a/db/migrations/initial_data/hiking.jpg b/db/migrations/initial_data/hiking.jpg deleted file mode 100644 index 9700419b..00000000 Binary files a/db/migrations/initial_data/hiking.jpg and /dev/null differ diff --git a/db/migrations/initial_data/skiing.jpg b/db/migrations/initial_data/skiing.jpg deleted file mode 100644 index dede5023..00000000 Binary files a/db/migrations/initial_data/skiing.jpg and /dev/null differ diff --git a/db/migrations/initial_data/walking.jpg b/db/migrations/initial_data/walking.jpg deleted file mode 100644 index 167a70d5..00000000 Binary files a/db/migrations/initial_data/walking.jpg and /dev/null differ diff --git a/db/plugins/importer/importer.go b/db/plugins/importer/importer.go index a427c9b4..7d1dd8c1 100644 --- a/db/plugins/importer/importer.go +++ b/db/plugins/importer/importer.go @@ -29,7 +29,7 @@ type Options struct { ActorID string DefaultPublic bool CreateSummitLogForCompleted bool - CategoryMapping map[string]string + CategoryMapping map[string]CategoryMappingValue Manifest pluginsystem.Manifest Policy pluginsystem.RequestPolicyContext Auth map[string]any @@ -43,6 +43,16 @@ type Result struct { Skipped bool } +type CategoryMappingTarget struct { + CategoryID string + SubcategoryID string +} + +type CategoryMappingValue struct { + Category string + Subcategory string +} + // ImportTrail is the boundary between plugin output and wanderer records. It // validates the provider identity, deduplicates by trail_external_reference, // stores the GPX/photos, maps GPX metrics onto the trail record, and creates the @@ -78,7 +88,7 @@ func ImportTrail(ctx context.Context, app core.App, item pluginsystem.TrailImpor applyProviderStart(&metrics, trackIndex, item.Metadata) applyProviderMetrics(&metrics, item.Metadata) public := publicFromPrivacy(item.Privacy, opts.DefaultPublic) - categoryID := categoryIDForImport(app, item, opts.CategoryMapping) + categoryTarget := categoryTargetForImport(app, item, opts.CategoryMapping) date := dateFromImport(item, metrics) mediaBudget := &pluginMediaBudget{} photos := photoFiles(ctx, app, item.Photos, opts, mediaBudget) @@ -96,7 +106,8 @@ func ImportTrail(ctx context.Context, app core.App, item pluginsystem.TrailImpor "lat": metrics.StartLat, "lon": metrics.StartLon, "difficulty": "easy", - "category": categoryID, + "category": categoryTarget.CategoryID, + "subcategory": categoryTarget.SubcategoryID, "author": opts.ActorID, }) record.Set("gpx", gpxFile) @@ -772,11 +783,15 @@ func createSummitLog(app core.App, trailID string, actorID string, date time.Tim return app.Save(record) } -func categoryIDForImport(app core.App, item pluginsystem.TrailImport, mapping map[string]string) string { - if category, matched := CategoryFromProviderMapping(app, ProviderCategoryFromImport(item), mapping); matched { - return category +func categoryTargetForImport(app core.App, item pluginsystem.TrailImport, mapping map[string]CategoryMappingValue) CategoryMappingTarget { + if categoryTarget, matched := CategoryTargetFromProviderMapping(app, ProviderCategoryFromImport(item), mapping); matched { + return categoryTarget } - return categoryIDForActivityType(app, item.ActivityType) + return categoryTargetForActivityType(app, item.ActivityType) +} + +func categoryIDForImport(app core.App, item pluginsystem.TrailImport, mapping map[string]CategoryMappingValue) string { + return categoryTargetForImport(app, item, mapping).CategoryID } func ProviderCategoryFromImport(item pluginsystem.TrailImport) string { @@ -788,58 +803,109 @@ func ProviderCategoryFromImport(item pluginsystem.TrailImport) string { return strings.TrimSpace(value) } -func CategoryFromProviderMapping(app core.App, providerCategory string, mapping map[string]string) (string, bool) { +func CategoryFromProviderMapping(app core.App, providerCategory string, mapping map[string]CategoryMappingValue) (string, bool) { + target, matched := CategoryTargetFromProviderMapping(app, providerCategory, mapping) + return target.CategoryID, matched +} + +func CategoryTargetFromProviderMapping(app core.App, providerCategory string, mapping map[string]CategoryMappingValue) (CategoryMappingTarget, bool) { providerCategory = strings.TrimSpace(providerCategory) if providerCategory == "" || len(mapping) == 0 { - return "", false + return CategoryMappingTarget{}, false } - rawTarget, matched := mapping[providerCategory] + mappingTarget, matched := mapping[providerCategory] if !matched { - return "", false + return CategoryMappingTarget{}, false } - target := strings.TrimSpace(rawTarget) - if target == "" { - return "", true + if mappingTarget.Category == "" && mappingTarget.Subcategory == "" { + return CategoryMappingTarget{}, true } - if category, err := app.FindRecordById("categories", target); err == nil && category != nil { - return category.Id, true - } - category, _ := app.FindFirstRecordByData("categories", "name", target) - if category == nil { - return "", false - } - return category.Id, true + + return resolveCategoryMappingTarget(app, mappingTarget) } // categoryIDForActivityType maps common provider activity labels to wanderer's // built-in categories. Unknown labels intentionally leave the category empty. func categoryIDForActivityType(app core.App, activityType string) string { - categoryMap := map[string]string{ - "hiking": "Hiking", - "hike": "Hiking", - "walking": "Walking", - "walk": "Walking", - "running": "Walking", - "run": "Walking", - "biking": "Biking", - "cycling": "Biking", - "ride": "Biking", - "mtb": "Biking", - "skiing": "Skiing", - "canoeing": "Canoeing", - "climbing": "Climbing", - } + return categoryTargetForActivityType(app, activityType).CategoryID +} - name := categoryMap[strings.ToLower(activityType)] +func categoryTargetForActivityType(app core.App, activityType string) CategoryMappingTarget { + name := categoryNameForActivityType(activityType) if name == "" { - return "" + return CategoryMappingTarget{} } - category, _ := app.FindFirstRecordByData("categories", "name", name) - if category == nil { - return "" + target, matched := resolveCategoryMappingTarget(app, CategoryMappingValue{Category: name}) + if !matched { + return CategoryMappingTarget{} } - return category.Id + return target +} + +func categoryNameForActivityType(activityType string) string { + categoryMap := map[string]string{ + "hiking": "Hiking", + "hike": "Hiking", + "walking": "Walking", + "walk": "Walking", + "running": "Running", + "run": "Running", + "virtualrun": "Running", + "trailrun": "Running", + "jogging": "Running", + "biking": "Biking", + "cycling": "Biking", + "ride": "Biking", + "mtb": "Biking", + "skiing": "Skiing", + "canoeing": "Canoeing", + "climbing": "Climbing", + } + + return categoryMap[strings.ToLower(strings.TrimSpace(activityType))] +} + +func resolveCategoryMappingTarget(app core.App, target CategoryMappingValue) (CategoryMappingTarget, bool) { + categoryNameOrID := strings.TrimSpace(target.Category) + subcategoryNameOrID := strings.TrimSpace(target.Subcategory) + if categoryNameOrID == "" && subcategoryNameOrID == "" { + return CategoryMappingTarget{}, false + } + + if subcategoryNameOrID != "" { + if subcategory, err := app.FindRecordById("subcategories", subcategoryNameOrID); err == nil && subcategory != nil { + categoryID := subcategory.GetString("category") + if categoryID == "" { + return CategoryMappingTarget{}, false + } + return CategoryMappingTarget{CategoryID: categoryID, SubcategoryID: subcategory.Id}, true + } + category, subcategory, err := util.ResolveCategoryAndSubcategoryByNormalizedNames(app, categoryNameOrID, subcategoryNameOrID) + if err == nil && category != nil && subcategory != nil { + return CategoryMappingTarget{CategoryID: category.Id, SubcategoryID: subcategory.Id}, true + } + return CategoryMappingTarget{}, false + } + + if category, err := app.FindRecordById("categories", categoryNameOrID); err == nil && category != nil { + return CategoryMappingTarget{CategoryID: category.Id}, true + } + + if subcategory, err := app.FindRecordById("subcategories", categoryNameOrID); err == nil && subcategory != nil { + categoryID := subcategory.GetString("category") + if categoryID == "" { + return CategoryMappingTarget{}, false + } + return CategoryMappingTarget{CategoryID: categoryID, SubcategoryID: subcategory.Id}, true + } + + category, _ := util.FindCategoryByNormalizedName(app, categoryNameOrID) + if category != nil { + return CategoryMappingTarget{CategoryID: category.Id}, true + } + + return CategoryMappingTarget{}, false } func fallbackName(name string) string { diff --git a/db/plugins/importer/importer_test.go b/db/plugins/importer/importer_test.go index ce08dc67..8d85a887 100644 --- a/db/plugins/importer/importer_test.go +++ b/db/plugins/importer/importer_test.go @@ -7,6 +7,9 @@ import ( "testing" "time" + "github.com/pocketbase/pocketbase/core" + pbtests "github.com/pocketbase/pocketbase/tests" + pluginsystem "pocketbase/pluginsystem" "pocketbase/util" ) @@ -264,7 +267,7 @@ func TestCategoryIDForImportDoesNotFallbackWhenProviderMappingIsBlank(t *testing }, } - if got := categoryIDForImport(nil, item, map[string]string{"Ride": ""}); got != "" { + if got := categoryIDForImport(nil, item, map[string]CategoryMappingValue{"Ride": {}}); got != "" { t.Fatalf("expected blank provider mapping to suppress activity fallback, got %q", got) } } @@ -282,6 +285,121 @@ func TestProviderCategoryFromImport(t *testing.T) { } } +func TestCategoryNameForActivityType(t *testing.T) { + cases := map[string]string{ + "run": "Running", + "running": "Running", + "VirtualRun": "Running", + "trailrun": "Running", + "jogging": "Running", + "walk": "Walking", + "hike": "Hiking", + "unknown": "", + } + + for activityType, want := range cases { + if got := categoryNameForActivityType(activityType); got != want { + t.Fatalf("categoryNameForActivityType(%q) = %q, want %q", activityType, got, want) + } + } +} + +func TestCategoryFromProviderMappingUsesNormalizedCategoryName(t *testing.T) { + app := setupImporterCategoryTestApp(t) + + category := core.NewRecord(mustFindImporterTestCollection(t, app, "categories")) + category.Set("name", "Trail Running") + if err := app.Save(category); err != nil { + t.Fatal(err) + } + + got, matched := CategoryFromProviderMapping(app, "Run", map[string]CategoryMappingValue{"Run": {Category: "trail-running"}}) + if !matched { + t.Fatal("expected provider mapping to match") + } + if got != category.Id { + t.Fatalf("CategoryFromProviderMapping() = %q, want %q", got, category.Id) + } +} + +func TestCategoryTargetFromProviderMappingSupportsSubcategoryPath(t *testing.T) { + app := setupImporterCategoryTestApp(t) + + category := core.NewRecord(mustFindImporterTestCollection(t, app, "categories")) + category.Set("name", "Running") + if err := app.Save(category); err != nil { + t.Fatal(err) + } + + subcategory := core.NewRecord(mustFindImporterTestCollection(t, app, "subcategories")) + subcategory.Set("category", category.Id) + subcategory.Set("name", "Trail") + if err := app.Save(subcategory); err != nil { + t.Fatal(err) + } + + target, matched := CategoryTargetFromProviderMapping(app, "TrailRun", map[string]CategoryMappingValue{"TrailRun": {Category: "Running", Subcategory: "Trail"}}) + if !matched { + t.Fatal("expected provider mapping to match") + } + if target.CategoryID != category.Id || target.SubcategoryID != subcategory.Id { + t.Fatalf("CategoryTargetFromProviderMapping() = %#v, want category=%q subcategory=%q", target, category.Id, subcategory.Id) + } +} + +func TestCategoryTargetFromProviderMappingPrefersLiteralCategoryWithSlash(t *testing.T) { + app := setupImporterCategoryTestApp(t) + + slashCategory := core.NewRecord(mustFindImporterTestCollection(t, app, "categories")) + slashCategory.Set("name", "Foo/Bar") + if err := app.Save(slashCategory); err != nil { + t.Fatal(err) + } + + parentCategory := core.NewRecord(mustFindImporterTestCollection(t, app, "categories")) + parentCategory.Set("name", "Foo") + if err := app.Save(parentCategory); err != nil { + t.Fatal(err) + } + + subcategory := core.NewRecord(mustFindImporterTestCollection(t, app, "subcategories")) + subcategory.Set("category", parentCategory.Id) + subcategory.Set("name", "Bar") + if err := app.Save(subcategory); err != nil { + t.Fatal(err) + } + + target, matched := CategoryTargetFromProviderMapping(app, "Provider", map[string]CategoryMappingValue{"Provider": {Category: "Foo/Bar"}}) + if !matched { + t.Fatal("expected provider mapping to match") + } + if target.CategoryID != slashCategory.Id || target.SubcategoryID != "" { + t.Fatalf("CategoryTargetFromProviderMapping() = %#v, want literal category %q", target, slashCategory.Id) + } +} + +func TestCategoryTargetFromProviderMappingDoesNotSplitSlashCategoryName(t *testing.T) { + app := setupImporterCategoryTestApp(t) + + category := core.NewRecord(mustFindImporterTestCollection(t, app, "categories")) + category.Set("name", "Foo") + if err := app.Save(category); err != nil { + t.Fatal(err) + } + + subcategory := core.NewRecord(mustFindImporterTestCollection(t, app, "subcategories")) + subcategory.Set("category", category.Id) + subcategory.Set("name", "Bar") + if err := app.Save(subcategory); err != nil { + t.Fatal(err) + } + + target, matched := CategoryTargetFromProviderMapping(app, "Provider", map[string]CategoryMappingValue{"Provider": {Category: "Foo/Bar"}}) + if matched { + t.Fatalf("CategoryTargetFromProviderMapping() = %#v, expected slash category name not to be split", target) + } +} + func TestDateFromImport(t *testing.T) { started := time.Date(2025, 6, 1, 8, 0, 0, 0, time.UTC) @@ -430,3 +548,42 @@ func TestRemoveRawQueryParamOrdered(t *testing.T) { t.Fatalf("unexpected query: %q", got) } } + +func setupImporterCategoryTestApp(t *testing.T) *pbtests.TestApp { + t.Helper() + + app, err := pbtests.NewTestApp(t.TempDir()) + if err != nil { + t.Fatal(err) + } + + categories := core.NewBaseCollection("categories") + categories.Fields.Add(&core.TextField{Name: "name", Required: true}) + if err := app.Save(categories); err != nil { + app.Cleanup() + t.Fatal(err) + } + + subcategories := core.NewBaseCollection("subcategories") + subcategories.Fields.Add( + &core.RelationField{Name: "category", CollectionId: categories.Id, MaxSelect: 1, Required: true}, + &core.TextField{Name: "name", Required: true}, + ) + if err := app.Save(subcategories); err != nil { + app.Cleanup() + t.Fatal(err) + } + + return app +} + +func mustFindImporterTestCollection(t *testing.T, app core.App, name string) *core.Collection { + t.Helper() + + collection, err := app.FindCollectionByNameOrId(name) + if err != nil { + t.Fatal(err) + } + + return collection +} diff --git a/db/routes/category_preferences.go b/db/routes/category_preferences.go new file mode 100644 index 00000000..8e2a7060 --- /dev/null +++ b/db/routes/category_preferences.go @@ -0,0 +1,52 @@ +package routes + +import ( + "net/http" + "pocketbase/util" + + "github.com/pocketbase/pocketbase/apis" + "github.com/pocketbase/pocketbase/core" +) + +type categoryPreferenceReorderRequest struct { + Categories []string `json:"categories"` +} + +type subcategoryPreferenceReorderRequest struct { + Category string `json:"category"` + Subcategories []string `json:"subcategories"` +} + +func CategoryPreferencesReorder(e *core.RequestEvent) error { + if e.Auth == nil { + return apis.NewUnauthorizedError("authentication required", nil) + } + + var request categoryPreferenceReorderRequest + if err := e.BindBody(&request); err != nil { + return apis.NewBadRequestError("failed to read request data", err) + } + + if err := util.ReorderUserCategoryPreferences(e.App, e.Auth.Id, request.Categories); err != nil { + return apis.NewBadRequestError(err.Error(), err) + } + + return e.JSON(http.StatusOK, map[string]any{"acknowledged": true}) +} + +func SubcategoryPreferencesReorder(e *core.RequestEvent) error { + if e.Auth == nil { + return apis.NewUnauthorizedError("authentication required", nil) + } + + var request subcategoryPreferenceReorderRequest + if err := e.BindBody(&request); err != nil { + return apis.NewBadRequestError("failed to read request data", err) + } + + if err := util.ReorderUserSubcategoryPreferences(e.App, e.Auth.Id, request.Category, request.Subcategories); err != nil { + return apis.NewBadRequestError(err.Error(), err) + } + + return e.JSON(http.StatusOK, map[string]any{"acknowledged": true}) +} diff --git a/db/routes/plugin_system_category_remap.go b/db/routes/plugin_system_category_remap.go index 70794183..88447e20 100644 --- a/db/routes/plugin_system_category_remap.go +++ b/db/routes/plugin_system_category_remap.go @@ -24,8 +24,9 @@ type pluginCategoryRemapResponse struct { } type pluginCategoryRemapCandidate struct { - Trail *core.Record - CategoryID string + Trail *core.Record + CategoryID string + SubcategoryID string } type pluginCategoryTrailReference struct { @@ -72,6 +73,7 @@ func PluginSystemCategoryRemapApply(e *core.RequestEvent) error { return err } trail.Set("category", candidate.CategoryID) + trail.Set("subcategory", candidate.SubcategoryID) if err := txApp.Save(trail); err != nil { return err } @@ -84,7 +86,7 @@ func PluginSystemCategoryRemapApply(e *core.RequestEvent) error { return e.JSON(http.StatusOK, pluginCategoryRemapResponse{Count: len(candidates), Remapped: remapped}) } -func pluginCategoryRemapInput(e *core.RequestEvent) (*core.Record, map[string]string, error) { +func pluginCategoryRemapInput(e *core.RequestEvent) (*core.Record, map[string]importer.CategoryMappingValue, error) { if e.Auth == nil { return nil, nil, apis.NewUnauthorizedError("authentication required", nil) } @@ -109,7 +111,7 @@ func pluginCategoryRemapInput(e *core.RequestEvent) (*core.Record, map[string]st return instance, categoryMapping(pluginHostConfig(config)), nil } -func pluginCategoryRemapCandidates(app core.App, userID string, pluginID string, mapping map[string]string) ([]pluginCategoryRemapCandidate, error) { +func pluginCategoryRemapCandidates(app core.App, userID string, pluginID string, mapping map[string]importer.CategoryMappingValue) ([]pluginCategoryRemapCandidate, error) { if userID == "" || pluginID == "" || len(mapping) == 0 { return nil, nil } @@ -122,7 +124,7 @@ func pluginCategoryRemapCandidates(app core.App, userID string, pluginID string, return pluginCategoryRemapCandidatesFromRefs(app, refs, mapping), nil } -func pluginCategoryRemapCandidatesFromRefs(app core.App, refs []pluginCategoryTrailReference, mapping map[string]string) []pluginCategoryRemapCandidate { +func pluginCategoryRemapCandidatesFromRefs(app core.App, refs []pluginCategoryTrailReference, mapping map[string]importer.CategoryMappingValue) []pluginCategoryRemapCandidate { if len(refs) == 0 || len(mapping) == 0 { return nil } @@ -130,19 +132,23 @@ func pluginCategoryRemapCandidatesFromRefs(app core.App, refs []pluginCategoryTr candidates := make([]pluginCategoryRemapCandidate, 0, len(refs)) for _, ref := range refs { providerCategory := strings.TrimSpace(ref.Ref.GetString("provider_category")) - categoryID, matched := importer.CategoryFromProviderMapping(app, providerCategory, mapping) - if !matched || categoryID == "" || ref.Trail.GetString("category") == categoryID { + target, matched := importer.CategoryTargetFromProviderMapping(app, providerCategory, mapping) + if !matched || target.CategoryID == "" { + continue + } + if ref.Trail.GetString("category") == target.CategoryID && ref.Trail.GetString("subcategory") == target.SubcategoryID { continue } candidates = append(candidates, pluginCategoryRemapCandidate{ - Trail: ref.Trail, - CategoryID: categoryID, + Trail: ref.Trail, + CategoryID: target.CategoryID, + SubcategoryID: target.SubcategoryID, }) } return candidates } -func pluginCategoryBackfilledSinceMappingCountFromRefs(app core.App, instance *core.Record, refs []pluginCategoryTrailReference, mapping map[string]string) int { +func pluginCategoryBackfilledSinceMappingCountFromRefs(app core.App, instance *core.Record, refs []pluginCategoryTrailReference, mapping map[string]importer.CategoryMappingValue) int { mappingUpdatedAt := categoryMappingUpdatedAt(app, instance) if mappingUpdatedAt.IsZero() || len(refs) == 0 || len(mapping) == 0 { return 0 @@ -155,8 +161,8 @@ func pluginCategoryBackfilledSinceMappingCountFromRefs(app core.App, instance *c continue } providerCategory := strings.TrimSpace(ref.Ref.GetString("provider_category")) - categoryID, matched := importer.CategoryFromProviderMapping(app, providerCategory, mapping) - if matched && categoryID != "" && ref.Trail.GetString("category") != categoryID { + target, matched := importer.CategoryTargetFromProviderMapping(app, providerCategory, mapping) + if matched && target.CategoryID != "" && (ref.Trail.GetString("category") != target.CategoryID || ref.Trail.GetString("subcategory") != target.SubcategoryID) { count++ } } diff --git a/db/routes/plugin_system_sync.go b/db/routes/plugin_system_sync.go index 7c5cb471..93443807 100644 --- a/db/routes/plugin_system_sync.go +++ b/db/routes/plugin_system_sync.go @@ -578,24 +578,31 @@ func boolOption(config map[string]any, key string, fallback bool) bool { return value } -func categoryMapping(config map[string]any) map[string]string { +func categoryMapping(config map[string]any) map[string]importer.CategoryMappingValue { raw, ok := config["categoryMapping"].(map[string]any) if !ok { return nil } - result := make(map[string]string, len(raw)) + result := make(map[string]importer.CategoryMappingValue, len(raw)) for key, value := range raw { - category, ok := value.(string) - if ok { - result[key] = category + switch typed := value.(type) { + case string: + result[key] = importer.CategoryMappingValue{Category: strings.TrimSpace(typed)} + case map[string]any: + category, _ := typed["category"].(string) + subcategory, _ := typed["subcategory"].(string) + result[key] = importer.CategoryMappingValue{ + Category: strings.TrimSpace(category), + Subcategory: strings.TrimSpace(subcategory), + } } } return result } -func hasUsableCategoryMapping(mapping map[string]string) bool { - for _, category := range mapping { - if strings.TrimSpace(category) != "" { +func hasUsableCategoryMapping(mapping map[string]importer.CategoryMappingValue) bool { + for _, target := range mapping { + if strings.TrimSpace(target.Category) != "" || strings.TrimSpace(target.Subcategory) != "" { return true } } diff --git a/db/routes/plugin_system_sync_test.go b/db/routes/plugin_system_sync_test.go index 62f87aaf..1d21b9b6 100644 --- a/db/routes/plugin_system_sync_test.go +++ b/db/routes/plugin_system_sync_test.go @@ -1,6 +1,10 @@ package routes -import "testing" +import ( + "testing" + + "pocketbase/plugins/importer" +) func TestCategoryMappingPreservesExplicitEmptyMap(t *testing.T) { mapping := categoryMapping(map[string]any{ @@ -29,7 +33,22 @@ func TestCategoryMappingPreservesBlankProviderMapping(t *testing.T) { if mapping == nil { t.Fatal("expected category mapping") } - if value, ok := mapping["Ride"]; !ok || value != "" { + if value, ok := mapping["Ride"]; !ok || value != (importer.CategoryMappingValue{}) { t.Fatalf("expected blank provider mapping to be preserved, got %#v", mapping) } } + +func TestCategoryMappingParsesStructuredTarget(t *testing.T) { + mapping := categoryMapping(map[string]any{ + "categoryMapping": map[string]any{ + "TrailRun": map[string]any{ + "category": "Running", + "subcategory": "Trail", + }, + }, + }) + want := importer.CategoryMappingValue{Category: "Running", Subcategory: "Trail"} + if value, ok := mapping["TrailRun"]; !ok || value != want { + t.Fatalf("structured provider mapping = %#v, want %#v", mapping, want) + } +} diff --git a/db/routes/remote_trail.go b/db/routes/remote_trail.go index 3f9eeb84..a32fd8bf 100644 --- a/db/routes/remote_trail.go +++ b/db/routes/remote_trail.go @@ -237,15 +237,39 @@ func performFullSync(app core.App, ctx context.Context, reqURL *url.URL, localTr // --- Sub-Sync Helpers --- func syncTrailMetadata(app core.App, record *core.Record, data map[string]any) { - // Resolve Category if present in expand + var federatedCategoryName, federatedSubcategoryName string + if expand, ok := data["expand"].(map[string]any); ok { if cat, ok := expand["category"].(map[string]any); ok { if name, ok := cat["name"].(string); ok { - if c, _ := app.FindFirstRecordByData("categories", "name", name); c != nil { - record.Set("category", c.Id) - } + federatedCategoryName = name } } + if subcat, ok := expand["subcategory"].(map[string]any); ok { + if name, ok := subcat["name"].(string); ok { + federatedSubcategoryName = name + } + } + } + + if federatedCategoryName != "" { + record.Set("federated_category_name", federatedCategoryName) + } + if federatedSubcategoryName != "" { + record.Set("federated_subcategory_name", federatedSubcategoryName) + } + + category, subcategory, err := util.ResolveCategoryAndSubcategoryByNormalizedNames(app, federatedCategoryName, federatedSubcategoryName) + if err == nil && category != nil { + record.Set("category", category.Id) + if subcategory != nil { + record.Set("subcategory", subcategory.Id) + } else { + record.Set("subcategory", "") + } + } else if err == nil && federatedCategoryName != "" { + record.Set("category", "") + record.Set("subcategory", "") } // Resolve Tags @@ -260,8 +284,11 @@ func syncTrailMetadata(app core.App, record *core.Record, data map[string]any) { delete(data, "gpx") delete(data, "author") delete(data, "category") + delete(data, "subcategory") delete(data, "tags") delete(data, "iri") + delete(data, "federated_category_name") + delete(data, "federated_subcategory_name") record.Load(data) } diff --git a/db/util/activitypub.go b/db/util/activitypub.go index 74fde892..452614ec 100644 --- a/db/util/activitypub.go +++ b/db/util/activitypub.go @@ -173,7 +173,15 @@ func TrailFromActivity(activity pub.Activity, app core.App, actor *core.Record) } } else { // this trail exists already - // ensure that it is fully synced to catch waypoint/summit log updates + // keep searchable category metadata fresh from the update activity while + // still requiring a full sync to catch waypoint/summit log updates. + categoryMetadata, err := trailCategoryMetadataFromActivityObject(t) + if err != nil { + return nil, err + } + if err := applyTrailActivityCategoryMetadata(app, record, categoryMetadata); err != nil { + return nil, err + } record.Set("needs_full_sync", true) err = app.Save(record) @@ -185,22 +193,22 @@ func TrailFromActivity(activity pub.Activity, app core.App, actor *core.Record) } var distance, duration, elevation_gain, elevation_loss float64 - var diffculty, category string + var diffculty string trailTags := []string{} tags, err := pub.ToItemCollection(t.Tag) if err != nil { return nil, err } + categoryMetadata := trailCategoryMetadataFromTags(tags) for _, tag := range tags.Collection() { tagObj, err := pub.ToObject(tag) if err != nil { continue } + content := tagObj.Content.First().Value.String() switch tagObj.Name.First().Value.String() { - case "category": - category = content case "difficulty": diffculty = content case "elevation_gain": @@ -254,9 +262,8 @@ func TrailFromActivity(activity pub.Activity, app core.App, actor *core.Record) record.Set("author", actor.Id) record.Set("needs_full_sync", true) - categoryRecord, err := app.FindFirstRecordByData("categories", "name", category) - if err == nil { - record.Set("category", categoryRecord.Id) + if err := applyTrailActivityCategoryMetadata(app, record, categoryMetadata); err != nil { + return nil, err } if t.Attachment != nil { @@ -282,12 +289,12 @@ func TrailFromActivity(activity pub.Activity, app core.App, actor *core.Record) if len(photoURLs) > 0 { photos := []*filesystem.File{} - for i, purl := range photoURLs { + for _, purl := range photoURLs { photo, err := filesystem.NewFileFromURL(context.Background(), purl) if err != nil { continue } - photos[i] = photo + photos = append(photos, photo) } record.Set("photos", photos) @@ -306,6 +313,80 @@ func TrailFromActivity(activity pub.Activity, app core.App, actor *core.Record) return record, app.Save(record) } +type trailActivityCategoryMetadata struct { + category string + subcategory string + categorySet bool + subcategorySet bool +} + +func trailCategoryMetadataFromActivityObject(object *pub.Object) (trailActivityCategoryMetadata, error) { + if len(object.Tag) == 0 { + return trailActivityCategoryMetadata{}, nil + } + + tags, err := pub.ToItemCollection(object.Tag) + if err != nil { + return trailActivityCategoryMetadata{}, err + } + + return trailCategoryMetadataFromTags(tags), nil +} + +func trailCategoryMetadataFromTags(tags *pub.ItemCollection) trailActivityCategoryMetadata { + metadata := trailActivityCategoryMetadata{} + for _, tag := range tags.Collection() { + tagObj, err := pub.ToObject(tag) + if err != nil { + continue + } + + switch tagObj.Name.First().Value.String() { + case "category": + metadata.category = tagObj.Content.First().Value.String() + metadata.categorySet = true + case "subcategory": + metadata.subcategory = tagObj.Content.First().Value.String() + metadata.subcategorySet = true + } + } + + return metadata +} + +func applyTrailActivityCategoryMetadata(app core.App, record *core.Record, metadata trailActivityCategoryMetadata) error { + if metadata.categorySet { + record.Set("federated_category_name", metadata.category) + } + if metadata.subcategorySet { + record.Set("federated_subcategory_name", metadata.subcategory) + } else if metadata.categorySet { + record.Set("federated_subcategory_name", "") + } + + if !metadata.categorySet { + return nil + } + + categoryRecord, subcategoryRecord, err := ResolveCategoryAndSubcategoryByNormalizedNames(app, metadata.category, metadata.subcategory) + if err != nil { + return err + } + if categoryRecord != nil { + record.Set("category", categoryRecord.Id) + if subcategoryRecord != nil { + record.Set("subcategory", subcategoryRecord.Id) + } else { + record.Set("subcategory", "") + } + } else { + record.Set("category", "") + record.Set("subcategory", "") + } + + return nil +} + func ObjectFromTrail(app core.App, trail *core.Record, mentions *pub.ItemCollection) (*pub.Object, error) { origin := os.Getenv("ORIGIN") if origin == "" { @@ -320,9 +401,9 @@ func ObjectFromTrail(app core.App, trail *core.Record, mentions *pub.ItemCollect if len(errs) > 0 { return nil, fmt.Errorf("failed to expand tags: %v", errs) } - errs = app.ExpandRecord(trail, []string{"category"}, nil) + errs = app.ExpandRecord(trail, []string{"category", "subcategory"}, nil) if len(errs) > 0 { - return nil, fmt.Errorf("failed to expand category: %v", errs) + return nil, fmt.Errorf("failed to expand category/subcategory: %v", errs) } category := "" @@ -330,6 +411,11 @@ func ObjectFromTrail(app core.App, trail *core.Record, mentions *pub.ItemCollect if categoryRecord != nil { category = categoryRecord.GetString("name") } + subcategory := "" + subcategoryRecord := trail.ExpandedOne("subcategory") + if subcategoryRecord != nil { + subcategory = subcategoryRecord.GetString("name") + } tagRecords := trail.ExpandedAll("tags") @@ -372,6 +458,14 @@ func ObjectFromTrail(app core.App, trail *core.Record, mentions *pub.ItemCollect } } + if subcategory != "" { + tags.Append(pub.Object{ + Type: pub.NoteType, + Name: pub.NaturalLanguageValuesNew(pub.LangRefValueNew(pub.NilLangRef, "subcategory")), + Content: pub.NaturalLanguageValuesNew(pub.LangRefValueNew(pub.NilLangRef, subcategory)), + }) + } + for _, v := range tagRecords { hashtag := pub.ObjectNew(pub.NoteType) hashtag.Name = pub.NaturalLanguageValuesNew(pub.LangRefValueNew(pub.NilLangRef, "tag")) diff --git a/db/util/category.go b/db/util/category.go new file mode 100644 index 00000000..7270d0ac --- /dev/null +++ b/db/util/category.go @@ -0,0 +1,216 @@ +package util + +import ( + "encoding/json" + "fmt" + "strings" + "unicode" + + "github.com/pocketbase/pocketbase/core" + "github.com/pocketbase/pocketbase/tools/types" + "golang.org/x/text/cases" + "golang.org/x/text/language" + "golang.org/x/text/unicode/norm" +) + +type CategoryTranslation struct { + Name string `json:"name"` + ShortName string `json:"short_name"` +} + +func NormalizeCategoryName(name string) string { + decomposed := norm.NFD.String(name) + + var b strings.Builder + b.Grow(len(decomposed)) + for _, r := range decomposed { + if unicode.Is(unicode.Mn, r) { + continue + } + b.WriteRune(r) + } + + folded := cases.Fold().String(b.String()) + + b.Reset() + b.Grow(len(folded)) + lastWasSeparator := false + for _, r := range folded { + if unicode.IsSpace(r) || r == '-' || r == '_' { + if !lastWasSeparator { + b.WriteByte(' ') + lastWasSeparator = true + } + continue + } + + b.WriteRune(r) + lastWasSeparator = false + } + + return strings.TrimSpace(b.String()) +} + +func ParseCategoryTranslations(raw any) (map[string]CategoryTranslation, error) { + if raw == nil { + return nil, nil + } + + switch value := raw.(type) { + case map[string]CategoryTranslation: + return value, nil + case map[string]any: + return normalizeCategoryTranslations(value) + case types.JSONRaw: + if len(value) == 0 { + return nil, nil + } + + var decoded map[string]any + if err := json.Unmarshal(value, &decoded); err != nil { + return nil, fmt.Errorf("translations must be valid JSON: %w", err) + } + + return normalizeCategoryTranslations(decoded) + case []byte: + if len(value) == 0 { + return nil, nil + } + + var decoded map[string]any + if err := json.Unmarshal(value, &decoded); err != nil { + return nil, fmt.Errorf("translations must be valid JSON: %w", err) + } + + return normalizeCategoryTranslations(decoded) + case string: + if strings.TrimSpace(value) == "" { + return nil, nil + } + + var decoded map[string]any + if err := json.Unmarshal([]byte(value), &decoded); err != nil { + return nil, fmt.Errorf("translations must be valid JSON: %w", err) + } + + return normalizeCategoryTranslations(decoded) + default: + return nil, fmt.Errorf("translations must be a JSON object") + } +} + +func ValidateCategoryRecord(app core.App, record *core.Record) error { + name := record.GetString("name") + normalizedName := NormalizeCategoryName(name) + + allCategories, err := app.FindAllRecords("categories") + if err != nil { + return err + } + + for _, existing := range allCategories { + if existing.Id == record.Id { + continue + } + + if NormalizeCategoryName(existing.GetString("name")) == normalizedName { + return fmt.Errorf("category name %q collides with existing category %q after normalization", name, existing.GetString("name")) + } + } + + if _, err := ParseCategoryTranslations(record.Get("translations")); err != nil { + return err + } + + return nil +} + +func FindCategoryByNormalizedName(app core.App, name string) (*core.Record, error) { + normalizedName := NormalizeCategoryName(name) + if normalizedName == "" { + return nil, nil + } + + allCategories, err := app.FindAllRecords("categories") + if err != nil { + return nil, err + } + + for _, category := range allCategories { + if NormalizeCategoryName(category.GetString("name")) == normalizedName { + return category, nil + } + } + + return nil, nil +} + +func ValidateCategoryCollectionState(app core.App) error { + allCategories, err := app.FindAllRecords("categories") + if err != nil { + return err + } + + seen := map[string]string{} + for _, category := range allCategories { + name := category.GetString("name") + normalizedName := NormalizeCategoryName(name) + if other, ok := seen[normalizedName]; ok { + return fmt.Errorf("category normalization collision: %q conflicts with %q", name, other) + } + seen[normalizedName] = name + + if _, err := ParseCategoryTranslations(category.Get("translations")); err != nil { + return fmt.Errorf("invalid translations for category %q: %w", name, err) + } + } + + return nil +} + +func normalizeCategoryTranslations(raw map[string]any) (map[string]CategoryTranslation, error) { + if len(raw) == 0 { + return nil, nil + } + + translations := make(map[string]CategoryTranslation, len(raw)) + for locale, entry := range raw { + tag, err := language.Parse(locale) + if err != nil { + return nil, fmt.Errorf("translations locale %q is invalid", locale) + } + base, _ := tag.Base() + if locale != base.String() { + return nil, fmt.Errorf("translations locale %q must use the base locale %q", locale, base.String()) + } + if _, ok := supportedCategoryLocales[base.String()]; !ok { + return nil, fmt.Errorf("translations locale %q is not supported", locale) + } + + entryMap, ok := entry.(map[string]any) + if !ok { + return nil, fmt.Errorf("translations[%s] must be an object", locale) + } + + translation := CategoryTranslation{} + if name, ok := entryMap["name"]; ok { + nameString, ok := name.(string) + if !ok { + return nil, fmt.Errorf("translations[%s].name must be a string", locale) + } + translation.Name = nameString + } + + if shortName, ok := entryMap["short_name"]; ok { + shortNameString, ok := shortName.(string) + if !ok { + return nil, fmt.Errorf("translations[%s].short_name must be a string", locale) + } + translation.ShortName = shortNameString + } + + translations[locale] = translation + } + + return translations, nil +} diff --git a/db/util/category_defaults.go b/db/util/category_defaults.go new file mode 100644 index 00000000..b81b9750 --- /dev/null +++ b/db/util/category_defaults.go @@ -0,0 +1,289 @@ +package util + +import ( + "fmt" + + "github.com/pocketbase/pocketbase/core" +) + +var supportedCategoryLocales = map[string]struct{}{ + "cs": {}, + "de": {}, + "en": {}, + "es": {}, + "eu": {}, + "fr": {}, + "hu": {}, + "it": {}, + "nl": {}, + "no": {}, + "pl": {}, + "pt": {}, + "ru": {}, + "zh": {}, +} + +var defaultCategoryNames = []string{"Hiking", "Walking", "Running", "Climbing", "Skiing", "Canoeing", "Biking", "Other"} + +func DefaultCategoryNames() []string { + return append([]string(nil), defaultCategoryNames...) +} + +func SeedDefaultCategories(app core.App) error { + collection, err := app.FindCollectionByNameOrId("categories") + if err != nil { + return err + } + + allCategories, err := app.FindAllRecords("categories") + if err != nil { + return err + } + + existing := make(map[string]struct{}, len(allCategories)) + for _, category := range allCategories { + existing[NormalizeCategoryName(category.GetString("name"))] = struct{}{} + } + + for _, name := range defaultCategoryNames { + if _, ok := existing[NormalizeCategoryName(name)]; ok { + continue + } + + record := core.NewRecord(collection) + record.Set("name", name) + if collection.Fields.GetByName("settings") != nil { + record.Set("settings", defaultCategorySettings()) + } + if err := app.Save(record); err != nil { + return fmt.Errorf("failed to seed default category %q: %w", name, err) + } + } + + return nil +} + +func defaultCategorySettings() map[string]any { + return map[string]any{ + "wp_merge_enabled": true, + "wp_merge_radius": 50, + } +} + +var defaultCategoryTranslations = map[string]map[string]string{ + "Biking": { + "cs": "Cyklistika", + "de": "Radfahren", + "en": "Biking", + "es": "Ciclismo", + "eu": "Bizikleta", + "fr": "Vélo", + "hu": "Biking", + "it": "Ciclismo", + "nl": "Fietsen", + "no": "Sykling", + "pl": "Rower", + "pt": "Ciclismo", + "ru": "Велоспорт", + "zh": "骑行", + }, + "Canoeing": { + "cs": "Kanoistika", + "de": "Kanufahren", + "en": "Canoeing", + "es": "Remo", + "eu": "Kanoa", + "fr": "Canoë", + "hu": "Canoeing", + "it": "Canoa", + "nl": "Kanoën", + "no": "Padling", + "pl": "Kajak", + "pt": "Canoagem", + "ru": "Каякинг", + "zh": "划艇", + }, + "Climbing": { + "cs": "Horolezectví", + "de": "Klettern", + "en": "Climbing", + "es": "Escalada", + "eu": "Eskalada", + "fr": "Escalade", + "hu": "Climbing", + "it": "Arrampicata", + "nl": "Klimmen", + "no": "Klatring", + "pl": "Wspinaczka", + "pt": "Escalada", + "ru": "Скалолазание", + "zh": "攀岩", + }, + "Hiking": { + "cs": "Turistika", + "de": "Wandern", + "en": "Hiking", + "es": "Senderismo", + "eu": "Mendi-ibilaldia", + "fr": "Randonnée", + "hu": "Hiking", + "it": "Escursionismo", + "nl": "Hiken", + "no": "Vandring", + "pl": "Wędrówka", + "pt": "Montanhismo", + "ru": "Пеший туризм", + "zh": "徒步", + }, + "Other": { + "cs": "Ostatní", + "de": "Sonstiges", + "en": "Other", + "es": "Otros", + "eu": "Bestelakoak", + "fr": "Autre", + "hu": "Egyéb", + "it": "Altro", + "nl": "Overig", + "no": "Annet", + "pl": "Inne", + "pt": "Outros", + "ru": "Другое", + "zh": "其他", + }, + "Running": { + "cs": "Běh", + "de": "Laufen", + "en": "Running", + "es": "Carrera", + "eu": "Korrika", + "fr": "Course à pied", + "hu": "Futás", + "it": "Corsa", + "nl": "Hardlopen", + "no": "Løping", + "pl": "Bieganie", + "pt": "Corrida", + "ru": "Бег", + "zh": "跑步", + }, + "Skiing": { + "de": "Skifahren", + "no": "Skisport", + }, + "Walking": { + "cs": "Chůze", + "de": "Spazieren", + "en": "Walking", + "es": "Paseo", + "eu": "Oinez", + "fr": "Marche", + "hu": "Walking", + "it": "Camminare", + "nl": "Wandelen", + "no": "Gåtur", + "pl": "Spacer", + "pt": "Caminhada", + "ru": "Прогулка", + "zh": "步行", + }, +} + +var defaultCategoryIcons = map[string]string{ + "Biking": "person-biking", + "Canoeing": "sailboat", + "Climbing": "mountain", + "Hiking": "person-hiking", + "Other": "shapes", + "Running": "person-running", + "Skiing": "person-skiing-nordic", + "Walking": "person-walking", +} + +func PrepopulateDefaultCategoryTranslations(app core.App) error { + allCategories, err := app.FindAllRecords("categories") + if err != nil { + return err + } + + for _, category := range allCategories { + staticTranslations, ok := defaultCategoryTranslations[category.GetString("name")] + if !ok { + continue + } + + currentTranslations, err := ParseCategoryTranslations(category.Get("translations")) + if err != nil { + return fmt.Errorf("invalid existing translations for category %q: %w", category.GetString("name"), err) + } + mergedTranslations, changed := mergeDefaultCategoryTranslations(staticTranslations, currentTranslations) + if !changed { + continue + } + + category.Set("translations", mergedTranslations) + if err := app.Save(category); err != nil { + return fmt.Errorf("failed to prepopulate translations for category %q: %w", category.GetString("name"), err) + } + } + + return nil +} + +func PrepopulateDefaultCategoryIcons(app core.App) error { + collection, err := app.FindCollectionByNameOrId("categories") + if err != nil { + return err + } + if collection.Fields.GetByName("icon") == nil { + return nil + } + + allCategories, err := app.FindAllRecords("categories") + if err != nil { + return err + } + + for _, category := range allCategories { + categoryName := category.GetString("name") + defaultIcon, ok := defaultCategoryIcons[categoryName] + if !ok { + continue + } + + if category.GetString("icon") != "" { + continue + } + + category.Set("icon", defaultIcon) + if err := app.Save(category); err != nil { + return fmt.Errorf("failed to prepopulate icon for category %q: %w", category.GetString("name"), err) + } + } + + return nil +} + +func mergeDefaultCategoryTranslations(staticTranslations map[string]string, currentTranslations map[string]CategoryTranslation) (map[string]CategoryTranslation, bool) { + if currentTranslations == nil { + currentTranslations = map[string]CategoryTranslation{} + } + + changed := false + for locale, name := range staticTranslations { + if name == "" { + continue + } + + translation := currentTranslations[locale] + if translation.Name != "" { + continue + } + + translation.Name = name + currentTranslations[locale] = translation + changed = true + } + + return currentTranslations, changed +} diff --git a/db/util/category_preference.go b/db/util/category_preference.go new file mode 100644 index 00000000..fdea759f --- /dev/null +++ b/db/util/category_preference.go @@ -0,0 +1,241 @@ +package util + +import ( + "fmt" + + "github.com/pocketbase/pocketbase/core" +) + +const DefaultPriorityCategoryName = "Hiking" + +func ValidateUserCategoryPreferenceRequest(priorityExplicit bool) error { + if priorityExplicit { + return fmt.Errorf("category preference priority can only be changed through the reorder endpoint") + } + + return nil +} + +func ValidateUserSubcategoryPreferenceRequest(priorityExplicit bool) error { + if priorityExplicit { + return fmt.Errorf("subcategory preference priority can only be changed through the reorder endpoint") + } + + return nil +} + +func EnsureUserCategoryPriority(app core.App, userID, categoryID string) error { + if userID == "" { + return nil + } + + prioritized, err := app.FindRecordsByFilter( + "user_category_preferences", + "user = {:user} && priority > 0", + "", + 1, + 0, + map[string]any{"user": userID}, + ) + if err != nil { + return err + } + if len(prioritized) > 0 { + return nil + } + + if categoryID == "" { + category, err := FindCategoryByNormalizedName(app, DefaultPriorityCategoryName) + if err != nil { + return err + } + if category == nil { + return nil + } + categoryID = category.Id + } + + collection, err := app.FindCollectionByNameOrId("user_category_preferences") + if err != nil { + return err + } + + existing, err := app.FindRecordsByFilter( + "user_category_preferences", + "user = {:user} && category = {:category}", + "", + 1, + 0, + map[string]any{"user": userID, "category": categoryID}, + ) + if err != nil { + return err + } + + var record *core.Record + if len(existing) > 0 { + record = existing[0] + } else { + record = core.NewRecord(collection) + record.Set("user", userID) + record.Set("category", categoryID) + record.Set("visible", true) + } + + record.Set("priority", 1) + return app.SaveNoValidate(record) +} + +func ReorderUserCategoryPreferences(app core.App, userID string, categoryIDs []string) error { + if userID == "" { + return fmt.Errorf("authentication required") + } + + categories, err := app.FindAllRecords("categories") + if err != nil { + return err + } + + if len(categoryIDs) != len(categories) { + return fmt.Errorf("reorder request must include all categories") + } + + validCategories := make(map[string]struct{}, len(categories)) + for _, category := range categories { + validCategories[category.Id] = struct{}{} + } + + seen := make(map[string]struct{}, len(categoryIDs)) + for _, categoryID := range categoryIDs { + if _, ok := validCategories[categoryID]; !ok { + return fmt.Errorf("unknown category %q", categoryID) + } + if _, ok := seen[categoryID]; ok { + return fmt.Errorf("duplicate category %q", categoryID) + } + seen[categoryID] = struct{}{} + } + + return app.RunInTransaction(func(txApp core.App) error { + collection, err := txApp.FindCollectionByNameOrId("user_category_preferences") + if err != nil { + return err + } + + existing, err := txApp.FindRecordsByFilter( + "user_category_preferences", + "user = {:user}", + "", + 0, + 0, + map[string]any{"user": userID}, + ) + if err != nil { + return err + } + + byCategory := make(map[string]*core.Record, len(existing)) + for _, record := range existing { + byCategory[record.GetString("category")] = record + } + + for index, categoryID := range categoryIDs { + record := byCategory[categoryID] + if record == nil { + record = core.NewRecord(collection) + record.Set("user", userID) + record.Set("category", categoryID) + record.Set("visible", true) + } + + record.Set("priority", index+1) + if err := txApp.SaveNoValidate(record); err != nil { + return err + } + } + + return nil + }) +} + +func ReorderUserSubcategoryPreferences(app core.App, userID, categoryID string, subcategoryIDs []string) error { + if userID == "" { + return fmt.Errorf("authentication required") + } + if categoryID == "" { + return fmt.Errorf("category is required") + } + + subcategories, err := app.FindRecordsByFilter( + "subcategories", + "category = {:category}", + "", + 0, + 0, + map[string]any{"category": categoryID}, + ) + if err != nil { + return err + } + + if len(subcategoryIDs) != len(subcategories) { + return fmt.Errorf("reorder request must include all subcategories for the category") + } + + validSubcategories := make(map[string]struct{}, len(subcategories)) + for _, subcategory := range subcategories { + validSubcategories[subcategory.Id] = struct{}{} + } + + seen := make(map[string]struct{}, len(subcategoryIDs)) + for _, subcategoryID := range subcategoryIDs { + if _, ok := validSubcategories[subcategoryID]; !ok { + return fmt.Errorf("unknown subcategory %q", subcategoryID) + } + if _, ok := seen[subcategoryID]; ok { + return fmt.Errorf("duplicate subcategory %q", subcategoryID) + } + seen[subcategoryID] = struct{}{} + } + + return app.RunInTransaction(func(txApp core.App) error { + collection, err := txApp.FindCollectionByNameOrId("user_subcategory_preferences") + if err != nil { + return err + } + + existing, err := txApp.FindRecordsByFilter( + "user_subcategory_preferences", + "user = {:user}", + "", + 0, + 0, + map[string]any{"user": userID}, + ) + if err != nil { + return err + } + + bySubcategory := make(map[string]*core.Record, len(existing)) + for _, record := range existing { + bySubcategory[record.GetString("subcategory")] = record + } + + for index, subcategoryID := range subcategoryIDs { + record := bySubcategory[subcategoryID] + if record == nil { + record = core.NewRecord(collection) + record.Set("user", userID) + record.Set("subcategory", subcategoryID) + record.Set("visible", true) + } + + record.Set("priority", index+1) + if err := txApp.SaveNoValidate(record); err != nil { + return err + } + } + + return nil + }) +} diff --git a/db/util/category_test.go b/db/util/category_test.go new file mode 100644 index 00000000..a2245f8d --- /dev/null +++ b/db/util/category_test.go @@ -0,0 +1,1301 @@ +package util + +import ( + "reflect" + "strings" + "testing" + + "github.com/pocketbase/pocketbase/core" + pbtests "github.com/pocketbase/pocketbase/tests" + "github.com/pocketbase/pocketbase/tools/types" +) + +func TestNormalizeCategoryName(t *testing.T) { + t.Run("WhitespaceAndSeparators", func(t *testing.T) { + for _, value := range []string{"E-Bike", "E Bike", "e-bike"} { + if got := NormalizeCategoryName(value); got != "e bike" { + t.Fatalf("NormalizeCategoryName(%q) = %q, want %q", value, got, "e bike") + } + } + + if got := NormalizeCategoryName(" Mountain Biking "); got != "mountain biking" { + t.Fatalf("NormalizeCategoryName(%q) = %q, want %q", " Mountain Biking ", got, "mountain biking") + } + }) + + t.Run("AccentFolding", func(t *testing.T) { + for _, value := range []string{"Canoë", "Canoe"} { + if got := NormalizeCategoryName(value); got != "canoe" { + t.Fatalf("NormalizeCategoryName(%q) = %q, want %q", value, got, "canoe") + } + } + }) + + t.Run("SeparatorsNotRemoved", func(t *testing.T) { + if got := NormalizeCategoryName("EBike"); got != "ebike" { + t.Fatalf("NormalizeCategoryName(%q) = %q, want %q", "EBike", got, "ebike") + } + + if NormalizeCategoryName("EBike") == NormalizeCategoryName("E Bike") { + t.Fatalf("expected %q and %q to normalize differently", "EBike", "E Bike") + } + }) +} + +func TestParseCategoryTranslations(t *testing.T) { + tests := []struct { + name string + input types.JSONRaw + wantErr bool + want map[string]CategoryTranslation + }{ + { + name: "valid base locale", + input: types.JSONRaw(`{"de": {"name": "Wandern", "short_name": "WAND"}}`), + want: map[string]CategoryTranslation{"de": {Name: "Wandern", ShortName: "WAND"}}, + }, + { + name: "null is nil without error", + input: types.JSONRaw(`null`), + want: nil, + }, + { + name: "region locale rejected", + input: types.JSONRaw(`{"pt-BR": {"name": "Caminhada"}}`), + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ParseCategoryTranslations(tt.input) + if tt.wantErr { + if err == nil { + t.Fatal("ParseCategoryTranslations() error = nil, want error") + } + return + } + if err != nil { + t.Fatalf("ParseCategoryTranslations() error = %v", err) + } + if !reflect.DeepEqual(got, tt.want) { + t.Fatalf("ParseCategoryTranslations() = %#v, want %#v", got, tt.want) + } + }) + } +} + +func TestDefaultCategoryTranslationsAreSupportedAndNonEmpty(t *testing.T) { + for _, category := range DefaultCategoryNames() { + translations, ok := defaultCategoryTranslations[category] + if !ok { + t.Fatalf("defaultCategoryTranslations missing category %q", category) + } + + for locale, name := range translations { + if _, ok := supportedCategoryLocales[locale]; !ok { + t.Fatalf("defaultCategoryTranslations[%q] contains unsupported locale %q", category, locale) + } + + if name == "" { + t.Fatalf("defaultCategoryTranslations[%q][%q] is empty", category, locale) + } + } + } +} + +func TestDefaultCategoryIconsAreNonEmpty(t *testing.T) { + for _, category := range DefaultCategoryNames() { + icon, ok := defaultCategoryIcons[category] + if !ok { + t.Fatalf("defaultCategoryIcons missing category %q", category) + } + + if strings.TrimSpace(icon) == "" { + t.Fatalf("defaultCategoryIcons[%q] is empty", category) + } + } +} + +func TestSeedDefaultCategoriesAddsMissingDefaults(t *testing.T) { + app := setupCategoryValidationTestApp(t) + defer app.Cleanup() + + running := createTestCategory(t, app, "Running") + + if err := SeedDefaultCategories(app); err != nil { + t.Fatalf("SeedDefaultCategories() error = %v", err) + } + + categories, err := app.FindAllRecords("categories") + if err != nil { + t.Fatal(err) + } + + byName := map[string]*core.Record{} + for _, category := range categories { + byName[category.GetString("name")] = category + } + + for _, name := range DefaultCategoryNames() { + if byName[name] == nil { + t.Fatalf("default category %q was not seeded", name) + } + } + if byName["Running"].Id != running.Id { + t.Fatalf("existing Running category was replaced: got %q, want %q", byName["Running"].Id, running.Id) + } + if len(categories) != len(DefaultCategoryNames()) { + t.Fatalf("category count = %d, want %d", len(categories), len(DefaultCategoryNames())) + } +} + +func TestMergeDefaultCategoryTranslations(t *testing.T) { + merged, changed := mergeDefaultCategoryTranslations( + map[string]string{ + "de": "Wandern", + "en": "Hiking", + "fr": "", + }, + map[string]CategoryTranslation{ + "de": { + Name: "Custom Wandern", + ShortName: "WAND", + }, + }, + ) + + if !changed { + t.Fatal("mergeDefaultCategoryTranslations changed = false, want true") + } + + want := map[string]CategoryTranslation{ + "de": { + Name: "Custom Wandern", + ShortName: "WAND", + }, + "en": { + Name: "Hiking", + }, + } + if !reflect.DeepEqual(merged, want) { + t.Fatalf("mergeDefaultCategoryTranslations() = %#v, want %#v", merged, want) + } +} + +func TestMergeDefaultSubcategoryTranslations(t *testing.T) { + merged, changed := mergeDefaultSubcategoryTranslations( + map[string]CategoryTranslation{ + "de": { + Name: "Rennrad", + ShortName: "ROAD", + }, + "en": { + Name: "Road", + ShortName: "ROAD", + }, + }, + map[string]CategoryTranslation{ + "de": { + Name: "Custom Road", + ShortName: "ROADX", + }, + "en": { + Name: "Road", + }, + }, + ) + + if !changed { + t.Fatal("mergeDefaultSubcategoryTranslations changed = false, want true") + } + + want := map[string]CategoryTranslation{ + "de": { + Name: "Custom Road", + ShortName: "ROADX", + }, + "en": { + Name: "Road", + ShortName: "ROAD", + }, + } + if !reflect.DeepEqual(merged, want) { + t.Fatalf("mergeDefaultSubcategoryTranslations() = %#v, want %#v", merged, want) + } +} + +func TestPrepopulateDefaultCategoryIcons(t *testing.T) { + app := setupCategoryValidationTestApp(t) + defer app.Cleanup() + + hiking := createTestCategory(t, app, "Hiking") + walking := createTestCategory(t, app, "Walking") + canoeing := createTestCategory(t, app, "Canoeing") + walking.Set("icon", "custom-icon") + if err := app.Save(walking); err != nil { + t.Fatal(err) + } + canoeing.Set("icon", "ship") + if err := app.Save(canoeing); err != nil { + t.Fatal(err) + } + + if err := PrepopulateDefaultCategoryIcons(app); err != nil { + t.Fatalf("PrepopulateDefaultCategoryIcons() error = %v", err) + } + + hiking, err := app.FindRecordById("categories", hiking.Id) + if err != nil { + t.Fatal(err) + } + walking, err = app.FindRecordById("categories", walking.Id) + if err != nil { + t.Fatal(err) + } + canoeing, err = app.FindRecordById("categories", canoeing.Id) + if err != nil { + t.Fatal(err) + } + + if got := hiking.GetString("icon"); got != defaultCategoryIcons["Hiking"] { + t.Fatalf("hiking icon = %q, want %q", got, defaultCategoryIcons["Hiking"]) + } + if got := walking.GetString("icon"); got != "custom-icon" { + t.Fatalf("walking icon = %q, want custom icon", got) + } + if got := canoeing.GetString("icon"); got != "ship" { + t.Fatalf("canoeing icon = %q, want existing icon", got) + } +} + +func TestDefaultSubcategories(t *testing.T) { + seen := map[string]struct{}{} + wantDefaults := map[string]struct{}{ + "Hiking/Winter": {}, + "Hiking/Alpine": {}, + "Hiking/Long-distance": {}, + "Hiking/Snowshoeing": {}, + "Hiking/Family": {}, + "Hiking/Pilgrimage": {}, + "Biking/Touring": {}, + "Running/Trail": {}, + "Running/Road": {}, + "Skiing/Cross-country": {}, + "Skiing/Skating": {}, + "Skiing/Backcountry": {}, + } + wantBadgeIcons := map[string]string{ + "Biking/MTB": "mountain", + "Biking/Road": "grip-lines-vertical", + "Biking/E-Bike": "bolt", + "Hiking/Winter": "snowflake", + "Hiking/Alpine": "mountain", + "Hiking/Snowshoeing": "snowflake", + "Hiking/Family": "child", + "Hiking/Pilgrimage": "cross", + "Running/Road": "grip-lines-vertical", + } + for _, subcategory := range defaultSubcategories { + if subcategory.parentCategory == "" { + t.Fatalf("default subcategory %q parent is empty", subcategory.name) + } + if subcategory.name == "" { + t.Fatal("default subcategory name is empty") + } + if subcategory.shortName == "" { + t.Fatalf("default subcategory %q shortName is empty", subcategory.name) + } + + normalizedName := NormalizeCategoryName(subcategory.name) + key := subcategory.parentCategory + "/" + normalizedName + if _, ok := seen[key]; ok { + t.Fatalf("default subcategory %q duplicates normalized key %q", subcategory.name, key) + } + seen[key] = struct{}{} + + if _, ok := wantDefaults[subcategory.parentCategory+"/"+subcategory.name]; ok { + delete(wantDefaults, subcategory.parentCategory+"/"+subcategory.name) + } + if wantBadgeIcon, ok := wantBadgeIcons[subcategory.parentCategory+"/"+subcategory.name]; ok { + if subcategory.badgeIcon != wantBadgeIcon { + t.Fatalf("%s/%s badgeIcon = %q, want %q", subcategory.parentCategory, subcategory.name, subcategory.badgeIcon, wantBadgeIcon) + } + } + if subcategory.parentCategory == "Hiking" && subcategory.name == "Winter" { + if subcategory.translations["de"].Name != "Winterwandern" { + t.Fatalf("Winter de translation = %q, want Winterwandern", subcategory.translations["de"].Name) + } + } + if subcategory.parentCategory == "Hiking" && subcategory.name == "Pilgrimage" { + if subcategory.translations["de"].Name != "Pilgern" { + t.Fatalf("Pilgrimage de translation = %q, want Pilgern", subcategory.translations["de"].Name) + } + } + if subcategory.parentCategory == "Biking" && subcategory.name == "Touring" { + if subcategory.translations["de"].Name != "Tourenrad" { + t.Fatalf("Touring de translation = %q, want Tourenrad", subcategory.translations["de"].Name) + } + } + } + + if len(wantDefaults) > 0 { + t.Fatalf("default subcategories missing entries: %#v", wantDefaults) + } +} + +func TestValidateSubcategoryRecord(t *testing.T) { + app := setupCategoryValidationTestApp(t) + defer app.Cleanup() + + biking := createTestCategory(t, app, "Biking") + hiking := createTestCategory(t, app, "Hiking") + createTestSubcategory(t, app, biking.Id, "MTB", "MTB") + + subcategoriesCollection := mustFindTestCollection(t, app, "subcategories") + + tests := []struct { + name string + category string + record string + wantError bool + }{ + { + name: "requires parent category", + category: "", + record: "Road", + wantError: true, + }, + { + name: "rejects normalized collision in same parent", + category: biking.Id, + record: "mtb", + wantError: true, + }, + { + name: "allows same normalized name in different parent", + category: hiking.Id, + record: "mtb", + }, + { + name: "allows unique name in same parent", + category: biking.Id, + record: "Road", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + record := core.NewRecord(subcategoriesCollection) + record.Set("category", tt.category) + record.Set("name", tt.record) + + err := ValidateSubcategoryRecord(app, record) + if (err != nil) != tt.wantError { + t.Fatalf("ValidateSubcategoryRecord() error = %v, wantError %v", err, tt.wantError) + } + }) + } +} + +func TestValidateTrailSubcategoryRecord(t *testing.T) { + app := setupCategoryValidationTestApp(t) + defer app.Cleanup() + + biking := createTestCategory(t, app, "Biking") + hiking := createTestCategory(t, app, "Hiking") + mtb := createTestSubcategory(t, app, biking.Id, "MTB", "MTB") + + trailsCollection := mustFindTestCollection(t, app, "trails") + missingSubcategoryID := "missing1234567" + + tests := []struct { + name string + category string + subcategory string + subcategoryExplicit bool + wantSubcategory string + wantError bool + }{ + { + name: "allows empty subcategory", + }, + { + name: "requires category when subcategory is explicit", + subcategory: mtb.Id, + subcategoryExplicit: true, + wantSubcategory: mtb.Id, + wantError: true, + }, + { + name: "clears subcategory without category when subcategory is implicit", + subcategory: mtb.Id, + wantSubcategory: "", + }, + { + name: "allows matching parent category", + category: biking.Id, + subcategory: mtb.Id, + subcategoryExplicit: true, + wantSubcategory: mtb.Id, + }, + { + name: "rejects mismatched explicit subcategory", + category: hiking.Id, + subcategory: mtb.Id, + subcategoryExplicit: true, + wantSubcategory: mtb.Id, + wantError: true, + }, + { + name: "clears mismatched implicit subcategory", + category: hiking.Id, + subcategory: mtb.Id, + wantSubcategory: "", + }, + { + name: "rejects missing explicit subcategory", + category: biking.Id, + subcategory: missingSubcategoryID, + subcategoryExplicit: true, + wantSubcategory: missingSubcategoryID, + wantError: true, + }, + { + name: "clears missing implicit subcategory", + category: biking.Id, + subcategory: missingSubcategoryID, + wantSubcategory: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + record := core.NewRecord(trailsCollection) + record.Set("category", tt.category) + record.Set("subcategory", tt.subcategory) + + err := ValidateTrailSubcategoryRecord(app, record, tt.subcategoryExplicit) + if (err != nil) != tt.wantError { + t.Fatalf("ValidateTrailSubcategoryRecord() error = %v, wantError %v", err, tt.wantError) + } + + if got := record.GetString("subcategory"); got != tt.wantSubcategory { + t.Fatalf("record subcategory = %q, want %q", got, tt.wantSubcategory) + } + }) + } +} + +func TestSeedDefaultSubcategoriesSkipsNormalizedExisting(t *testing.T) { + app := setupCategoryValidationTestApp(t) + defer app.Cleanup() + + biking := createTestCategory(t, app, "Biking") + mtb := createTestSubcategory(t, app, biking.Id, "mtb", "CUSTOM") + + if err := SeedDefaultSubcategories(app); err != nil { + t.Fatalf("SeedDefaultSubcategories() error = %v", err) + } + + records, err := app.FindRecordsByFilter( + "subcategories", + "category = {:category}", + "", + 0, + 0, + map[string]any{"category": biking.Id}, + ) + if err != nil { + t.Fatal(err) + } + + var normalizedMTBCount int + for _, record := range records { + if NormalizeCategoryName(record.GetString("name")) == NormalizeCategoryName("MTB") { + normalizedMTBCount++ + } + } + + if normalizedMTBCount != 1 { + t.Fatalf("normalized MTB subcategory count = %d, want 1", normalizedMTBCount) + } + + updatedMTB, err := app.FindRecordById("subcategories", mtb.Id) + if err != nil { + t.Fatal(err) + } + if got := updatedMTB.GetString("name"); got != "mtb" { + t.Fatalf("existing MTB subcategory name = %q, want mtb", got) + } + if got := updatedMTB.GetString("short_name"); got != "CUSTOM" { + t.Fatalf("existing MTB short_name = %q, want CUSTOM", got) + } + + defaultBikingSubcategories := 0 + for _, subcategory := range defaultSubcategories { + if subcategory.parentCategory == "Biking" { + defaultBikingSubcategories++ + } + } + + if len(records) != defaultBikingSubcategories { + t.Fatalf("seeded Biking subcategory count = %d, want %d", len(records), defaultBikingSubcategories) + } +} + +func TestResolveCategoryAndSubcategoryByNormalizedNames(t *testing.T) { + app := setupCategoryValidationTestApp(t) + defer app.Cleanup() + + biking := createTestCategory(t, app, "Biking") + mtb := createTestSubcategory(t, app, biking.Id, "E-Bike", "EBIKE") + hiking := createTestCategory(t, app, "Hiking") + createTestSubcategory(t, app, hiking.Id, "E-Bike", "EBIKE") + + category, subcategory, err := ResolveCategoryAndSubcategoryByNormalizedNames(app, " biking ", "e bike") + if err != nil { + t.Fatalf("ResolveCategoryAndSubcategoryByNormalizedNames() error = %v", err) + } + if category == nil || category.Id != biking.Id { + t.Fatalf("category = %#v, want Biking", category) + } + if subcategory == nil || subcategory.Id != mtb.Id { + t.Fatalf("subcategory = %#v, want Biking/E-Bike", subcategory) + } + + category, subcategory, err = ResolveCategoryAndSubcategoryByNormalizedNames(app, "Biking", "Bikepacking") + if err != nil { + t.Fatalf("ResolveCategoryAndSubcategoryByNormalizedNames() unknown subcategory error = %v", err) + } + if category == nil || category.Id != biking.Id { + t.Fatalf("unknown subcategory category = %#v, want Biking", category) + } + if subcategory != nil { + t.Fatalf("unknown subcategory = %#v, want nil", subcategory) + } + + category, subcategory, err = ResolveCategoryAndSubcategoryByNormalizedNames(app, "Skydiving", "Wingsuit") + if err != nil { + t.Fatalf("ResolveCategoryAndSubcategoryByNormalizedNames() unknown category error = %v", err) + } + if category != nil || subcategory != nil { + t.Fatalf("unknown category resolved category=%#v subcategory=%#v, want nil/nil", category, subcategory) + } +} + +func TestBackfillRemoteTrailCategoryBackfillsTargetCategoryOnly(t *testing.T) { + app := setupCategoryValidationTestApp(t) + defer app.Cleanup() + + biking := createTestCategory(t, app, "Biking") + gravel := createTestSubcategory(t, app, biking.Id, "Gravel", "GRVL") + matchingTrail := createTestTrail(t, app, "", "", "biking", "gravel") + otherTrail := createTestTrail(t, app, "", "", "Hiking", "gravel") + + if err := BackfillRemoteTrailCategory(app, biking); err != nil { + t.Fatalf("BackfillRemoteTrailCategory() error = %v", err) + } + + updatedMatchingTrail, err := app.FindRecordById("trails", matchingTrail.Id) + if err != nil { + t.Fatal(err) + } + if got := updatedMatchingTrail.GetString("category"); got != biking.Id { + t.Fatalf("matching trail category = %q, want %q", got, biking.Id) + } + if got := updatedMatchingTrail.GetString("subcategory"); got != gravel.Id { + t.Fatalf("matching trail subcategory = %q, want %q", got, gravel.Id) + } + + updatedOtherTrail, err := app.FindRecordById("trails", otherTrail.Id) + if err != nil { + t.Fatal(err) + } + if got := updatedOtherTrail.GetString("category"); got != "" { + t.Fatalf("other trail category = %q, want empty", got) + } + if got := updatedOtherTrail.GetString("subcategory"); got != "" { + t.Fatalf("other trail subcategory = %q, want empty", got) + } +} + +func TestBackfillRemoteTrailSubcategoryBackfillsWithinParentScope(t *testing.T) { + app := setupCategoryValidationTestApp(t) + defer app.Cleanup() + + biking := createTestCategory(t, app, "Biking") + hiking := createTestCategory(t, app, "Hiking") + bikingGravel := createTestSubcategory(t, app, biking.Id, "Gravel", "GRVL") + createTestSubcategory(t, app, hiking.Id, "Gravel", "GRVL") + + existingParentTrail := createTestTrail(t, app, biking.Id, "", "Biking", "gravel") + emptyParentTrail := createTestTrail(t, app, "", "", "biking", "gravel") + wrongParentTrail := createTestTrail(t, app, hiking.Id, "", "Hiking", "gravel") + + if err := BackfillRemoteTrailSubcategory(app, bikingGravel); err != nil { + t.Fatalf("BackfillRemoteTrailSubcategory() error = %v", err) + } + + updatedExistingParentTrail, err := app.FindRecordById("trails", existingParentTrail.Id) + if err != nil { + t.Fatal(err) + } + if got := updatedExistingParentTrail.GetString("category"); got != biking.Id { + t.Fatalf("existing parent trail category = %q, want %q", got, biking.Id) + } + if got := updatedExistingParentTrail.GetString("subcategory"); got != bikingGravel.Id { + t.Fatalf("existing parent trail subcategory = %q, want %q", got, bikingGravel.Id) + } + + updatedEmptyParentTrail, err := app.FindRecordById("trails", emptyParentTrail.Id) + if err != nil { + t.Fatal(err) + } + if got := updatedEmptyParentTrail.GetString("category"); got != biking.Id { + t.Fatalf("empty parent trail category = %q, want %q", got, biking.Id) + } + if got := updatedEmptyParentTrail.GetString("subcategory"); got != bikingGravel.Id { + t.Fatalf("empty parent trail subcategory = %q, want %q", got, bikingGravel.Id) + } + + updatedWrongParentTrail, err := app.FindRecordById("trails", wrongParentTrail.Id) + if err != nil { + t.Fatal(err) + } + if got := updatedWrongParentTrail.GetString("category"); got != hiking.Id { + t.Fatalf("wrong parent trail category = %q, want %q", got, hiking.Id) + } + if got := updatedWrongParentTrail.GetString("subcategory"); got != "" { + t.Fatalf("wrong parent trail subcategory = %q, want empty", got) + } +} + +func TestSeedDefaultSubcategoriesRenamesLegacyDefault(t *testing.T) { + app := setupCategoryValidationTestApp(t) + defer app.Cleanup() + + hiking := createTestCategory(t, app, "Hiking") + winter := createTestSubcategory(t, app, hiking.Id, "Winter Hiking", "WINT") + + if err := SeedDefaultSubcategories(app); err != nil { + t.Fatalf("SeedDefaultSubcategories() error = %v", err) + } + if err := SeedDefaultSubcategories(app); err != nil { + t.Fatalf("second SeedDefaultSubcategories() error = %v", err) + } + + records, err := app.FindRecordsByFilter( + "subcategories", + "category = {:category} && short_name = 'WINT'", + "", + 0, + 0, + map[string]any{"category": hiking.Id}, + ) + if err != nil { + t.Fatal(err) + } + + if len(records) != 1 { + t.Fatalf("Winter subcategory count = %d, want 1", len(records)) + } + if got := records[0].GetString("name"); got != "Winter" { + t.Fatalf("Winter subcategory name = %q, want Winter", got) + } + if records[0].Id != winter.Id { + t.Fatalf("Winter subcategory id = %q, want %q", records[0].Id, winter.Id) + } + if got := records[0].GetString("badge_icon"); got != "snowflake" { + t.Fatalf("Winter badge_icon = %q, want snowflake", got) + } +} + +func TestSeedDefaultSubcategoriesDoesNotOverwriteBadgeIcon(t *testing.T) { + app := setupCategoryValidationTestApp(t) + defer app.Cleanup() + + hiking := createTestCategory(t, app, "Hiking") + winter := createTestSubcategory(t, app, hiking.Id, "Winter", "WINT") + winter.Set("badge_icon", "custom") + if err := app.Save(winter); err != nil { + t.Fatal(err) + } + + if err := SeedDefaultSubcategories(app); err != nil { + t.Fatalf("SeedDefaultSubcategories() error = %v", err) + } + + updatedWinter, err := app.FindRecordById("subcategories", winter.Id) + if err != nil { + t.Fatal(err) + } + if got := updatedWinter.GetString("badge_icon"); got != "custom" { + t.Fatalf("Winter badge_icon = %q, want custom", got) + } +} + +func TestSeedDefaultSubcategoriesDoesNotRenameAliasWhenCanonicalExists(t *testing.T) { + app := setupCategoryValidationTestApp(t) + defer app.Cleanup() + + hiking := createTestCategory(t, app, "Hiking") + winter := createTestSubcategory(t, app, hiking.Id, "Winter", "WINT") + legacy := createTestSubcategory(t, app, hiking.Id, "Winter Hiking", "WHIKE") + + if err := SeedDefaultSubcategories(app); err != nil { + t.Fatalf("SeedDefaultSubcategories() error = %v", err) + } + + updatedWinter, err := app.FindRecordById("subcategories", winter.Id) + if err != nil { + t.Fatal(err) + } + if got := updatedWinter.GetString("name"); got != "Winter" { + t.Fatalf("canonical subcategory name = %q, want Winter", got) + } + + updatedLegacy, err := app.FindRecordById("subcategories", legacy.Id) + if err != nil { + t.Fatal(err) + } + if got := updatedLegacy.GetString("name"); got != "Winter Hiking" { + t.Fatalf("legacy subcategory name = %q, want Winter Hiking", got) + } + + records, err := app.FindRecordsByFilter( + "subcategories", + "category = {:category} && name = 'Winter'", + "", + 0, + 0, + map[string]any{"category": hiking.Id}, + ) + if err != nil { + t.Fatal(err) + } + if len(records) != 1 { + t.Fatalf("canonical Winter subcategory count = %d, want 1", len(records)) + } +} + +func TestValidateUserCategoryPreferenceRequest(t *testing.T) { + tests := []struct { + name string + priorityExplicit bool + wantError bool + }{ + { + name: "allows requests without priority", + }, + { + name: "rejects explicit priority", + priorityExplicit: true, + wantError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := ValidateUserCategoryPreferenceRequest(tt.priorityExplicit) + if (err != nil) != tt.wantError { + t.Fatalf("ValidateUserCategoryPreferenceRequest() error = %v, wantError %v", err, tt.wantError) + } + }) + } +} + +func TestValidateUserSubcategoryPreferenceRequest(t *testing.T) { + tests := []struct { + name string + priorityExplicit bool + wantError bool + }{ + { + name: "allows requests without priority", + }, + { + name: "rejects explicit priority", + priorityExplicit: true, + wantError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := ValidateUserSubcategoryPreferenceRequest(tt.priorityExplicit) + if (err != nil) != tt.wantError { + t.Fatalf("ValidateUserSubcategoryPreferenceRequest() error = %v, wantError %v", err, tt.wantError) + } + }) + } +} + +func TestReorderUserCategoryPreferencesValidation(t *testing.T) { + tests := []struct { + name string + userID string + categoryIDs func(categories []*core.Record) []string + wantError bool + }{ + { + name: "requires authenticated user", + userID: "", + categoryIDs: func(categories []*core.Record) []string { + return []string{categories[0].Id, categories[1].Id} + }, + wantError: true, + }, + { + name: "rejects incomplete list", + userID: "user12345678901", + categoryIDs: func(categories []*core.Record) []string { + return []string{categories[0].Id} + }, + wantError: true, + }, + { + name: "rejects duplicate category", + userID: "user12345678901", + categoryIDs: func(categories []*core.Record) []string { + return []string{categories[0].Id, categories[0].Id} + }, + wantError: true, + }, + { + name: "rejects unknown category", + userID: "user12345678901", + categoryIDs: func(categories []*core.Record) []string { + return []string{categories[0].Id, "unknown12345678"} + }, + wantError: true, + }, + { + name: "allows complete category list", + userID: "user12345678901", + categoryIDs: func(categories []*core.Record) []string { + return []string{categories[1].Id, categories[0].Id} + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + app := setupCategoryValidationTestApp(t) + defer app.Cleanup() + + categories := []*core.Record{ + createTestCategory(t, app, "Biking"), + createTestCategory(t, app, "Hiking"), + } + + err := ReorderUserCategoryPreferences(app, tt.userID, tt.categoryIDs(categories)) + if (err != nil) != tt.wantError { + t.Fatalf("ReorderUserCategoryPreferences() error = %v, wantError %v", err, tt.wantError) + } + }) + } +} + +func TestReorderUserSubcategoryPreferencesValidation(t *testing.T) { + tests := []struct { + name string + userID string + categoryID func(categories []*core.Record) string + subcategoryIDs func(subcategories []*core.Record) []string + wantError bool + }{ + { + name: "requires authenticated user", + categoryID: func(categories []*core.Record) string { + return categories[0].Id + }, + subcategoryIDs: func(subcategories []*core.Record) []string { + return []string{subcategories[0].Id, subcategories[1].Id} + }, + wantError: true, + }, + { + name: "requires category", + userID: "user12345678901", + categoryID: func(categories []*core.Record) string { + return "" + }, + subcategoryIDs: func(subcategories []*core.Record) []string { + return []string{subcategories[0].Id, subcategories[1].Id} + }, + wantError: true, + }, + { + name: "rejects incomplete list", + userID: "user12345678901", + categoryID: func(categories []*core.Record) string { + return categories[0].Id + }, + subcategoryIDs: func(subcategories []*core.Record) []string { + return []string{subcategories[0].Id} + }, + wantError: true, + }, + { + name: "rejects duplicate subcategory", + userID: "user12345678901", + categoryID: func(categories []*core.Record) string { + return categories[0].Id + }, + subcategoryIDs: func(subcategories []*core.Record) []string { + return []string{subcategories[0].Id, subcategories[0].Id} + }, + wantError: true, + }, + { + name: "rejects subcategory from another category", + userID: "user12345678901", + categoryID: func(categories []*core.Record) string { + return categories[0].Id + }, + subcategoryIDs: func(subcategories []*core.Record) []string { + return []string{subcategories[0].Id, subcategories[2].Id} + }, + wantError: true, + }, + { + name: "allows complete subcategory list", + userID: "user12345678901", + categoryID: func(categories []*core.Record) string { + return categories[0].Id + }, + subcategoryIDs: func(subcategories []*core.Record) []string { + return []string{subcategories[1].Id, subcategories[0].Id} + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + app := setupCategoryValidationTestApp(t) + defer app.Cleanup() + + categories := []*core.Record{ + createTestCategory(t, app, "Hiking"), + createTestCategory(t, app, "Biking"), + } + subcategories := []*core.Record{ + createTestSubcategory(t, app, categories[0].Id, "Mountain Hiking", "", ""), + createTestSubcategory(t, app, categories[0].Id, "Family Hiking", "", ""), + createTestSubcategory(t, app, categories[1].Id, "E-Bike", "", ""), + } + + err := ReorderUserSubcategoryPreferences(app, tt.userID, tt.categoryID(categories), tt.subcategoryIDs(subcategories)) + if (err != nil) != tt.wantError { + t.Fatalf("ReorderUserSubcategoryPreferences() error = %v, wantError %v", err, tt.wantError) + } + }) + } +} + +func TestEnsureUserCategoryPriorityDefaultsToHiking(t *testing.T) { + app := setupCategoryValidationTestApp(t) + defer app.Cleanup() + + createTestCategory(t, app, "Canoeing") + hiking := createTestCategory(t, app, "Hiking") + userID := "user12345678901" + + if err := EnsureUserCategoryPriority(app, userID, ""); err != nil { + t.Fatalf("EnsureUserCategoryPriority() error = %v", err) + } + + records, err := app.FindRecordsByFilter( + "user_category_preferences", + "user = {:user}", + "", + 0, + 0, + map[string]any{"user": userID}, + ) + if err != nil { + t.Fatal(err) + } + if len(records) != 1 { + t.Fatalf("preference count = %d, want 1", len(records)) + } + + record := records[0] + if got := record.GetString("category"); got != hiking.Id { + t.Fatalf("category = %q, want Hiking id %q", got, hiking.Id) + } + if got := record.GetInt("priority"); got != 1 { + t.Fatalf("priority = %d, want 1", got) + } + if !record.GetBool("visible") { + t.Fatal("visible = false, want true") + } +} + +func TestReorderUserCategoryPreferencesUpsertsAndRenumbers(t *testing.T) { + app := setupCategoryValidationTestApp(t) + defer app.Cleanup() + + biking := createTestCategory(t, app, "Biking") + hiking := createTestCategory(t, app, "Hiking") + walking := createTestCategory(t, app, "Walking") + userID := "user12345678901" + + existing := core.NewRecord(mustFindTestCollection(t, app, "user_category_preferences")) + existing.Set("user", userID) + existing.Set("category", biking.Id) + existing.Set("visible", false) + existing.Set("priority", 99) + if err := app.SaveNoValidate(existing); err != nil { + t.Fatal(err) + } + + if err := ReorderUserCategoryPreferences(app, userID, []string{walking.Id, biking.Id, hiking.Id}); err != nil { + t.Fatalf("ReorderUserCategoryPreferences() error = %v", err) + } + + records, err := app.FindRecordsByFilter( + "user_category_preferences", + "user = {:user}", + "priority", + 0, + 0, + map[string]any{"user": userID}, + ) + if err != nil { + t.Fatal(err) + } + + if len(records) != 3 { + t.Fatalf("preference count = %d, want 3", len(records)) + } + + wantPriorityByCategory := map[string]int{ + walking.Id: 1, + biking.Id: 2, + hiking.Id: 3, + } + for _, record := range records { + categoryID := record.GetString("category") + if got := record.GetInt("priority"); got != wantPriorityByCategory[categoryID] { + t.Fatalf("priority for category %q = %d, want %d", categoryID, got, wantPriorityByCategory[categoryID]) + } + if categoryID == biking.Id { + if record.Id != existing.Id { + t.Fatalf("biking preference id = %q, want existing id %q", record.Id, existing.Id) + } + if record.GetBool("visible") { + t.Fatal("existing preference visible = true, want false") + } + } else { + if !record.GetBool("visible") { + t.Fatalf("new preference for category %q visible = false, want true", categoryID) + } + } + } +} + +func TestReorderUserSubcategoryPreferencesUpsertsAndRenumbers(t *testing.T) { + app := setupCategoryValidationTestApp(t) + defer app.Cleanup() + + hiking := createTestCategory(t, app, "Hiking") + biking := createTestCategory(t, app, "Biking") + mountain := createTestSubcategory(t, app, hiking.Id, "Mountain Hiking", "", "") + family := createTestSubcategory(t, app, hiking.Id, "Family Hiking", "", "") + ebike := createTestSubcategory(t, app, biking.Id, "E-Bike", "", "") + userID := "user12345678901" + + existing := core.NewRecord(mustFindTestCollection(t, app, "user_subcategory_preferences")) + existing.Set("user", userID) + existing.Set("subcategory", family.Id) + existing.Set("visible", false) + existing.Set("priority", 99) + if err := app.SaveNoValidate(existing); err != nil { + t.Fatal(err) + } + + otherCategoryPreference := core.NewRecord(mustFindTestCollection(t, app, "user_subcategory_preferences")) + otherCategoryPreference.Set("user", userID) + otherCategoryPreference.Set("subcategory", ebike.Id) + otherCategoryPreference.Set("visible", true) + otherCategoryPreference.Set("priority", 12) + if err := app.SaveNoValidate(otherCategoryPreference); err != nil { + t.Fatal(err) + } + + if err := ReorderUserSubcategoryPreferences(app, userID, hiking.Id, []string{family.Id, mountain.Id}); err != nil { + t.Fatalf("ReorderUserSubcategoryPreferences() error = %v", err) + } + + records, err := app.FindRecordsByFilter( + "user_subcategory_preferences", + "user = {:user}", + "priority", + 0, + 0, + map[string]any{"user": userID}, + ) + if err != nil { + t.Fatal(err) + } + + if len(records) != 3 { + t.Fatalf("preference count = %d, want 3", len(records)) + } + + wantPriorityBySubcategory := map[string]int{ + family.Id: 1, + mountain.Id: 2, + ebike.Id: 12, + } + for _, record := range records { + subcategoryID := record.GetString("subcategory") + if got := record.GetInt("priority"); got != wantPriorityBySubcategory[subcategoryID] { + t.Fatalf("priority for subcategory %q = %d, want %d", subcategoryID, got, wantPriorityBySubcategory[subcategoryID]) + } + if subcategoryID == family.Id { + if record.Id != existing.Id { + t.Fatalf("family preference id = %q, want existing id %q", record.Id, existing.Id) + } + if record.GetBool("visible") { + t.Fatal("existing preference visible = true, want false") + } + } + if subcategoryID == mountain.Id && !record.GetBool("visible") { + t.Fatal("new preference visible = false, want true") + } + if subcategoryID == ebike.Id && record.Id != otherCategoryPreference.Id { + t.Fatalf("other category preference id = %q, want existing id %q", record.Id, otherCategoryPreference.Id) + } + } +} + +func setupCategoryValidationTestApp(t *testing.T) *pbtests.TestApp { + t.Helper() + + app, err := pbtests.NewTestApp(t.TempDir()) + if err != nil { + t.Fatal(err) + } + + categories := core.NewBaseCollection("categories") + categories.Fields.Add( + &core.TextField{Name: "name", Required: true}, + &core.TextField{Name: "short_name"}, + &core.TextField{Name: "icon"}, + &core.JSONField{Name: "translations"}, + ) + if err := app.Save(categories); err != nil { + app.Cleanup() + t.Fatal(err) + } + + subcategories := core.NewBaseCollection("subcategories") + subcategories.Fields.Add( + &core.RelationField{Name: "category", CollectionId: categories.Id, MaxSelect: 1, Required: true}, + &core.TextField{Name: "name", Required: true}, + &core.TextField{Name: "short_name"}, + &core.TextField{Name: "icon"}, + &core.TextField{Name: "badge_icon"}, + &core.JSONField{Name: "translations"}, + ) + if err := app.Save(subcategories); err != nil { + app.Cleanup() + t.Fatal(err) + } + + trails := core.NewBaseCollection("trails") + trails.Fields.Add( + &core.RelationField{Name: "category", CollectionId: categories.Id, MaxSelect: 1}, + &core.RelationField{Name: "subcategory", CollectionId: subcategories.Id, MaxSelect: 1}, + &core.TextField{Name: "federated_category_name"}, + &core.TextField{Name: "federated_subcategory_name"}, + ) + if err := app.Save(trails); err != nil { + app.Cleanup() + t.Fatal(err) + } + + preferences := core.NewBaseCollection("user_category_preferences") + preferences.Fields.Add( + &core.RelationField{Name: "user", CollectionId: "_pb_users_auth_", MaxSelect: 1, Required: true}, + &core.RelationField{Name: "category", CollectionId: categories.Id, MaxSelect: 1, Required: true}, + &core.BoolField{Name: "visible"}, + &core.NumberField{Name: "priority", OnlyInt: true}, + ) + if err := app.Save(preferences); err != nil { + app.Cleanup() + t.Fatal(err) + } + + subcategoryPreferences := core.NewBaseCollection("user_subcategory_preferences") + subcategoryPreferences.Fields.Add( + &core.RelationField{Name: "user", CollectionId: "_pb_users_auth_", MaxSelect: 1, Required: true}, + &core.RelationField{Name: "subcategory", CollectionId: subcategories.Id, MaxSelect: 1, Required: true}, + &core.BoolField{Name: "visible"}, + &core.NumberField{Name: "priority", OnlyInt: true}, + ) + if err := app.Save(subcategoryPreferences); err != nil { + app.Cleanup() + t.Fatal(err) + } + + return app +} + +func mustFindTestCollection(t *testing.T, app core.App, name string) *core.Collection { + t.Helper() + + collection, err := app.FindCollectionByNameOrId(name) + if err != nil { + t.Fatal(err) + } + + return collection +} + +func createTestCategory(t *testing.T, app core.App, name string) *core.Record { + t.Helper() + + record := core.NewRecord(mustFindTestCollection(t, app, "categories")) + record.Set("name", name) + if err := app.Save(record); err != nil { + t.Fatal(err) + } + + return record +} + +func createTestSubcategory(t *testing.T, app core.App, categoryID string, name string, shortName string, badgeIcon ...string) *core.Record { + t.Helper() + + record := core.NewRecord(mustFindTestCollection(t, app, "subcategories")) + record.Set("category", categoryID) + record.Set("name", name) + record.Set("short_name", shortName) + if len(badgeIcon) > 0 { + record.Set("badge_icon", badgeIcon[0]) + } + if err := app.Save(record); err != nil { + t.Fatal(err) + } + + return record +} + +func createTestTrail(t *testing.T, app core.App, categoryID string, subcategoryID string, federatedCategoryName string, federatedSubcategoryName string) *core.Record { + t.Helper() + + record := core.NewRecord(mustFindTestCollection(t, app, "trails")) + record.Set("category", categoryID) + record.Set("subcategory", subcategoryID) + record.Set("federated_category_name", federatedCategoryName) + record.Set("federated_subcategory_name", federatedSubcategoryName) + if err := app.Save(record); err != nil { + t.Fatal(err) + } + + return record +} diff --git a/db/util/meilisearch.go b/db/util/meilisearch.go index 395c5cd8..bae8546b 100644 --- a/db/util/meilisearch.go +++ b/db/util/meilisearch.go @@ -33,10 +33,24 @@ func documentFromTrailRecord(r *core.Record, author *core.Record, includeShares tags[i] = v.GetString("name") } + categoryID := r.GetString("category") + var categoryIDValue any + if categoryID != "" { + categoryIDValue = categoryID + } + + subcategoryID := r.GetString("subcategory") + var subcategoryIDValue any + if subcategoryID != "" { + subcategoryIDValue = subcategoryID + } + category := "" + categoryIcon := "" trailCategory := r.ExpandedOne("category") if trailCategory != nil { category = trailCategory.GetString("name") + categoryIcon = trailCategory.GetString("icon") } bounds := getStoredBounds(r) @@ -52,34 +66,40 @@ func documentFromTrailRecord(r *core.Record, author *core.Record, includeShares } document := map[string]any{ - "id": r.Id, - "author": author.Id, - "author_name": author.GetString("preferred_username"), - "author_avatar": author.GetString("icon"), - "name": r.GetString("name"), - "description": r.GetString("description"), - "location": r.GetString("location"), - "distance": r.GetFloat("distance"), - "elevation_gain": r.GetFloat("elevation_gain"), - "elevation_loss": r.GetFloat("elevation_loss"), - "duration": r.GetFloat("duration"), - "difficulty": difficultyToNumber(r.GetString("difficulty")), - "category": category, - "completed": r.GetBool("completed"), - "date": r.GetDateTime("date").Time().Unix(), - "created": r.GetDateTime("created").Time().Unix(), - "public": r.GetBool("public"), - "thumbnail": thumbnail, - "gpx": r.GetString("gpx"), - "tags": tags, - "polyline": r.GetString("polyline"), - "domain": domain, - "iri": r.GetString("iri"), - "min_lat": bounds[0], - "max_lat": bounds[1], - "min_lon": bounds[2], - "max_lon": bounds[3], - "bounding_box_diagonal": diagonal, + "id": r.Id, + "author": author.Id, + "author_name": author.GetString("preferred_username"), + "author_avatar": author.GetString("icon"), + "name": r.GetString("name"), + "description": r.GetString("description"), + "location": r.GetString("location"), + "distance": r.GetFloat("distance"), + "elevation_gain": r.GetFloat("elevation_gain"), + "elevation_loss": r.GetFloat("elevation_loss"), + "duration": r.GetFloat("duration"), + "difficulty": difficultyToNumber(r.GetString("difficulty")), + "category": category, + "category_id": categoryIDValue, + "category_icon": categoryIcon, + "subcategory_id": subcategoryIDValue, + "is_federated": !author.GetBool("is_local"), + "federated_category_name": r.GetString("federated_category_name"), + "federated_subcategory_name": r.GetString("federated_subcategory_name"), + "completed": r.GetBool("completed"), + "date": r.GetDateTime("date").Time().Unix(), + "created": r.GetDateTime("created").Time().Unix(), + "public": r.GetBool("public"), + "thumbnail": thumbnail, + "gpx": r.GetString("gpx"), + "tags": tags, + "polyline": r.GetString("polyline"), + "domain": domain, + "iri": r.GetString("iri"), + "min_lat": bounds[0], + "max_lat": bounds[1], + "min_lon": bounds[2], + "max_lon": bounds[3], + "bounding_box_diagonal": diagonal, "_geo": map[string]float64{ "lat": r.GetFloat("lat"), "lng": r.GetFloat("lon"), diff --git a/db/util/subcategory.go b/db/util/subcategory.go new file mode 100644 index 00000000..7440077e --- /dev/null +++ b/db/util/subcategory.go @@ -0,0 +1,230 @@ +package util + +import ( + "fmt" + + "github.com/pocketbase/pocketbase/core" +) + +func ValidateSubcategoryRecord(app core.App, record *core.Record) error { + parentCategory := record.GetString("category") + if parentCategory == "" { + return fmt.Errorf("subcategory category is required") + } + + name := record.GetString("name") + normalizedName := NormalizeCategoryName(name) + + allSubcategories, err := app.FindAllRecords("subcategories") + if err != nil { + return err + } + + for _, existing := range allSubcategories { + if existing.Id == record.Id || existing.GetString("category") != parentCategory { + continue + } + + if NormalizeCategoryName(existing.GetString("name")) == normalizedName { + return fmt.Errorf("subcategory name %q collides with existing subcategory %q in the same category after normalization", name, existing.GetString("name")) + } + } + + if _, err := ParseCategoryTranslations(record.Get("translations")); err != nil { + return err + } + + return nil +} + +func ValidateTrailSubcategoryRecord(app core.App, record *core.Record, subcategoryExplicit bool) error { + subcategoryID := record.GetString("subcategory") + if subcategoryID == "" { + return nil + } + + categoryID := record.GetString("category") + if categoryID == "" { + if !subcategoryExplicit { + record.Set("subcategory", "") + return nil + } + + return fmt.Errorf("trail subcategory requires a category") + } + + subcategory, err := app.FindRecordById("subcategories", subcategoryID) + if err != nil { + if !subcategoryExplicit { + record.Set("subcategory", "") + return nil + } + + return fmt.Errorf("trail subcategory %q does not exist: %w", subcategoryID, err) + } + + parentCategory := subcategory.GetString("category") + if parentCategory != categoryID { + if !subcategoryExplicit { + record.Set("subcategory", "") + return nil + } + + return fmt.Errorf("trail subcategory %q belongs to category %q, not %q", subcategoryID, parentCategory, categoryID) + } + + return nil +} + +func FindSubcategoryByNormalizedName(app core.App, categoryID string, name string) (*core.Record, error) { + normalizedName := NormalizeCategoryName(name) + if categoryID == "" || normalizedName == "" { + return nil, nil + } + + subcategories, err := app.FindRecordsByFilter( + "subcategories", + "category = {:category}", + "", + 0, + 0, + map[string]any{"category": categoryID}, + ) + if err != nil { + return nil, err + } + + for _, subcategory := range subcategories { + if NormalizeCategoryName(subcategory.GetString("name")) == normalizedName { + return subcategory, nil + } + } + + return nil, nil +} + +func ResolveCategoryAndSubcategoryByNormalizedNames(app core.App, categoryName string, subcategoryName string) (*core.Record, *core.Record, error) { + category, err := FindCategoryByNormalizedName(app, categoryName) + if err != nil || category == nil { + return category, nil, err + } + + subcategory, err := FindSubcategoryByNormalizedName(app, category.Id, subcategoryName) + if err != nil { + return category, nil, err + } + + return category, subcategory, nil +} + +func BackfillRemoteTrailCategory(app core.App, category *core.Record) error { + if category == nil || category.Id == "" { + return nil + } + + subcategoriesByName, err := normalizedSubcategoriesByName(app, category.Id) + if err != nil { + return err + } + + trails, err := app.FindRecordsByFilter( + "trails", + "federated_category_name != '' && category = ''", + "", + 0, + 0, + nil, + ) + if err != nil { + return err + } + + normalizedCategoryName := NormalizeCategoryName(category.GetString("name")) + for _, trail := range trails { + if NormalizeCategoryName(trail.GetString("federated_category_name")) != normalizedCategoryName { + continue + } + + trail.Set("category", category.Id) + if subcategory, ok := subcategoriesByName[NormalizeCategoryName(trail.GetString("federated_subcategory_name"))]; ok { + trail.Set("subcategory", subcategory.Id) + } + + if err := app.Save(trail); err != nil { + return err + } + } + + return nil +} + +func BackfillRemoteTrailSubcategory(app core.App, subcategory *core.Record) error { + if subcategory == nil || subcategory.Id == "" { + return nil + } + + category, err := app.FindRecordById("categories", subcategory.GetString("category")) + if err != nil { + return err + } + + trails, err := app.FindRecordsByFilter( + "trails", + "federated_subcategory_name != '' && subcategory = '' && (category = {:category} || category = '')", + "", + 0, + 0, + map[string]any{"category": category.Id}, + ) + if err != nil { + return err + } + + normalizedCategoryName := NormalizeCategoryName(category.GetString("name")) + normalizedSubcategoryName := NormalizeCategoryName(subcategory.GetString("name")) + for _, trail := range trails { + categoryID := trail.GetString("category") + if categoryID == "" { + if NormalizeCategoryName(trail.GetString("federated_category_name")) != normalizedCategoryName { + continue + } + trail.Set("category", category.Id) + } + + if NormalizeCategoryName(trail.GetString("federated_subcategory_name")) != normalizedSubcategoryName { + continue + } + + trail.Set("subcategory", subcategory.Id) + if err := app.Save(trail); err != nil { + return err + } + } + + return nil +} + +func normalizedSubcategoriesByName(app core.App, categoryID string) (map[string]*core.Record, error) { + subcategories, err := app.FindRecordsByFilter( + "subcategories", + "category = {:category}", + "", + 0, + 0, + map[string]any{"category": categoryID}, + ) + if err != nil { + return nil, err + } + + byName := make(map[string]*core.Record, len(subcategories)) + for _, subcategory := range subcategories { + normalizedName := NormalizeCategoryName(subcategory.GetString("name")) + if normalizedName == "" { + continue + } + byName[normalizedName] = subcategory + } + + return byName, nil +} diff --git a/db/util/subcategory_defaults.go b/db/util/subcategory_defaults.go new file mode 100644 index 00000000..5395ce01 --- /dev/null +++ b/db/util/subcategory_defaults.go @@ -0,0 +1,284 @@ +package util + +import ( + "fmt" + + "github.com/pocketbase/pocketbase/core" +) + +type defaultSubcategorySeed struct { + parentCategory string + name string + shortName string + badgeIcon string + translations map[string]CategoryTranslation + aliases []string +} + +var defaultSubcategories = []defaultSubcategorySeed{ + {parentCategory: "Biking", name: "MTB", shortName: "MTB", badgeIcon: "mountain"}, + {parentCategory: "Biking", name: "Gravel", shortName: "GRVL"}, + { + parentCategory: "Biking", + name: "Touring", + shortName: "TOUR", + aliases: []string{"Touring Bike", "City Bike"}, + translations: subcategoryTranslations("Touring", "Tourenrad", "TOUR"), + }, + { + parentCategory: "Biking", + name: "Road", + shortName: "ROAD", + badgeIcon: "grip-lines-vertical", + translations: subcategoryTranslations("Road", "Rennrad", "ROAD"), + }, + {parentCategory: "Biking", name: "E-Bike", shortName: "EBIKE", badgeIcon: "bolt"}, + { + parentCategory: "Hiking", + name: "Winter", + shortName: "WINT", + badgeIcon: "snowflake", + aliases: []string{"Winter Hiking"}, + translations: map[string]CategoryTranslation{ + "de": {Name: "Winterwandern", ShortName: "WINT"}, + "en": {Name: "Winter", ShortName: "WINT"}, + }, + }, + { + parentCategory: "Hiking", + name: "Alpine", + shortName: "ALP", + badgeIcon: "mountain", + aliases: []string{"Alpine Hiking"}, + translations: subcategoryTranslations("Alpine", "Bergwandern", "ALP"), + }, + { + parentCategory: "Hiking", + name: "Long-distance", + shortName: "LONG", + aliases: []string{"Long-distance Hiking"}, + translations: subcategoryTranslations("Long-distance", "Fernwandern", "LONG"), + }, + { + parentCategory: "Hiking", + name: "Snowshoeing", + shortName: "SNOW", + badgeIcon: "snowflake", + translations: subcategoryTranslations("Snowshoeing", "Schneeschuhwandern", "SNOW"), + }, + { + parentCategory: "Hiking", + name: "Family", + shortName: "FAM", + badgeIcon: "child", + aliases: []string{"Family Hiking"}, + translations: subcategoryTranslations("Family", "Familienwandern", "FAM"), + }, + { + parentCategory: "Hiking", + name: "Pilgrimage", + shortName: "PILG", + badgeIcon: "cross", + translations: subcategoryTranslations("Pilgrimage", "Pilgern", "PILG"), + }, + { + parentCategory: "Running", + name: "Trail", + shortName: "TRAIL", + aliases: []string{"Trail Running"}, + translations: subcategoryTranslations("Trail", "Trailrunning", "TRAIL"), + }, + { + parentCategory: "Running", + name: "Road", + shortName: "ROAD", + badgeIcon: "grip-lines-vertical", + aliases: []string{"Road Running"}, + translations: subcategoryTranslations("Road", "Straßenlauf", "ROAD"), + }, + { + parentCategory: "Skiing", + name: "Cross-country", + shortName: "NORD", + aliases: []string{"Cross-country Skiing"}, + translations: subcategoryTranslations("Cross-country", "Langlauf", "NORD"), + }, + { + parentCategory: "Skiing", + name: "Skating", + shortName: "SKATE", + translations: subcategoryTranslations("Skating", "Skating", "SKATE"), + }, + { + parentCategory: "Skiing", + name: "Backcountry", + shortName: "BACK", + aliases: []string{"Backcountry Skiing"}, + translations: subcategoryTranslations("Backcountry", "Skitour", "BACK"), + }, +} + +func subcategoryTranslations(en string, de string, shortName string) map[string]CategoryTranslation { + return map[string]CategoryTranslation{ + "de": {Name: de, ShortName: shortName}, + "en": {Name: en, ShortName: shortName}, + } +} + +func SeedDefaultSubcategories(app core.App) error { + subcategoriesCollection, err := app.FindCollectionByNameOrId("subcategories") + if err != nil { + return err + } + + for _, seed := range defaultSubcategories { + categories, err := app.FindRecordsByFilter( + "categories", + "name = {:name}", + "", + 1, + 0, + map[string]any{"name": seed.parentCategory}, + ) + if err != nil { + return err + } + if len(categories) == 0 { + continue + } + category := categories[0] + + existing, err := app.FindRecordsByFilter( + "subcategories", + "category = {:category}", + "", + 0, + 0, + map[string]any{"category": category.Id}, + ) + if err != nil { + return err + } + if existingRecord := findDefaultSubcategory(existing, seed.name, seed.aliases); existingRecord != nil { + changed, err := applyDefaultSubcategorySeed(existingRecord, seed) + if err != nil { + return err + } + if changed { + if err := app.Save(existingRecord); err != nil { + return fmt.Errorf("failed to update seeded subcategory %q: %w", seed.name, err) + } + } + continue + } + + record := core.NewRecord(subcategoriesCollection) + record.Set("category", category.Id) + record.Set("name", seed.name) + record.Set("short_name", seed.shortName) + if seed.badgeIcon != "" { + record.Set("badge_icon", seed.badgeIcon) + } + if len(seed.translations) > 0 { + record.Set("translations", seed.translations) + } + if err := app.Save(record); err != nil { + return fmt.Errorf("failed to seed subcategory %q: %w", seed.name, err) + } + } + + return nil +} + +func applyDefaultSubcategorySeed(record *core.Record, seed defaultSubcategorySeed) (bool, error) { + changed := false + + if isDefaultSubcategoryAlias(record.GetString("name"), seed.aliases) { + record.Set("name", seed.name) + changed = true + } + + if record.GetString("short_name") == "" && seed.shortName != "" { + record.Set("short_name", seed.shortName) + changed = true + } + + if record.GetString("badge_icon") == "" && seed.badgeIcon != "" { + record.Set("badge_icon", seed.badgeIcon) + changed = true + } + + if len(seed.translations) > 0 { + currentTranslations, err := ParseCategoryTranslations(record.Get("translations")) + if err != nil { + return false, fmt.Errorf("invalid existing translations for subcategory %q: %w", record.GetString("name"), err) + } + + mergedTranslations, translationsChanged := mergeDefaultSubcategoryTranslations(seed.translations, currentTranslations) + if translationsChanged { + record.Set("translations", mergedTranslations) + changed = true + } + } + + return changed, nil +} + +func isDefaultSubcategoryAlias(name string, aliases []string) bool { + normalizedName := NormalizeCategoryName(name) + for _, alias := range aliases { + if normalizedName == NormalizeCategoryName(alias) { + return true + } + } + + return false +} + +func mergeDefaultSubcategoryTranslations(staticTranslations map[string]CategoryTranslation, currentTranslations map[string]CategoryTranslation) (map[string]CategoryTranslation, bool) { + if currentTranslations == nil { + currentTranslations = map[string]CategoryTranslation{} + } + + changed := false + for locale, staticTranslation := range staticTranslations { + translation := currentTranslations[locale] + localeChanged := false + if translation.Name == "" && staticTranslation.Name != "" { + translation.Name = staticTranslation.Name + localeChanged = true + } + if translation.ShortName == "" && staticTranslation.ShortName != "" { + translation.ShortName = staticTranslation.ShortName + localeChanged = true + } + if localeChanged { + changed = true + currentTranslations[locale] = translation + } + } + + return currentTranslations, changed +} + +func findDefaultSubcategory(records []*core.Record, name string, aliases []string) *core.Record { + normalizedName := NormalizeCategoryName(name) + for _, record := range records { + if NormalizeCategoryName(record.GetString("name")) == normalizedName { + return record + } + } + + normalizedAliases := map[string]struct{}{} + for _, alias := range aliases { + normalizedAliases[NormalizeCategoryName(alias)] = struct{}{} + } + + for _, record := range records { + if _, ok := normalizedAliases[NormalizeCategoryName(record.GetString("name"))]; ok { + return record + } + } + + return nil +} diff --git a/docs/astro.config.mjs b/docs/astro.config.mjs index aeed090d..1081c919 100644 --- a/docs/astro.config.mjs +++ b/docs/astro.config.mjs @@ -51,6 +51,10 @@ export default defineConfig({ label: 'Create/Edit a trail', link: '/use/create-a-trail/' }, + { + label: 'Categories', + link: '/use/categories/' + }, { label: 'Summit logs', link: '/use/summit-logs/' diff --git a/docs/package-lock.json b/docs/package-lock.json index f13f4b15..a2fe756f 100644 --- a/docs/package-lock.json +++ b/docs/package-lock.json @@ -9,19 +9,19 @@ "version": "0.19.2", "dependencies": { "@astrojs/check": "^0.9.9", - "@astrojs/node": "^10.1.2", - "@astrojs/starlight": "^0.39.2", + "@astrojs/node": "^11.0.0", + "@astrojs/starlight": "^0.41.1", "@astrojs/starlight-tailwind": "^5.0.0", - "@astrojs/svelte": "^8.1.2", + "@astrojs/svelte": "^9.0.0", "@fontsource/ibm-plex-mono": "^5.2.7", "@fontsource/ibm-plex-sans": "^5.2.8", - "@tailwindcss/vite": "^4.3.0", - "astro": "^6.4.8", - "sharp": "^0.34.5", - "starlight-openapi": "^0.25.3", - "svelte": "^5.56.0", - "tailwindcss": "^4.1.10", - "typescript": "^5.9.3" + "@tailwindcss/vite": "^4.3.2", + "astro": "^7.0.4", + "sharp": "^0.35.2", + "starlight-openapi": "^0.26.0", + "svelte": "^5.56.4", + "tailwindcss": "^4.3.2", + "typescript": "^6.0.3" } }, "node_modules/@apidevtools/json-schema-ref-parser": { @@ -64,6 +64,182 @@ "integrity": "sha512-f3FN83d2G/v32ipNClRKgYv30onQlMZX1vCeZMjPsMMPl1mDpmbl0+N5BYo4S/ofzqJyS5hvwacEo0CCVDn/Qg==", "license": "MIT" }, + "node_modules/@astrojs/compiler-binding": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding/-/compiler-binding-0.3.0.tgz", + "integrity": "sha512-zlsOT5COD9hRwplJCgQhS21unxON5AKirf0vgt1ijXwuseYIaZdm2ZOpF8fsz+DY9EyXx+I/ukxtg7uoBep68A==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@astrojs/compiler-binding-darwin-arm64": "0.3.0", + "@astrojs/compiler-binding-darwin-x64": "0.3.0", + "@astrojs/compiler-binding-linux-arm64-gnu": "0.3.0", + "@astrojs/compiler-binding-linux-arm64-musl": "0.3.0", + "@astrojs/compiler-binding-linux-x64-gnu": "0.3.0", + "@astrojs/compiler-binding-linux-x64-musl": "0.3.0", + "@astrojs/compiler-binding-wasm32-wasi": "0.3.0", + "@astrojs/compiler-binding-win32-arm64-msvc": "0.3.0", + "@astrojs/compiler-binding-win32-x64-msvc": "0.3.0" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-arm64": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-arm64/-/compiler-binding-darwin-arm64-0.3.0.tgz", + "integrity": "sha512-3n0uu+uJpnCq8b4JFi3uGDsIisAvHctxSmH+cIO9Gbei1H1Y1QXaYboXyiWJugUmprr3OEYP7+LdodzpVFzLMQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-darwin-x64": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-darwin-x64/-/compiler-binding-darwin-x64-0.3.0.tgz", + "integrity": "sha512-scxNGKjOBydMo1QR4LtK0FMgh7ubQomJDv953nz2msQFkPKke/0FpPv/cQM0T/kuZdReZQFU8Oz3iOrP/6WHEg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-gnu": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-gnu/-/compiler-binding-linux-arm64-gnu-0.3.0.tgz", + "integrity": "sha512-NZrWLolVUANmrnl0zrFK/Sx5Sock1gEUT49ALfMTTCA5Ya2ec/BoJXMIg4KgE+wZcrdXJ8e+WyEhM7YLk/FJkA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-arm64-musl": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-arm64-musl/-/compiler-binding-linux-arm64-musl-0.3.0.tgz", + "integrity": "sha512-PjwRmKgMFDsFhg82g0poXlIY8Qn3fMA3hXjaR0coJWJzTJsRH9ATU0j2ocigjtU1h3vL/yR7yLUxGj/lTCq73g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-gnu": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-gnu/-/compiler-binding-linux-x64-gnu-0.3.0.tgz", + "integrity": "sha512-Dr69VJYlnSfyL8gzELW6S4mE41P7TDPn1IKjwMnjdZ7+dxgJI50oMLFSk1LVe26bHmWB3ktuh8fDVK1THI9e9A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-linux-x64-musl": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-linux-x64-musl/-/compiler-binding-linux-x64-musl-0.3.0.tgz", + "integrity": "sha512-AEt+bRw8PfImCcyRH1lpXVB8CdmQ1K/wPo5u99iec4/U/XdNvQZ715YVuNzIJpbJXelgQeZ5H2+Ea7XwRyWY5g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-wasm32-wasi": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-wasm32-wasi/-/compiler-binding-wasm32-wasi-0.3.0.tgz", + "integrity": "sha512-U80tA1j8V6LjhiTZzVCtG4E8hrNVVNXDGV5fCgJ94q8FU9CPH+XwdDDhLzBybfWhKfyItXmQiZNRPTiPCYTpVg==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-arm64-msvc": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-arm64-msvc/-/compiler-binding-win32-arm64-msvc-0.3.0.tgz", + "integrity": "sha512-CpY1RII2r1XMpOUVD1VR/F2wtuRsiOCkFULS10Khyj8/DFZMtxVuUCAWGw+CW2Ka0h6eP3Xc1CA+glFlvXMPxA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-binding-win32-x64-msvc": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-binding-win32-x64-msvc/-/compiler-binding-win32-x64-msvc-0.3.0.tgz", + "integrity": "sha512-qmFbs769oeeGrRebAnCW7aBk8m71vf85W/dX/jddfx5Z06/w0wf7TZCfJPOX1Fld2t+4N+iXzfGEJG+zJQ+bzg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@astrojs/compiler-rs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@astrojs/compiler-rs/-/compiler-rs-0.3.0.tgz", + "integrity": "sha512-J2qEVHtIDjEM9TxwmwuebOGmZNwhKu/dR7P7qBpnJKGmBBX0vdweQ/4cEXhj8fBbWVUB5V12xWChri3CgKNULQ==", + "license": "MIT", + "dependencies": { + "@astrojs/compiler-binding": "0.3.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, "node_modules/@astrojs/internal-helpers": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.10.0.tgz", @@ -146,13 +322,26 @@ "vfile": "^6.0.3" } }, - "node_modules/@astrojs/mdx": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-5.0.6.tgz", - "integrity": "sha512-4dKe0ZMmqujofPNDHahzClkwinn9f8jHPcaXcgdGvPAlboD2mjzkUCofli2cBnxYAkdfhC6d50gBJ8i/cH8gHw==", + "node_modules/@astrojs/markdown-satteri": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@astrojs/markdown-satteri/-/markdown-satteri-0.3.2.tgz", + "integrity": "sha512-feXuUPy41gVfeM7EHT1ciUim8ozGr+YHXab9uUBc1Hk8y60DQosO8ldL+AoPXnCAoGj1OChwHfvXmmJ6XVnY9A==", "license": "MIT", "dependencies": { - "@astrojs/markdown-remark": "7.1.2", + "@astrojs/internal-helpers": "0.10.0", + "@astrojs/prism": "4.0.2", + "github-slugger": "^2.0.0", + "satteri": "^0.9.1" + } + }, + "node_modules/@astrojs/mdx": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@astrojs/mdx/-/mdx-7.0.0.tgz", + "integrity": "sha512-LKwNA8nnLtEM0auoP6OfH/UnlKe1Ub59qZjbcYkZjPBGw6PkJewWkA/1qwLpECvV6gMDd6TR6eqV9p/VYZrcrQ==", + "license": "MIT", + "dependencies": { + "@astrojs/internal-helpers": "0.10.0", + "@astrojs/markdown-remark": "7.2.0", "@mdx-js/mdx": "^3.1.1", "acorn": "^8.16.0", "es-module-lexer": "^2.0.0", @@ -170,51 +359,19 @@ "node": ">=22.12.0" }, "peerDependencies": { - "astro": "^6.0.0" - } - }, - "node_modules/@astrojs/mdx/node_modules/@astrojs/internal-helpers": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.9.1.tgz", - "integrity": "sha512-1pWuARqYom/TzuU3+0ZugsTrKlUydWKuULmDqSMTuonY+9IRDUEGKX/8PXQ1nBxRq3w85uGtd9q9SXfqEldMIQ==", - "license": "MIT", - "dependencies": { - "picomatch": "^4.0.4" - } - }, - "node_modules/@astrojs/mdx/node_modules/@astrojs/markdown-remark": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-7.1.2.tgz", - "integrity": "sha512-caXZ4Dc2St2dW8luEg22GlP0gupLdztCTQE4EzZOxW1pqWXz9mbeJEuHUkgDYcKWW8tjIHkydYDhWLVoxJ327Q==", - "license": "MIT", - "dependencies": { - "@astrojs/internal-helpers": "0.9.1", - "@astrojs/prism": "4.0.2", - "github-slugger": "^2.0.0", - "hast-util-from-html": "^2.0.3", - "hast-util-to-text": "^4.0.2", - "js-yaml": "^4.1.1", - "mdast-util-definitions": "^6.0.0", - "rehype-raw": "^7.0.0", - "rehype-stringify": "^10.0.1", - "remark-gfm": "^4.0.1", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.1.2", - "remark-smartypants": "^3.0.2", - "retext-smartypants": "^6.2.0", - "shiki": "^4.0.0", - "smol-toml": "^1.6.0", - "unified": "^11.0.5", - "unist-util-remove-position": "^5.0.0", - "unist-util-visit": "^5.1.0", - "unist-util-visit-parents": "^6.0.2", - "vfile": "^6.0.3" + "@astrojs/markdown-satteri": "^0.3.1-alpha.0", + "astro": "^7.0.0-alpha.0" + }, + "peerDependenciesMeta": { + "@astrojs/markdown-satteri": { + "optional": true + } } }, "node_modules/@astrojs/node": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@astrojs/node/-/node-10.1.2.tgz", - "integrity": "sha512-6MtNb0iEdZw3m7Dva8V3qo1y9MVoVJKx9dQiNGvy4Ncg5yfaAwGa+9mrDJ3gWCWUtESZi8cvqVshy92sFufg1Q==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@astrojs/node/-/node-11.0.0.tgz", + "integrity": "sha512-lmwSTG42fAs9/UxNAHt4noseEwXcDupwqMWVftw0j24BaHU9F1CEfi63p3pPlwGP9g0wLqitGQJP/P6x1vLdOQ==", "license": "MIT", "dependencies": { "@astrojs/internal-helpers": "0.10.0", @@ -222,7 +379,7 @@ "server-destroy": "^1.0.1" }, "peerDependencies": { - "astro": "^6.3.0" + "astro": "^7.0.0-alpha.2" } }, "node_modules/@astrojs/prism": { @@ -249,19 +406,19 @@ } }, "node_modules/@astrojs/starlight": { - "version": "0.39.2", - "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.39.2.tgz", - "integrity": "sha512-vlw+bwnjtf5buCTUtLU7JfV6D3knslxqnspr6LKs6hfRuFZiyr5hT44F7GyDqR9FKANUqFxnIzWM81F1k/kOUA==", + "version": "0.41.1", + "resolved": "https://registry.npmjs.org/@astrojs/starlight/-/starlight-0.41.1.tgz", + "integrity": "sha512-avf2OmrVg6GdVU18juebjjIIuLa+uS3syHuJ/3yDaEFP/8it+YvcxRrYDSf7K6rC4v770UxIddba2hAqQyTeYA==", "license": "MIT", "dependencies": { - "@astrojs/markdown-remark": "^7.1.1", - "@astrojs/mdx": "^5.0.4", + "@astrojs/markdown-satteri": "^0.3.2", + "@astrojs/mdx": "^7.0.0", "@astrojs/sitemap": "^3.7.2", "@pagefind/default-ui": "^1.3.0", "@types/hast": "^3.0.4", "@types/js-yaml": "^4.0.9", "@types/mdast": "^4.0.4", - "astro-expressive-code": "^0.42.0", + "astro-expressive-code": "^0.44.0", "bcp-47": "^2.1.0", "hast-util-from-html": "^2.0.3", "hast-util-select": "^6.0.4", @@ -274,17 +431,24 @@ "mdast-util-directive": "^3.1.0", "mdast-util-to-markdown": "^2.1.2", "mdast-util-to-string": "^4.0.0", - "pagefind": "^1.3.0", + "pagefind": "^1.5.2", "rehype": "^13.0.2", "rehype-format": "^5.0.1", "remark-directive": "^4.0.0", + "satteri": "^0.9.1", "ultrahtml": "^1.6.0", "unified": "^11.0.5", "unist-util-visit": "^5.1.0", "vfile": "^6.0.3" }, "peerDependencies": { - "astro": "^6.0.0" + "@astrojs/markdown-remark": "^7.2.0", + "astro": "^7.0.2" + }, + "peerDependenciesMeta": { + "@astrojs/markdown-remark": { + "optional": true + } } }, "node_modules/@astrojs/starlight-tailwind": { @@ -298,21 +462,21 @@ } }, "node_modules/@astrojs/svelte": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/@astrojs/svelte/-/svelte-8.1.2.tgz", - "integrity": "sha512-L4eBoTY+DdgyH1g8pmKJxidRSvdk+NbkyJt5+f8EtdyJtPdxogeCyG3eEDmlIqqXxzNqLg7k/tYCjfBQ4kDMRA==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@astrojs/svelte/-/svelte-9.0.0.tgz", + "integrity": "sha512-gn4yFrTNvabpNu/yDp6xsD8xBVy4T2UdvEouLnvMQdBL7KbdZwkeR8Y3RBNBdYyIpRp1+oBPcxqr9DFpUrJT8A==", "license": "MIT", "dependencies": { - "@sveltejs/vite-plugin-svelte": "^6.2.4", + "@sveltejs/vite-plugin-svelte": "^7.1.0", "svelte2tsx": "^0.7.55", - "vite": "^7.3.2", + "vite": "^8.0.13", "vitefu": "^1.1.2" }, "engines": { "node": ">=22.12.0" }, "peerDependencies": { - "astro": "^6.0.0", + "astro": "^7.0.0-alpha.0", "svelte": "^5.43.6", "typescript": "^5.3.3 || ^6.0.0" } @@ -343,12 +507,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -357,30 +521,30 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", - "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -390,56 +554,178 @@ } }, "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, + "node_modules/@bruits/satteri-darwin-arm64": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-arm64/-/satteri-darwin-arm64-0.9.4.tgz", + "integrity": "sha512-W3MSUkr2mZRR8Stoe+lqNAyzQzRuFMU8WffV9IvFSxTok0LGWR0ZZQPLELU4QTRiUbhL2Y4VUP9vV7pj8rHjgg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-darwin-x64": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@bruits/satteri-darwin-x64/-/satteri-darwin-x64-0.9.4.tgz", + "integrity": "sha512-DXOuuaE1lsv7mpk2mOvGrzqoEWEvOIZEO/fXVa7zfM23Iob+CBjBkRAMwpHA4pmZ3j6Gj7WJzPKw0kQ7w741AQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-gnu": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-gnu/-/satteri-linux-arm64-gnu-0.9.4.tgz", + "integrity": "sha512-gJxU9rGGoqIznSEgEzpjxkry24jeHuMpoo1tCIAhHYh7WaD3j5F8zt3jmHxEaN1Uwa+K5+wFgIR2uIGOnMzEmw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-arm64-musl": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-arm64-musl/-/satteri-linux-arm64-musl-0.9.4.tgz", + "integrity": "sha512-Wjzu9hmmAbfmDkBfPI1VdZygJtYz9uYZQnkEyrXi6S2JFi+2pXQ1A5irj38bqm0IZmWcTbk0cVG4NZnPdtVNJA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-gnu": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-gnu/-/satteri-linux-x64-gnu-0.9.4.tgz", + "integrity": "sha512-MR1Q+wMx65FQlbSV7cRqWW87Knp0zkoaIV55Dt+xZl028wJABXEPEEmG3670SLq7lVZvcGIDwCgSg2kCYxvRwA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-linux-x64-musl": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@bruits/satteri-linux-x64-musl/-/satteri-linux-x64-musl-0.9.4.tgz", + "integrity": "sha512-T4gxhXve3zyNAZesrXAd/rDZOGRkbfFIUFld4TGsw6BsjoIteCcDji6IMqeXyaWEVSykY2X8Eid2hr6aXGYAaw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@bruits/satteri-wasm32-wasi": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@bruits/satteri-wasm32-wasi/-/satteri-wasm32-wasi-0.9.4.tgz", + "integrity": "sha512-/CEG8LUlpaBEnhFnYVn0UnlHFLs51UhrkJBUPDUXLzkadzAcnR88iRA/nOl7Zwhjb4WhfBV4p3P5qeOJMtH0iA==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@bruits/satteri-win32-arm64-msvc": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-arm64-msvc/-/satteri-win32-arm64-msvc-0.9.4.tgz", + "integrity": "sha512-E1ZPQbgCtFKiU7pFYVndynvY7ne4coeVDUgnVThErSFlJ2ceQCBZrfRTD1lzrIDy63Bbqo+g/cZY9duw+JYjIw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@bruits/satteri-win32-x64-msvc": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@bruits/satteri-win32-x64-msvc/-/satteri-win32-x64-msvc-0.9.4.tgz", + "integrity": "sha512-5I7SiarsNdAUuhJb50CXJPTwr/ECVrBoU+fymoLjChK5fW//+srhY4lstcNTzgFRtQSYfVtm4OQZz16CVMeTeA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@capsizecss/unpack": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.0.tgz", - "integrity": "sha512-VERIM64vtTP1C4mxQ5thVT9fK0apjPFobqybMtA1UdUujWka24ERHbRHFGmpbbhp73MhV+KSsHQH9C6uOTdEQA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-4.0.1.tgz", + "integrity": "sha512-CuNiSqg7+e1cO/GjffyMOm5Tt2jUF9CWHHnvQ/UkqvtkGfHdgwEC0wpmq7fkN3gxwpRnrAN0WzO3vREKmNolMQ==", "license": "MIT", "dependencies": { - "fontkitten": "^1.0.0" + "fontkitten": "^1.0.3" }, "engines": { "node": ">=18" } }, "node_modules/@clack/core": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.1.0.tgz", - "integrity": "sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.4.2.tgz", + "integrity": "sha512-0Ty/1Gfm+Kb07sXcuESjyKfwEhSy4Ns1AgeEisHb/bDY5fWme0tTeTkU14T1Gmcs17YIjB/teiDe4uaCghbYqQ==", "license": "MIT", "dependencies": { + "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" } }, "node_modules/@clack/prompts": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.1.0.tgz", - "integrity": "sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.6.0.tgz", + "integrity": "sha512-EYlRokl8szrP9Z25qT5aepMdBjzBvHF9ZEhzIiUBc9guz/T31EqRgvD0QSgZcpE93xiwrr+OkB4nz0BZyF6fSA==", "license": "MIT", "dependencies": { - "@clack/core": "1.1.0", + "@clack/core": "1.4.2", + "fast-string-width": "^3.0.2", + "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 20.12.0" } }, "node_modules/@ctrl/tinycolor": { @@ -506,10 +792,31 @@ "integrity": "sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==", "license": "MIT" }, + "node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/runtime": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", - "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "license": "MIT", "optional": true, "dependencies": { @@ -517,9 +824,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", - "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", "cpu": [ "ppc64" ], @@ -533,9 +840,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", - "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", "cpu": [ "arm" ], @@ -549,9 +856,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", - "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", "cpu": [ "arm64" ], @@ -565,9 +872,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", - "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", "cpu": [ "x64" ], @@ -581,9 +888,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", - "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", "cpu": [ "arm64" ], @@ -597,9 +904,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", - "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", "cpu": [ "x64" ], @@ -613,9 +920,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", - "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", "cpu": [ "arm64" ], @@ -629,9 +936,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", - "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", "cpu": [ "x64" ], @@ -645,9 +952,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", - "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", "cpu": [ "arm" ], @@ -661,9 +968,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", - "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", "cpu": [ "arm64" ], @@ -677,9 +984,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", - "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", "cpu": [ "ia32" ], @@ -693,9 +1000,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", - "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", "cpu": [ "loong64" ], @@ -709,9 +1016,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", - "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", "cpu": [ "mips64el" ], @@ -725,9 +1032,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", - "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", "cpu": [ "ppc64" ], @@ -741,9 +1048,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", - "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", "cpu": [ "riscv64" ], @@ -757,9 +1064,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", - "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", "cpu": [ "s390x" ], @@ -773,9 +1080,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", - "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", "cpu": [ "x64" ], @@ -789,9 +1096,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", - "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", "cpu": [ "arm64" ], @@ -805,9 +1112,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", - "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", "cpu": [ "x64" ], @@ -821,9 +1128,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", - "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", "cpu": [ "arm64" ], @@ -837,9 +1144,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", - "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", "cpu": [ "x64" ], @@ -853,9 +1160,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", - "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", "cpu": [ "arm64" ], @@ -869,9 +1176,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", - "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", "cpu": [ "x64" ], @@ -885,9 +1192,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", - "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", "cpu": [ "arm64" ], @@ -901,9 +1208,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", - "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", "cpu": [ "ia32" ], @@ -917,9 +1224,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", - "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", "cpu": [ "x64" ], @@ -933,9 +1240,9 @@ } }, "node_modules/@expressive-code/core": { - "version": "0.42.0", - "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.42.0.tgz", - "integrity": "sha512-MN11+9nfmaC7sYu2BZJXAXqwkBRt8t1xTSqP+Ti1NfTEskgl6xUnzDxoaiQkg0BMzpglA0pys4dpDKquP/cyIw==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@expressive-code/core/-/core-0.44.0.tgz", + "integrity": "sha512-xgiF2P6tYUbrhi3+x0S8xHZWT1t3Bvb3U91tAtRbLb9HLejLvYc5GZUqKICKLaUN4iSGhhNJu2fM/aH8e5yCMg==", "license": "MIT", "dependencies": { "@ctrl/tinycolor": "^4.0.4", @@ -950,31 +1257,31 @@ } }, "node_modules/@expressive-code/plugin-frames": { - "version": "0.42.0", - "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.42.0.tgz", - "integrity": "sha512-XtkPm+941Uta7Y+81Acv+OA/20F1NJmJhCX6UYGKpqEIGqplNh3PTOhcURp6tcruhlzJcWcvpWy6Oigz3SrjqA==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-frames/-/plugin-frames-0.44.0.tgz", + "integrity": "sha512-V6M6+zVc1GzqCvXkQHc2m5rcFOIVzJgMq5gnfrMnVf2gwtj/sg4H93c1f/mGeqHycubwkHFUDyParAOiGeDZeA==", "license": "MIT", "dependencies": { - "@expressive-code/core": "^0.42.0" + "@expressive-code/core": "^0.44.0" } }, "node_modules/@expressive-code/plugin-shiki": { - "version": "0.42.0", - "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.42.0.tgz", - "integrity": "sha512-PMKey/kLmewttAHQezL+Y5Fx3vVssfDi3+FJOYQQS2mXP3tQspFELtKKAfsXfmSXdToZYgwoO69HJndqfE+09g==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-shiki/-/plugin-shiki-0.44.0.tgz", + "integrity": "sha512-RZsdaqlbGqyAQKuoX4myQXxjmiE2l5KBpJ/gKPh62tCdIdpWyjbzVqSo8+5XsezZxkfi8AJ/J6EUaBTPROFX/Q==", "license": "MIT", "dependencies": { - "@expressive-code/core": "^0.42.0", + "@expressive-code/core": "^0.44.0", "shiki": "^4.0.2" } }, "node_modules/@expressive-code/plugin-text-markers": { - "version": "0.42.0", - "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.42.0.tgz", - "integrity": "sha512-l59lUx8fq1v5g6SpmbDjiU0+7IdfbiWnAyRmtTVSpfhyq+nZMN4UcmYyu2b9Mynhzt7Gr+O+cXyEPDNb2AVWVQ==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/@expressive-code/plugin-text-markers/-/plugin-text-markers-0.44.0.tgz", + "integrity": "sha512-0/m3A5b+lz2upyNq+wzZ1S69HRoJmyFs5LsR42lVZ9pmGRlBiSBYQpvqlji4DBj1+Riamxc0AvcCr5kuzOQeWA==", "license": "MIT", "dependencies": { - "@expressive-code/core": "^0.42.0" + "@expressive-code/core": "^0.44.0" } }, "node_modules/@fontsource/ibm-plex-mono": { @@ -1014,9 +1321,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.2.tgz", + "integrity": "sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg==", "cpu": [ "arm64" ], @@ -1026,19 +1333,19 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "@img/sharp-libvips-darwin-arm64": "1.3.1" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.2.tgz", + "integrity": "sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw==", "cpu": [ "x64" ], @@ -1048,19 +1355,38 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "@img/sharp-libvips-darwin-x64": "1.3.1" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.2.tgz", + "integrity": "sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.2" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.1.tgz", + "integrity": "sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==", "cpu": [ "arm64" ], @@ -1074,9 +1400,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.1.tgz", + "integrity": "sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==", "cpu": [ "x64" ], @@ -1090,9 +1416,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.1.tgz", + "integrity": "sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==", "cpu": [ "arm" ], @@ -1106,9 +1432,9 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.1.tgz", + "integrity": "sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==", "cpu": [ "arm64" ], @@ -1122,9 +1448,9 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.1.tgz", + "integrity": "sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==", "cpu": [ "ppc64" ], @@ -1138,9 +1464,9 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.1.tgz", + "integrity": "sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw==", "cpu": [ "riscv64" ], @@ -1154,9 +1480,9 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.1.tgz", + "integrity": "sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew==", "cpu": [ "s390x" ], @@ -1170,9 +1496,9 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.1.tgz", + "integrity": "sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A==", "cpu": [ "x64" ], @@ -1186,9 +1512,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.1.tgz", + "integrity": "sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw==", "cpu": [ "arm64" ], @@ -1202,9 +1528,9 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.1.tgz", + "integrity": "sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==", "cpu": [ "x64" ], @@ -1218,9 +1544,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.2.tgz", + "integrity": "sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A==", "cpu": [ "arm" ], @@ -1230,19 +1556,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "@img/sharp-libvips-linux-arm": "1.3.1" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.2.tgz", + "integrity": "sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA==", "cpu": [ "arm64" ], @@ -1252,19 +1578,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "@img/sharp-libvips-linux-arm64": "1.3.1" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.2.tgz", + "integrity": "sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg==", "cpu": [ "ppc64" ], @@ -1274,19 +1600,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "@img/sharp-libvips-linux-ppc64": "1.3.1" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.2.tgz", + "integrity": "sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA==", "cpu": [ "riscv64" ], @@ -1296,19 +1622,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "@img/sharp-libvips-linux-riscv64": "1.3.1" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.2.tgz", + "integrity": "sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA==", "cpu": [ "s390x" ], @@ -1318,19 +1644,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "@img/sharp-libvips-linux-s390x": "1.3.1" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.2.tgz", + "integrity": "sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA==", "cpu": [ "x64" ], @@ -1340,19 +1666,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "@img/sharp-libvips-linux-x64": "1.3.1" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.2.tgz", + "integrity": "sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg==", "cpu": [ "arm64" ], @@ -1362,19 +1688,19 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.1" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.2.tgz", + "integrity": "sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg==", "cpu": [ "x64" ], @@ -1384,38 +1710,54 @@ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "1.3.1" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", - "cpu": [ - "wasm32" - ], + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.2.tgz", + "integrity": "sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==", "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "@emnapi/runtime": "^1.11.1" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.2.tgz", + "integrity": "sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.2" + }, + "engines": { + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.2.tgz", + "integrity": "sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==", "cpu": [ "arm64" ], @@ -1425,16 +1767,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.2.tgz", + "integrity": "sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==", "cpu": [ "ia32" ], @@ -1444,16 +1786,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.2.tgz", + "integrity": "sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==", "cpu": [ "x64" ], @@ -1463,7 +1805,7 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" @@ -1551,16 +1893,43 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, "node_modules/@oslojs/encoding": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", "license": "MIT" }, + "node_modules/@oxc-project/types": { + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, "node_modules/@pagefind/darwin-arm64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.4.0.tgz", - "integrity": "sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-arm64/-/darwin-arm64-1.5.2.tgz", + "integrity": "sha512-MXpI+7HsAdPkvJ0gk9xj9g541BCqBZOBbdwj9g6lB5LCj6kSV6nqDSjzcAJwvOsfu0fjwvC8hQU+ecfhp+MpiQ==", "cpu": [ "arm64" ], @@ -1571,9 +1940,9 @@ ] }, "node_modules/@pagefind/darwin-x64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.4.0.tgz", - "integrity": "sha512-e7JPIS6L9/cJfow+/IAqknsGqEPjJnVXGjpGm25bnq+NPdoD3c/7fAwr1OXkG4Ocjx6ZGSCijXEV4ryMcH2E3A==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/darwin-x64/-/darwin-x64-1.5.2.tgz", + "integrity": "sha512-IojxFWMEJe0RQ7PQ3KXQsPIImNsbpPYpoZ+QUDrL8fAl/O27IX+LVLs74/UzEZy5uA2LD8Nz1AiwKr72vrkZQw==", "cpu": [ "x64" ], @@ -1584,15 +1953,15 @@ ] }, "node_modules/@pagefind/default-ui": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/default-ui/-/default-ui-1.4.0.tgz", - "integrity": "sha512-wie82VWn3cnGEdIjh4YwNESyS1G6vRHwL6cNjy9CFgNnWW/PGRjsLq300xjVH5sfPFK3iK36UxvIBymtQIEiSQ==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/default-ui/-/default-ui-1.5.2.tgz", + "integrity": "sha512-pm1LMnQg8N2B3n2TnjKlhaFihpz6zTiA4HiGQ6/slKO/+8K9CAU5kcjdSSPgpuk1PMuuN4hxLipUIifnrkl3Sg==", "license": "MIT" }, "node_modules/@pagefind/freebsd-x64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.4.0.tgz", - "integrity": "sha512-WcJVypXSZ+9HpiqZjFXMUobfFfZZ6NzIYtkhQ9eOhZrQpeY5uQFqNWLCk7w9RkMUwBv1HAMDW3YJQl/8OqsV0Q==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/freebsd-x64/-/freebsd-x64-1.5.2.tgz", + "integrity": "sha512-7EVzo9+0w+2cbe671BtMj10UlNo83I+HrLVLfRxO731svHRJKUfJ/mo05gU14pe9PCfpKNQT8FS3Xc/oDN6pOA==", "cpu": [ "x64" ], @@ -1603,9 +1972,9 @@ ] }, "node_modules/@pagefind/linux-arm64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.4.0.tgz", - "integrity": "sha512-PIt8dkqt4W06KGmQjONw7EZbhDF+uXI7i0XtRLN1vjCUxM9vGPdtJc2mUyVPevjomrGz5M86M8bqTr6cgDp1Uw==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/linux-arm64/-/linux-arm64-1.5.2.tgz", + "integrity": "sha512-Ovt9+K35sqzn8H3ZMXGwls4TD/wMJuvRtShHIsmUQREmaxjrDEX7gHckRCrwYJ4XE1H1p6HkLz3wukrAnsfXQw==", "cpu": [ "arm64" ], @@ -1616,9 +1985,9 @@ ] }, "node_modules/@pagefind/linux-x64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.4.0.tgz", - "integrity": "sha512-z4oddcWwQ0UHrTHR8psLnVlz6USGJ/eOlDPTDYZ4cI8TK8PgwRUPQZp9D2iJPNIPcS6Qx/E4TebjuGJOyK8Mmg==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/linux-x64/-/linux-x64-1.5.2.tgz", + "integrity": "sha512-V+tFqHKXhQKq/WqPBD67AFy7scn1/aZID00ws4fSDd+1daSi5UHR9VVlRrOUYKxn3VuFQYRD7lYXdZK1WED1YA==", "cpu": [ "x64" ], @@ -1628,10 +1997,23 @@ "linux" ] }, + "node_modules/@pagefind/windows-arm64": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/windows-arm64/-/windows-arm64-1.5.2.tgz", + "integrity": "sha512-hN9Nh90fNW61nNRCW9ZyQrAj/mD0eRvmJ8NlTUzkbuW8kIzGJUi3cxjFkEcMZ5h/8FsKWD/VcouZl4yo1F7B6g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@pagefind/windows-x64": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.4.0.tgz", - "integrity": "sha512-NkT+YAdgS2FPCn8mIA9bQhiBs+xmniMGq1LFPDhcFn0+2yIUEiIG06t7bsZlhdjknEQRTSdT7YitP6fC5qwP0g==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@pagefind/windows-x64/-/windows-x64-1.5.2.tgz", + "integrity": "sha512-Fa2Iyw7kaDRzGMfNYNUXNW2zbL5FQVDgSOcbDHdzBrDEdpqOqg8TcZ68F22ol6NJ9IGzvUdmeyZypLW5dyhqsg==", "cpu": [ "x64" ], @@ -1690,10 +2072,258 @@ "node": ">=18" } }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz", + "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz", + "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz", + "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz", + "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz", + "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz", + "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz", + "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz", + "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz", + "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz", + "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz", + "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz", + "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz", + "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz", + "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz", + "integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "license": "MIT" + }, "node_modules/@rollup/pluginutils": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", - "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", @@ -1718,339 +2348,14 @@ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", "license": "MIT" }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.0.tgz", - "integrity": "sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.0.tgz", - "integrity": "sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.0.tgz", - "integrity": "sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.0.tgz", - "integrity": "sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.0.tgz", - "integrity": "sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.0.tgz", - "integrity": "sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.0.tgz", - "integrity": "sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.0.tgz", - "integrity": "sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.0.tgz", - "integrity": "sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.0.tgz", - "integrity": "sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.0.tgz", - "integrity": "sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.0.tgz", - "integrity": "sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.0.tgz", - "integrity": "sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.0.tgz", - "integrity": "sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.0.tgz", - "integrity": "sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.0.tgz", - "integrity": "sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.0.tgz", - "integrity": "sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.0.tgz", - "integrity": "sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.0.tgz", - "integrity": "sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.0.tgz", - "integrity": "sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.0.tgz", - "integrity": "sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.0.tgz", - "integrity": "sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.0.tgz", - "integrity": "sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.0.tgz", - "integrity": "sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.0.tgz", - "integrity": "sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, "node_modules/@shikijs/core": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.0.2.tgz", - "integrity": "sha512-hxT0YF4ExEqB8G/qFdtJvpmHXBYJ2lWW7qTHDarVkIudPFE6iCIrqdgWxGn5s+ppkGXI0aEGlibI0PAyzP3zlw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-4.3.0.tgz", + "integrity": "sha512-EooU3i9F6IAE8kEu+AnGf9DFZWkQBZ+hJn3tLVbsH+61mtQiva5biai66fAA6nvFPXkLgvrh7BrR7YcJU83xQQ==", "license": "MIT", "dependencies": { - "@shikijs/primitive": "4.0.2", - "@shikijs/types": "4.0.2", + "@shikijs/primitive": "4.3.0", + "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" @@ -2060,26 +2365,26 @@ } }, "node_modules/@shikijs/engine-javascript": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.0.2.tgz", - "integrity": "sha512-7PW0Nm49DcoUIQEXlJhNNBHyoGMjalRETTCcjMqEaMoJRLljy1Bi/EGV3/qLBgLKQejdspiiYuHGQW6dX94Nag==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-4.3.0.tgz", + "integrity": "sha512-hTv/KiFf2tpiqlACPiztGGurEARWIutB8YUhcrA1pUC7VzzwKO+g5crUocrLztrZ5ro5Z4hbXg7bYclETn3gSQ==", "license": "MIT", "dependencies": { - "@shikijs/types": "4.0.2", + "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.4" + "oniguruma-to-es": "^4.3.6" }, "engines": { "node": ">=20" } }, "node_modules/@shikijs/engine-oniguruma": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.0.2.tgz", - "integrity": "sha512-UpCB9Y2sUKlS9z8juFSKz7ZtysmeXCgnRF0dlhXBkmQnek7lAToPte8DkxmEYGNTMii72zU/lyXiCB6StuZeJg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-4.3.0.tgz", + "integrity": "sha512-1vMdN3gHfnKfLYwecUI2ITJI4RhHt96xEaJumVn7Heb0IlJ8WQMIH0Voak+2j22BpSNKdnOfB/pCTPnPm2gq7A==", "license": "MIT", "dependencies": { - "@shikijs/types": "4.0.2", + "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2" }, "engines": { @@ -2087,24 +2392,24 @@ } }, "node_modules/@shikijs/langs": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.0.2.tgz", - "integrity": "sha512-KaXby5dvoeuZzN0rYQiPMjFoUrz4hgwIE+D6Du9owcHcl6/g16/yT5BQxSW5cGt2MZBz6Hl0YuRqf12omRfUUg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-4.3.0.tgz", + "integrity": "sha512-rnlqFbBRSys9bT4gl/5rw9RnS0W/I84ZldXPkO7cvlEMoV85TyF/aU01N7/NbSR776RNLjrJKjfFUXJR6wN1Cg==", "license": "MIT", "dependencies": { - "@shikijs/types": "4.0.2" + "@shikijs/types": "4.3.0" }, "engines": { "node": ">=20" } }, "node_modules/@shikijs/primitive": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.0.2.tgz", - "integrity": "sha512-M6UMPrSa3fN5ayeJwFVl9qWofl273wtK1VG8ySDZ1mQBfhCpdd8nEx7nPZ/tk7k+TYcpqBZzj/AnwxT9lO+HJw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@shikijs/primitive/-/primitive-4.3.0.tgz", + "integrity": "sha512-CPkz64PTa5diRW1ggzMZH9VM/du4RNChYgVtgqrFcgruvIybmCvySv8GkiHSczUHXYuuR8TdKEwFx+UnZMpgdg==", "license": "MIT", "dependencies": { - "@shikijs/types": "4.0.2", + "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" }, @@ -2113,21 +2418,21 @@ } }, "node_modules/@shikijs/themes": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.0.2.tgz", - "integrity": "sha512-mjCafwt8lJJaVSsQvNVrJumbnnj1RI8jbUKrPKgE6E3OvQKxnuRoBaYC51H4IGHePsGN/QtALglWBU7DoKDFnA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-4.3.0.tgz", + "integrity": "sha512-Avgt05YiT+Y3prjIc9lmQxhJzHBcCfR6cjiFW4OyaMBbt2A6trX5rfjUzx+Vj/mE9qpArYjatnqo9XPjQNW/AQ==", "license": "MIT", "dependencies": { - "@shikijs/types": "4.0.2" + "@shikijs/types": "4.3.0" }, "engines": { "node": ">=20" } }, "node_modules/@shikijs/types": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.0.2.tgz", - "integrity": "sha512-qzbeRooUTPnLE+sHD/Z8DStmaDgnbbc/pMrU203950aRqjX/6AFHeDYT+j00y2lPdz0ywJKx7o/7qnqTivtlXg==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-4.3.0.tgz", + "integrity": "sha512-oc8b9U2SYvofKZk8e/737nIX0qwf6eV2vHFATeObAu7r+mUVpLs8Re0BmVkIjAWAYgkmG/CzLNo7rzuBzRu/wQ==", "license": "MIT", "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", @@ -2153,84 +2458,66 @@ } }, "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "6.2.4", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-6.2.4.tgz", - "integrity": "sha512-ou/d51QSdTyN26D7h6dSpusAKaZkAiGM55/AKYi+9AGZw7q85hElbjK3kEyzXHhLSnRISHOYzVge6x0jRZ7DXA==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.1.2.tgz", + "integrity": "sha512-DrUBA2UXRfDmUX/ZTiEopd3X40yavsJF1FX2RygcuIScHL7o5YX1fMvoYnDhjeJQC4weCOklirpNWlcb2NiSeA==", "license": "MIT", "dependencies": { - "@sveltejs/vite-plugin-svelte-inspector": "^5.0.0", "deepmerge": "^4.3.1", "magic-string": "^0.30.21", "obug": "^2.1.0", - "vitefu": "^1.1.1" + "vitefu": "^1.1.2" }, "engines": { "node": "^20.19 || ^22.12 || >=24" }, "peerDependencies": { - "svelte": "^5.0.0", - "vite": "^6.3.0 || ^7.0.0" - } - }, - "node_modules/@sveltejs/vite-plugin-svelte-inspector": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-5.0.2.tgz", - "integrity": "sha512-TZzRTcEtZffICSAoZGkPSl6Etsj2torOVrx6Uw0KpXxrec9Gg6jFWQ60Q3+LmNGfZSxHRCZL7vXVZIWmuV50Ig==", - "license": "MIT", - "dependencies": { - "obug": "^2.1.0" - }, - "engines": { - "node": "^20.19 || ^22.12 || >=24" - }, - "peerDependencies": { - "@sveltejs/vite-plugin-svelte": "^6.0.0-next.0", - "svelte": "^5.0.0", - "vite": "^6.3.0 || ^7.0.0" + "svelte": "^5.46.4", + "vite": "^8.0.0-beta.7 || ^8.0.0" } }, "node_modules/@tailwindcss/node": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", - "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.2.tgz", + "integrity": "sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==", "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "^5.21.0", - "jiti": "^2.6.1", + "enhanced-resolve": "5.21.6", + "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.3.0" + "tailwindcss": "4.3.2" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz", - "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.2.tgz", + "integrity": "sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==", "license": "MIT", "engines": { "node": ">= 20" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.3.0", - "@tailwindcss/oxide-darwin-arm64": "4.3.0", - "@tailwindcss/oxide-darwin-x64": "4.3.0", - "@tailwindcss/oxide-freebsd-x64": "4.3.0", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", - "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", - "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", - "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", - "@tailwindcss/oxide-linux-x64-musl": "4.3.0", - "@tailwindcss/oxide-wasm32-wasi": "4.3.0", - "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", - "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" + "@tailwindcss/oxide-android-arm64": "4.3.2", + "@tailwindcss/oxide-darwin-arm64": "4.3.2", + "@tailwindcss/oxide-darwin-x64": "4.3.2", + "@tailwindcss/oxide-freebsd-x64": "4.3.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.2", + "@tailwindcss/oxide-linux-x64-musl": "4.3.2", + "@tailwindcss/oxide-wasm32-wasi": "4.3.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.2" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz", - "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.2.tgz", + "integrity": "sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==", "cpu": [ "arm64" ], @@ -2244,9 +2531,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz", - "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.2.tgz", + "integrity": "sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==", "cpu": [ "arm64" ], @@ -2260,9 +2547,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz", - "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.2.tgz", + "integrity": "sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==", "cpu": [ "x64" ], @@ -2276,9 +2563,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz", - "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.2.tgz", + "integrity": "sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==", "cpu": [ "x64" ], @@ -2292,9 +2579,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz", - "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.2.tgz", + "integrity": "sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==", "cpu": [ "arm" ], @@ -2308,9 +2595,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz", - "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.2.tgz", + "integrity": "sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==", "cpu": [ "arm64" ], @@ -2324,9 +2611,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz", - "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.2.tgz", + "integrity": "sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==", "cpu": [ "arm64" ], @@ -2340,9 +2627,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz", - "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.2.tgz", + "integrity": "sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==", "cpu": [ "x64" ], @@ -2356,9 +2643,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz", - "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.2.tgz", + "integrity": "sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==", "cpu": [ "x64" ], @@ -2372,9 +2659,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz", - "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.2.tgz", + "integrity": "sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -2389,81 +2676,21 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.10.0", - "@emnapi/runtime": "^1.10.0", - "@emnapi/wasi-threads": "^1.2.1", + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", "@napi-rs/wasm-runtime": "^1.1.4", - "@tybys/wasm-util": "^0.10.1", + "@tybys/wasm-util": "^0.10.2", "tslib": "^2.8.1" }, "engines": { "node": ">=14.0.0" } }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { - "version": "1.10.0", - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.2.1", - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { - "version": "1.10.0", - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@tybys/wasm-util": "^0.10.1" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - }, - "peerDependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "inBundle": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { - "version": "2.8.1", - "inBundle": true, - "license": "0BSD", - "optional": true - }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", - "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.2.tgz", + "integrity": "sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==", "cpu": [ "arm64" ], @@ -2477,9 +2704,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz", - "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.2.tgz", + "integrity": "sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ==", "cpu": [ "x64" ], @@ -2493,19 +2720,29 @@ } }, "node_modules/@tailwindcss/vite": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz", - "integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.2.tgz", + "integrity": "sha512-eHpMeX4JXfVNJDEcsouTeCBubJBTcTLigeaw/NTUW6PB5ATKKXdyonnXgTBX2VuRbjz1hjfz6C5XAhr52ImQXA==", "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.3.0", - "@tailwindcss/oxide": "4.3.0", - "tailwindcss": "4.3.0" + "@tailwindcss/node": "4.3.2", + "@tailwindcss/oxide": "4.3.2", + "tailwindcss": "4.3.2" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/debug": { "version": "4.1.13", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", @@ -2516,9 +2753,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "license": "MIT" }, "node_modules/@types/estree-jsx": { @@ -2582,12 +2819,12 @@ } }, "node_modules/@types/node": { - "version": "24.12.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", - "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", + "version": "24.13.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.2.tgz", + "integrity": "sha512-fRa09kZTgu8o71KFcDjUFuc7F+dEbZYZmkI0mg5YBTRs0yMKjYHsq/c0urDKeDb+D5qVgXOdFcuu+DZPKOITwA==", "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~7.18.0" } }, "node_modules/@types/sax": { @@ -2612,9 +2849,9 @@ "license": "MIT" }, "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.2.tgz", + "integrity": "sha512-5jsZFwgR5rTdKwidH9Qmat75RKwqfpKlWWB1frDkljN127mwqBu8K0PYo7/hFpF03IEJpfVPpCQDY/eDx3iHvA==", "license": "ISC" }, "node_modules/@volar/kit": { @@ -2701,6 +2938,12 @@ "vscode-uri": "^3.0.8" } }, + "node_modules/@vscode/emmet-helper/node_modules/jsonc-parser": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", + "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==", + "license": "MIT" + }, "node_modules/@vscode/l10n": { "version": "0.0.18", "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", @@ -2708,9 +2951,9 @@ "license": "MIT" }, "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -2729,9 +2972,9 @@ } }, "node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -2758,6 +3001,18 @@ } } }, + "node_modules/am-i-vibing": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/am-i-vibing/-/am-i-vibing-0.4.0.tgz", + "integrity": "sha512-MxT4XZL7pzLHpuvhDKdMaQHMGGkJDLluKBLsbstn+8wv9sWcFT6h+0ve9qkml95amVTZtZV83gQe2hY+ojgHLg==", + "license": "MIT", + "dependencies": { + "process-ancestry": "^0.1.0" + }, + "bin": { + "am-i-vibing": "dist/cli.mjs" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -2848,19 +3103,20 @@ } }, "node_modules/astro": { - "version": "6.4.8", - "resolved": "https://registry.npmjs.org/astro/-/astro-6.4.8.tgz", - "integrity": "sha512-KK5lX90uU9EeVaTjINyj3sy9/NFXVa59aowaqbWBDDKLXZh4rr7GwIaCFYVetE22MJtsCNFerQXn0vlCLmpP/Q==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/astro/-/astro-7.0.4.tgz", + "integrity": "sha512-6DSiJ4CT69vngNlwEKec7nGOJHcPzc95lX7E6qkUhAt6drGYzUdf0KTJToswu3vFp3lTCGnNDCzBi/5aW8tTPA==", "license": "MIT", "dependencies": { - "@astrojs/compiler": "^4.0.0", + "@astrojs/compiler-rs": "^0.3.0", "@astrojs/internal-helpers": "0.10.0", - "@astrojs/markdown-remark": "7.2.0", + "@astrojs/markdown-satteri": "0.3.2", "@astrojs/telemetry": "3.3.2", "@capsizecss/unpack": "^4.0.0", "@clack/prompts": "^1.1.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.3.0", + "am-i-vibing": "^0.4.0", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "ci-info": "^4.4.0", @@ -2871,7 +3127,7 @@ "diff": "^8.0.3", "dset": "^3.1.4", "es-module-lexer": "^2.0.0", - "esbuild": "^0.27.3", + "esbuild": "^0.28.0", "flattie": "^1.1.1", "fontace": "~0.4.1", "get-tsconfig": "5.0.0-beta.4", @@ -2903,7 +3159,7 @@ "unist-util-visit": "^5.1.0", "unstorage": "^1.17.5", "vfile": "^6.0.3", - "vite": "^7.3.2", + "vite": "^8.0.13", "vitefu": "^1.1.2", "xxhash-wasm": "^1.1.0", "yargs-parser": "^22.0.0", @@ -2922,33 +3178,30 @@ "url": "https://opencollective.com/astrodotbuild" }, "optionalDependencies": { - "sharp": "^0.34.0" + "sharp": "^0.34.0 || ^0.35.0" + }, + "peerDependencies": { + "@astrojs/markdown-remark": "7.2.0" + }, + "peerDependenciesMeta": { + "@astrojs/markdown-remark": { + "optional": true + } } }, "node_modules/astro-expressive-code": { - "version": "0.42.0", - "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.42.0.tgz", - "integrity": "sha512-aiTePi2Cn0mJPYWZSzP1GcxCinX9mNtJyCCshVVPSg1yRwM7ADvFJOx0FnS440M9t65hp8JH//dc2qr22Bm4ag==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/astro-expressive-code/-/astro-expressive-code-0.44.0.tgz", + "integrity": "sha512-b1wN/ZvbJprzxlGKIpIes2kQrCY5KRLwys2tWbZAZyjGZcW5ZtgneZnBwzNRiBna9/48d4mQl19KLjcRuhO1hw==", "license": "MIT", "dependencies": { - "rehype-expressive-code": "^0.42.0" + "rehype-expressive-code": "^0.44.0", + "url-extras": "^0.1.0" }, "peerDependencies": { - "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta" + "astro": "^4.0.0-beta || ^5.0.0-beta || ^3.3.0 || ^6.0.0-beta || ^7.0.0" } }, - "node_modules/astro/node_modules/@astrojs/compiler": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-4.0.0.tgz", - "integrity": "sha512-eouss7G8ygdZqHuke033VMcVw5HTZUu+PXd/h06DGDUg/jt5btPYPqh66ENWw/mU78rBrf/oeC4oqoBwMtDMNA==", - "license": "MIT" - }, - "node_modules/astro/node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "license": "MIT" - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -3603,9 +3856,9 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.23.0", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.23.0.tgz", - "integrity": "sha512-yJN/BOOLxcOW2aQgeif9mSnaUB8KtvmMMp56oA1kx1CRfBKbhZm2pJ+NBY+3eOboHxix8lfjWpHE0Ei5U8RbSA==", + "version": "5.21.6", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz", + "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", @@ -3646,15 +3899,15 @@ } }, "node_modules/es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.2.0.tgz", + "integrity": "sha512-3lGxdTXCLfe1MYfTz1y2ksAAUM4NAOP6rPEjxGJVKO7TZ5+tvHCaQWGpC4Y3IXvW3ece0Cz1cIP4FWBxOnGCTQ==", "license": "MIT" }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -3711,9 +3964,9 @@ } }, "node_modules/esbuild": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", - "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", "hasInstallScript": true, "license": "MIT", "bin": { @@ -3723,32 +3976,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.4", - "@esbuild/android-arm": "0.27.4", - "@esbuild/android-arm64": "0.27.4", - "@esbuild/android-x64": "0.27.4", - "@esbuild/darwin-arm64": "0.27.4", - "@esbuild/darwin-x64": "0.27.4", - "@esbuild/freebsd-arm64": "0.27.4", - "@esbuild/freebsd-x64": "0.27.4", - "@esbuild/linux-arm": "0.27.4", - "@esbuild/linux-arm64": "0.27.4", - "@esbuild/linux-ia32": "0.27.4", - "@esbuild/linux-loong64": "0.27.4", - "@esbuild/linux-mips64el": "0.27.4", - "@esbuild/linux-ppc64": "0.27.4", - "@esbuild/linux-riscv64": "0.27.4", - "@esbuild/linux-s390x": "0.27.4", - "@esbuild/linux-x64": "0.27.4", - "@esbuild/netbsd-arm64": "0.27.4", - "@esbuild/netbsd-x64": "0.27.4", - "@esbuild/openbsd-arm64": "0.27.4", - "@esbuild/openbsd-x64": "0.27.4", - "@esbuild/openharmony-arm64": "0.27.4", - "@esbuild/sunos-x64": "0.27.4", - "@esbuild/win32-arm64": "0.27.4", - "@esbuild/win32-ia32": "0.27.4", - "@esbuild/win32-x64": "0.27.4" + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" } }, "node_modules/escalade": { @@ -3785,9 +4038,9 @@ "license": "MIT" }, "node_modules/esrap": { - "version": "2.2.9", - "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.9.tgz", - "integrity": "sha512-4KijP+NxCWthMCUC3qHbE6n4vCjqgJS1uAYKhuT/GWfFTf1Qyive2TgOjep+gzbSzRfnNyaN/UU9YmdOt8Eg0A==", + "version": "2.2.13", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.13.tgz", + "integrity": "sha512-m8jH5hZgJE2RRUK/jjkGPcJEDAV+dYnZYFkosQaPTcE+Yw4xynXHOo6FUdwaWBtdR3b1MMa7wEDTSHeR2VWsGA==", "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -3923,15 +4176,15 @@ "license": "MIT" }, "node_modules/expressive-code": { - "version": "0.42.0", - "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.42.0.tgz", - "integrity": "sha512-V5DtJLEKuj4wf9O6IRtPtRObkMVy2ggR+S0MdjrTw6m58krZnDioyhW1si3Y04c5YPeooP4nd85Yq9NwEVHS4g==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/expressive-code/-/expressive-code-0.44.0.tgz", + "integrity": "sha512-JXVWVNCKlLuZLMQH8cOiDUSosT0Bb+elwE/dbAkpwFwDFmyFyWlECoWZIohh2FkIF1iI67TQJ+Ts9k7oNDh2qA==", "license": "MIT", "dependencies": { - "@expressive-code/core": "^0.42.0", - "@expressive-code/plugin-frames": "^0.42.0", - "@expressive-code/plugin-shiki": "^0.42.0", - "@expressive-code/plugin-text-markers": "^0.42.0" + "@expressive-code/core": "^0.44.0", + "@expressive-code/plugin-frames": "^0.44.0", + "@expressive-code/plugin-shiki": "^0.44.0", + "@expressive-code/plugin-text-markers": "^0.44.0" } }, "node_modules/extend": { @@ -3946,10 +4199,25 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-string-truncated-width": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", + "integrity": "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g==", + "license": "MIT" + }, + "node_modules/fast-string-width": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-string-width/-/fast-string-width-3.0.2.tgz", + "integrity": "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg==", + "license": "MIT", + "dependencies": { + "fast-string-truncated-width": "^3.0.2" + } + }, "node_modules/fast-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", - "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz", + "integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==", "funding": [ { "type": "github", @@ -3962,6 +4230,15 @@ ], "license": "BSD-3-Clause" }, + "node_modules/fast-wrap-ansi": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fast-wrap-ansi/-/fast-wrap-ansi-0.2.2.tgz", + "integrity": "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q==", + "license": "MIT", + "dependencies": { + "fast-string-width": "^3.0.2" + } + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -4235,9 +4512,9 @@ } }, "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -4684,9 +4961,9 @@ } }, "node_modules/i18next": { - "version": "26.3.1", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.3.1.tgz", - "integrity": "sha512-txQqd5EULsqEh9OJqRH15aCaOuy/nLJyhw5EHCSKLKJE1aBbb3Zve2+uQIxgWhPm1QqUQoWyQBm2kfmmIrzkcQ==", + "version": "26.3.4", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-26.3.4.tgz", + "integrity": "sha512-pa7m0d7pBDqGHZxljT+WPFeyFgQ7P7SciPPo1tTqYuO0z4sqADYhwnBESmmGp/wEof1inwdls/k8ZgTg8rxFHA==", "funding": [ { "type": "individual", @@ -4888,9 +5165,9 @@ } }, "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" @@ -4903,9 +5180,19 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -4921,9 +5208,9 @@ "license": "MIT" }, "node_modules/jsonc-parser": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", - "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", "license": "MIT" }, "node_modules/jsonpointer": { @@ -5228,9 +5515,9 @@ } }, "node_modules/lru-cache": { - "version": "11.3.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", - "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -5246,12 +5533,12 @@ } }, "node_modules/magicast": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", - "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.3.tgz", + "integrity": "sha512-pVKE4UdSQ7DvHzivsCIFx2BJn1mHG6KsyrFcaxFx6tONdneEuThrDx0Cj3AMg58KyN4pzYT+LHOotxDQDjNvkw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", + "@babel/parser": "^7.29.3", "@babel/types": "^7.29.0", "source-map-js": "^1.2.1" } @@ -6405,9 +6692,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", "funding": [ { "type": "github", @@ -6478,14 +6765,17 @@ } }, "node_modules/obug": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", - "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", + "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", "funding": [ "https://github.com/sponsors/sxzz", "https://opencollective.com/debug" ], - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } }, "node_modules/ofetch": { "version": "1.5.1", @@ -6517,18 +6807,18 @@ } }, "node_modules/oniguruma-parser": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", - "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", + "version": "0.12.2", + "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.2.tgz", + "integrity": "sha512-6HVa5oIrgMC6aA6WF6XyyqbhRPJrKR02L20+2+zpDtO5QAzGHAUGw5TKQvwi5vctNnRHkJYmjAhRVQF2EKdTQw==", "license": "MIT" }, "node_modules/oniguruma-to-es": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.5.tgz", - "integrity": "sha512-Zjygswjpsewa0NLTsiizVuMQZbp0MDyM6lIt66OxsF21npUDlzpHi1Mgb/qhQdkb+dWFTzJmFbEWdvZgRho8eQ==", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.6.tgz", + "integrity": "sha512-csuQ9x3Yr0cEIs/Zgx/OEt9iBw9vqIunAPQkx19R/fiMq2oGVTgcMqO/V3Ybqefr1TBvosI6jU539ksaBULJyA==", "license": "MIT", "dependencies": { - "oniguruma-parser": "^0.12.1", + "oniguruma-parser": "^0.12.2", "regex": "^6.1.0", "regex-recursion": "^6.0.2" } @@ -6556,12 +6846,12 @@ } }, "node_modules/p-queue": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.1.0.tgz", - "integrity": "sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.3.0.tgz", + "integrity": "sha512-7NED7xhQ74Ngp4JP/2e0VZHp7vSWfJfqeiR92jPgxsz6m0Se4P03YoTKa9dDXyZ3r6P616gUXttrB6nnHYKang==", "license": "MIT", "dependencies": { - "eventemitter3": "^5.0.1", + "eventemitter3": "^5.0.4", "p-timeout": "^7.0.0" }, "engines": { @@ -6584,26 +6874,27 @@ } }, "node_modules/package-manager-detector": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.6.0.tgz", - "integrity": "sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.7.0.tgz", + "integrity": "sha512-xg1eHpwYL/D/HEdWw2goFZP6vV0FH7W+PZ5rFkGjdIDLtxq7EkzBUeT3m+lndYCt8wKbmofUu1MUdMCXkCk9ZQ==", "license": "MIT" }, "node_modules/pagefind": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.4.0.tgz", - "integrity": "sha512-z2kY1mQlL4J8q5EIsQkLzQjilovKzfNVhX8De6oyE6uHpfFtyBaqUpcl/XzJC/4fjD8vBDyh1zolimIcVrCn9g==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/pagefind/-/pagefind-1.5.2.tgz", + "integrity": "sha512-XTUaK0hXMCu2jszWE584JGQT7y284TmMV9l/HX3rnG5uo3rHI/uHU56XTyyyPFjeWEBxECbAi0CaFDJOONtG0Q==", "license": "MIT", "bin": { "pagefind": "lib/runner/bin.cjs" }, "optionalDependencies": { - "@pagefind/darwin-arm64": "1.4.0", - "@pagefind/darwin-x64": "1.4.0", - "@pagefind/freebsd-x64": "1.4.0", - "@pagefind/linux-arm64": "1.4.0", - "@pagefind/linux-x64": "1.4.0", - "@pagefind/windows-x64": "1.4.0" + "@pagefind/darwin-arm64": "1.5.2", + "@pagefind/darwin-x64": "1.5.2", + "@pagefind/freebsd-x64": "1.5.2", + "@pagefind/linux-arm64": "1.5.2", + "@pagefind/linux-x64": "1.5.2", + "@pagefind/windows-arm64": "1.5.2", + "@pagefind/windows-x64": "1.5.2" } }, "node_modules/parse-entities": { @@ -6704,9 +6995,9 @@ } }, "node_modules/postcss": { - "version": "8.5.14", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", - "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "version": "8.5.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", + "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", "funding": [ { "type": "opencollective", @@ -6723,7 +7014,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -6757,9 +7048,9 @@ } }, "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz", + "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==", "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -6770,9 +7061,9 @@ } }, "node_modules/prettier": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", - "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "version": "3.9.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.4.tgz", + "integrity": "sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==", "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" @@ -6793,10 +7084,19 @@ "node": ">=6" } }, + "node_modules/process-ancestry": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/process-ancestry/-/process-ancestry-0.1.0.tgz", + "integrity": "sha512-tGqJW/UnclpYASFcM6Xh8D8l/BMtaQ9+CSG0vlJSJTcdMM4lDRv4c6H0Pdcsfted+bVczdYSfk2fdukg2gQkZg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", "license": "MIT", "funding": { "type": "github", @@ -6810,12 +7110,16 @@ "license": "MIT" }, "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", + "integrity": "sha512-hek2mFQpPuI4E1BBKrSto+BU3e3x4xuarsbiwr3+lf7p44juvFMV0XFWQAP3xUyqXA4RrXLIoaSUGbSt056ZMw==", "license": "MIT", "engines": { "node": ">= 0.6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/readdirp": { @@ -6939,12 +7243,12 @@ } }, "node_modules/rehype-expressive-code": { - "version": "0.42.0", - "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.42.0.tgz", - "integrity": "sha512-8rp/1YMEVVSYbtz+bFBx+uSx3vA4i4T8RwRm5Q/IWbucQnnQqQ0hDqtmKOr8tv+59Cik6cu5aH3WPo0I7csuTA==", + "version": "0.44.0", + "resolved": "https://registry.npmjs.org/rehype-expressive-code/-/rehype-expressive-code-0.44.0.tgz", + "integrity": "sha512-5r74C5F2sMR3X+QJH8OKWgZBO/cqRw5W1fLT6GVlSfLqepk+4j8tGFkyPqZYWjwntsBHzKPDH2zI68sZ7ScLfA==", "license": "MIT", "dependencies": { - "expressive-code": "^0.42.0" + "expressive-code": "^0.44.0" } }, "node_modules/rehype-format": { @@ -7226,48 +7530,59 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/rollup": { - "version": "4.60.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.0.tgz", - "integrity": "sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==", + "node_modules/rolldown": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz", + "integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==", "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@oxc-project/types": "=0.137.0", + "@rolldown/pluginutils": "^1.0.0" }, "bin": { - "rollup": "dist/bin/rollup" + "rolldown": "bin/cli.mjs" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.0", - "@rollup/rollup-android-arm64": "4.60.0", - "@rollup/rollup-darwin-arm64": "4.60.0", - "@rollup/rollup-darwin-x64": "4.60.0", - "@rollup/rollup-freebsd-arm64": "4.60.0", - "@rollup/rollup-freebsd-x64": "4.60.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.0", - "@rollup/rollup-linux-arm-musleabihf": "4.60.0", - "@rollup/rollup-linux-arm64-gnu": "4.60.0", - "@rollup/rollup-linux-arm64-musl": "4.60.0", - "@rollup/rollup-linux-loong64-gnu": "4.60.0", - "@rollup/rollup-linux-loong64-musl": "4.60.0", - "@rollup/rollup-linux-ppc64-gnu": "4.60.0", - "@rollup/rollup-linux-ppc64-musl": "4.60.0", - "@rollup/rollup-linux-riscv64-gnu": "4.60.0", - "@rollup/rollup-linux-riscv64-musl": "4.60.0", - "@rollup/rollup-linux-s390x-gnu": "4.60.0", - "@rollup/rollup-linux-x64-gnu": "4.60.0", - "@rollup/rollup-linux-x64-musl": "4.60.0", - "@rollup/rollup-openbsd-x64": "4.60.0", - "@rollup/rollup-openharmony-arm64": "4.60.0", - "@rollup/rollup-win32-arm64-msvc": "4.60.0", - "@rollup/rollup-win32-ia32-msvc": "4.60.0", - "@rollup/rollup-win32-x64-gnu": "4.60.0", - "@rollup/rollup-win32-x64-msvc": "4.60.0", - "fsevents": "~2.3.2" + "@rolldown/binding-android-arm64": "1.1.3", + "@rolldown/binding-darwin-arm64": "1.1.3", + "@rolldown/binding-darwin-x64": "1.1.3", + "@rolldown/binding-freebsd-x64": "1.1.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.3", + "@rolldown/binding-linux-arm64-gnu": "1.1.3", + "@rolldown/binding-linux-arm64-musl": "1.1.3", + "@rolldown/binding-linux-ppc64-gnu": "1.1.3", + "@rolldown/binding-linux-s390x-gnu": "1.1.3", + "@rolldown/binding-linux-x64-gnu": "1.1.3", + "@rolldown/binding-linux-x64-musl": "1.1.3", + "@rolldown/binding-openharmony-arm64": "1.1.3", + "@rolldown/binding-wasm32-wasi": "1.1.3", + "@rolldown/binding-win32-arm64-msvc": "1.1.3", + "@rolldown/binding-win32-x64-msvc": "1.1.3" + } + }, + "node_modules/satteri": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/satteri/-/satteri-0.9.4.tgz", + "integrity": "sha512-BKob126Tay84diOZsnVNH/Q/c+3njPJTCad3w5zLKa6j8bVjxskPNHDtxrMwYK4bN/RlqUSdMnPwKY4k65EMOQ==", + "dependencies": { + "@types/estree-jsx": "^1.0.5", + "@types/hast": "^3.0.4", + "@types/mdast": "^4.0.4", + "@types/unist": "^3.0.3" + }, + "optionalDependencies": { + "@bruits/satteri-darwin-arm64": "0.9.4", + "@bruits/satteri-darwin-x64": "0.9.4", + "@bruits/satteri-linux-arm64-gnu": "0.9.4", + "@bruits/satteri-linux-arm64-musl": "0.9.4", + "@bruits/satteri-linux-x64-gnu": "0.9.4", + "@bruits/satteri-linux-x64-musl": "0.9.4", + "@bruits/satteri-wasm32-wasi": "0.9.4", + "@bruits/satteri-win32-arm64-msvc": "0.9.4", + "@bruits/satteri-win32-x64-msvc": "0.9.4" } }, "node_modules/sax": { @@ -7286,9 +7601,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -7336,61 +7651,61 @@ "license": "ISC" }, "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", - "hasInstallScript": true, + "version": "0.35.2", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.2.tgz", + "integrity": "sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==", "license": "Apache-2.0", "dependencies": { - "@img/colour": "^1.0.0", + "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "semver": "^7.8.4" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" + "@img/sharp-darwin-arm64": "0.35.2", + "@img/sharp-darwin-x64": "0.35.2", + "@img/sharp-freebsd-wasm32": "0.35.2", + "@img/sharp-libvips-darwin-arm64": "1.3.1", + "@img/sharp-libvips-darwin-x64": "1.3.1", + "@img/sharp-libvips-linux-arm": "1.3.1", + "@img/sharp-libvips-linux-arm64": "1.3.1", + "@img/sharp-libvips-linux-ppc64": "1.3.1", + "@img/sharp-libvips-linux-riscv64": "1.3.1", + "@img/sharp-libvips-linux-s390x": "1.3.1", + "@img/sharp-libvips-linux-x64": "1.3.1", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.1", + "@img/sharp-libvips-linuxmusl-x64": "1.3.1", + "@img/sharp-linux-arm": "0.35.2", + "@img/sharp-linux-arm64": "0.35.2", + "@img/sharp-linux-ppc64": "0.35.2", + "@img/sharp-linux-riscv64": "0.35.2", + "@img/sharp-linux-s390x": "0.35.2", + "@img/sharp-linux-x64": "0.35.2", + "@img/sharp-linuxmusl-arm64": "0.35.2", + "@img/sharp-linuxmusl-x64": "0.35.2", + "@img/sharp-webcontainers-wasm32": "0.35.2", + "@img/sharp-win32-arm64": "0.35.2", + "@img/sharp-win32-ia32": "0.35.2", + "@img/sharp-win32-x64": "0.35.2" } }, "node_modules/shiki": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.0.2.tgz", - "integrity": "sha512-eAVKTMedR5ckPo4xne/PjYQYrU3qx78gtJZ+sHlXEg5IHhhoQhMfZVzetTYuaJS0L2Ef3AcCRzCHV8T0WI6nIQ==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-4.3.0.tgz", + "integrity": "sha512-NKKjWzR6LIGL3sXBrWDw9sDS9cxx42/DkysaNqJEeOWE8Kix5gpak0bc00OfDVEO4oyXSyz8+aRaqKoBD1yo7A==", "license": "MIT", "dependencies": { - "@shikijs/core": "4.0.2", - "@shikijs/engine-javascript": "4.0.2", - "@shikijs/engine-oniguruma": "4.0.2", - "@shikijs/langs": "4.0.2", - "@shikijs/themes": "4.0.2", - "@shikijs/types": "4.0.2", + "@shikijs/core": "4.3.0", + "@shikijs/engine-javascript": "4.3.0", + "@shikijs/engine-oniguruma": "4.3.0", + "@shikijs/langs": "4.3.0", + "@shikijs/themes": "4.3.0", + "@shikijs/types": "4.3.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" }, @@ -7424,9 +7739,9 @@ } }, "node_modules/smol-toml": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.1.tgz", - "integrity": "sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.7.0.tgz", + "integrity": "sha512-aqVvWoyO21L23mb+drl4RmMXbf6N7FdHjAhTRA9ZBL7apWBgfWC16KjrASI+1p9GAroljyMHj6fK67i0UiTNvQ==", "license": "BSD-3-Clause", "engines": { "node": ">= 18" @@ -7476,9 +7791,9 @@ } }, "node_modules/starlight-openapi": { - "version": "0.25.3", - "resolved": "https://registry.npmjs.org/starlight-openapi/-/starlight-openapi-0.25.3.tgz", - "integrity": "sha512-erFtGyU1NI1mHmlRrVo/SWLdVvjFr1Qsxwz1bnOss/rOSBH3Tgq39rSlkXr9HWbSTnbjIpZkitgYw+s73zk5pA==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/starlight-openapi/-/starlight-openapi-0.26.0.tgz", + "integrity": "sha512-+WJnS7nIW42KfgmtBvTdj0Sx0LR+3kYPCdXMoiiMxKWDTF9TBd8KhTRDqNiPPrksej+gItZV/ROKwsXoF2/qZQ==", "license": "MIT", "dependencies": { "@readme/openapi-parser": "^4.1.2", @@ -7490,9 +7805,9 @@ "node": ">=22.12.0" }, "peerDependencies": { - "@astrojs/markdown-remark": ">=7.0.0", - "@astrojs/starlight": ">=0.38.0", - "astro": ">=6.0.0" + "@astrojs/markdown-satteri": ">=0.3.2", + "@astrojs/starlight": ">=0.41.0", + "astro": ">=7.0.2" } }, "node_modules/statuses": { @@ -7605,9 +7920,9 @@ } }, "node_modules/svelte": { - "version": "5.56.0", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.56.0.tgz", - "integrity": "sha512-kTXr26t1bchFp28ROrb957LtbujpBmBDibmqMGziVpUs7awBi96TGgX6SovrA8BNoEUDVRK2Fb9FkeYlGspoVg==", + "version": "5.56.4", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.56.4.tgz", + "integrity": "sha512-/d0QHehmRuJW8gVz395MTkPcPozxzdjBMBE8oEYGz8O3b9KTMzzQ9ZHJQLuFKOHOPQbU6kx/X4iid/EBBzH7iw==", "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.4", @@ -7621,7 +7936,7 @@ "clsx": "^2.1.1", "devalue": "^5.8.1", "esm-env": "^1.2.1", - "esrap": "^2.2.9", + "esrap": "^2.2.12", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", @@ -7641,9 +7956,9 @@ } }, "node_modules/svelte2tsx": { - "version": "0.7.55", - "resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.7.55.tgz", - "integrity": "sha512-JWzgeM3lqySRNfqcsesvVEh8LhTWBxQJ9RMjzJ+VepdmXtVnNd0SbtGctG6+/fbHq0N6mhwSd823gszw9JHeGQ==", + "version": "0.7.57", + "resolved": "https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.7.57.tgz", + "integrity": "sha512-nQo0xEfUpSVjfkxan2UmC6Wl9UZVe9+/pglUiyBNMJ7KDQ9DDooucmXdToBOvzSfgYjj/kMYwf6CTTDz/z048w==", "license": "MIT", "dependencies": { "dedent-js": "^1.0.1", @@ -7680,9 +7995,9 @@ } }, "node_modules/tailwindcss": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", - "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.2.tgz", + "integrity": "sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==", "license": "MIT" }, "node_modules/tapable": { @@ -7711,18 +8026,18 @@ "license": "MIT" }, "node_modules/tinyclip": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-0.1.12.tgz", - "integrity": "sha512-Ae3OVUqifDw0wBriIBS7yVaW44Dp6eSHQcyq4Igc7eN2TJH/2YsicswaW+J/OuMvhpDPOKEgpAZCjkb4hpoyeA==", + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/tinyclip/-/tinyclip-0.1.15.tgz", + "integrity": "sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A==", "license": "MIT", "engines": { "node": "^16.14.0 || >= 17.3.0" } }, "node_modules/tinyexec": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", - "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", "license": "MIT", "engines": { "node": ">=18" @@ -7787,9 +8102,9 @@ "license": "MIT" }, "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -7809,9 +8124,9 @@ } }, "node_modules/ufo": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", - "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.4.tgz", + "integrity": "sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==", "license": "MIT" }, "node_modules/ultrahtml": { @@ -7827,9 +8142,9 @@ "license": "MIT" }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "license": "MIT" }, "node_modules/unified": { @@ -8122,6 +8437,18 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/url-extras": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/url-extras/-/url-extras-0.1.0.tgz", + "integrity": "sha512-8tzwTeXFPuX/5PHuCDQE5Dd9Ts4rwoq2t9aIT+HS4iAVpmj5l4Ao7Q+BuuFjvWRqrLswBhQDk8O96ZicgCqQqw==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/url-template": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/url-template/-/url-template-3.1.1.tgz", @@ -8180,17 +8507,16 @@ } }, "node_modules/vite": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.2.tgz", - "integrity": "sha512-Bby3NOsna2jsjfLVOHKes8sGwgl4TT0E6vvpYgnAYDIF/tie7MRaFthmKuHx1NSXjiTueXH3do80FMQgvEktRg==", + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.2.tgz", + "integrity": "sha512-6YYPbRXTxx6bRXmOn7XdnQAy5DQNHhDgtjhDHI13oe4pY93kkcdGJWxpGwOm++/Wh0QpQhDrpIoVMrmrsI5AGQ==", "license": "MIT", "dependencies": { - "esbuild": "^0.27.0", - "fdir": "^6.5.0", - "picomatch": "^4.0.3", - "postcss": "^8.5.6", - "rollup": "^4.43.0", - "tinyglobby": "^0.2.15" + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.16", + "rolldown": "~1.1.3", + "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" @@ -8206,9 +8532,10 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.3.0", + "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", - "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", @@ -8221,15 +8548,18 @@ "@types/node": { "optional": true }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, "jiti": { "optional": true }, "less": { "optional": true }, - "lightningcss": { - "optional": true - }, "sass": { "optional": true }, @@ -8254,9 +8584,9 @@ } }, "node_modules/vitefu": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.2.tgz", - "integrity": "sha512-zpKATdUbzbsycPFBN71nS2uzBUQiVnFoOrr2rvqv34S1lcAgMKKkjWleLGeiJlZ8lwCXvtWaRn7R3ZC16SYRuw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", "license": "MIT", "workspaces": [ "tests/deps/*", @@ -8264,7 +8594,7 @@ "tests/projects/workspace/packages/*" ], "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-beta.0" + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "vite": { @@ -8454,16 +8784,10 @@ "npm": ">=7.0.0" } }, - "node_modules/vscode-json-languageservice/node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "license": "MIT" - }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0.tgz", - "integrity": "sha512-+VvMmQPJhtvJ+8O+zu2JKIRiLxXF8NW7krWgyMGeOHrp4Cn23T5hc0v2LknNeopDOB70wghHAds7mKtcZ0I4Sg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.1.tgz", + "integrity": "sha512-rfuA6T75H6m5EkbhtEPzre9pT0HPcDI2MMy4+nPFIBks5J8JBAUHD4tRYSgaBOijIEC7SRkC1kKyXTLqbmh9jw==", "license": "MIT", "engines": { "node": ">=14.0.0" @@ -8482,12 +8806,12 @@ } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.18.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.18.0.tgz", - "integrity": "sha512-Zdz+kJ12Iz6tc11xfZyEo501bBATHXrCjmMfnaR3pMnf1CoqZBKIynba3P+/bi9VEdrMbNtAVKYpKhbODvqy+Q==", + "version": "3.18.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.18.2.tgz", + "integrity": "sha512-XRyDbT0Pp3sSNti3JmxVEUMySWCSi1hhM+/KUlCy1hV1zmrqpM1OwO12EAki8blhmLuIMpaJrYbo0OzGVfK2Qg==", "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0", + "vscode-jsonrpc": "9.0.1", "vscode-languageserver-types": "3.18.0" } }, @@ -8592,9 +8916,9 @@ } }, "node_modules/yaml": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", - "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -8647,9 +8971,9 @@ } }, "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "version": "17.7.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.3.tgz", + "integrity": "sha512-GZtjxm/J/4TSxuL3FNYjCmLktBTnIw/rVmKSIyKeYAZpmJB2ig9VauCC5xsa82GNKVKDAqpOn3KVzNt0zmrU0g==", "license": "MIT", "dependencies": { "cliui": "^8.0.1", @@ -8701,9 +9025,9 @@ "license": "MIT" }, "node_modules/zod": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", - "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/docs/package.json b/docs/package.json index 6495a685..e3389b82 100644 --- a/docs/package.json +++ b/docs/package.json @@ -12,18 +12,18 @@ }, "dependencies": { "@astrojs/check": "^0.9.9", - "@astrojs/node": "^10.1.2", - "@astrojs/starlight": "^0.39.2", + "@astrojs/node": "^11.0.0", + "@astrojs/starlight": "^0.41.1", "@astrojs/starlight-tailwind": "^5.0.0", - "@astrojs/svelte": "^8.1.2", + "@astrojs/svelte": "^9.0.0", "@fontsource/ibm-plex-mono": "^5.2.7", "@fontsource/ibm-plex-sans": "^5.2.8", - "@tailwindcss/vite": "^4.3.0", - "astro": "^6.4.8", - "sharp": "^0.34.5", - "starlight-openapi": "^0.25.3", - "svelte": "^5.56.0", - "tailwindcss": "^4.1.10", - "typescript": "^5.9.3" + "@tailwindcss/vite": "^4.3.2", + "astro": "^7.0.4", + "sharp": "^0.35.2", + "starlight-openapi": "^0.26.0", + "svelte": "^5.56.4", + "tailwindcss": "^4.3.2", + "typescript": "^6.0.3" } } diff --git a/docs/src/assets/guides/pocketbase_categories.png b/docs/src/assets/guides/pocketbase_categories.png index 98730280..5ddeeb08 100644 Binary files a/docs/src/assets/guides/pocketbase_categories.png and b/docs/src/assets/guides/pocketbase_categories.png differ diff --git a/docs/src/assets/guides/pocketbase_subcategories.png b/docs/src/assets/guides/pocketbase_subcategories.png new file mode 100644 index 00000000..f3b2da06 Binary files /dev/null and b/docs/src/assets/guides/pocketbase_subcategories.png differ diff --git a/docs/src/assets/guides/wanderer_settings_categories.png b/docs/src/assets/guides/wanderer_settings_categories.png new file mode 100644 index 00000000..08dd44bc Binary files /dev/null and b/docs/src/assets/guides/wanderer_settings_categories.png differ diff --git a/docs/src/assets/guides/wanderer_trails_adjust.png b/docs/src/assets/guides/wanderer_trails_adjust.png new file mode 100644 index 00000000..cec4568b Binary files /dev/null and b/docs/src/assets/guides/wanderer_trails_adjust.png differ diff --git a/docs/src/assets/guides/wanderer_trails_category_filter.png b/docs/src/assets/guides/wanderer_trails_category_filter.png new file mode 100644 index 00000000..03d8cae5 Binary files /dev/null and b/docs/src/assets/guides/wanderer_trails_category_filter.png differ diff --git a/docs/src/content/docs/develop/plugin-system.md b/docs/src/content/docs/develop/plugin-system.md index c299ad1e..ebd53f6a 100644 --- a/docs/src/content/docs/develop/plugin-system.md +++ b/docs/src/content/docs/develop/plugin-system.md @@ -536,12 +536,14 @@ Supported host fields: | `privacy` | string | Trail import | `original` keeps provider visibility; `settings` uses the local user trail privacy setting. | | `merge.enabled` | boolean | Trail import | Runs auto-merge after creating imported trails. | | `createSummitLogForCompleted` | boolean | Trail import | Creates summit logs for completed imported trails. Defaults to `true`. | -| `categoryMapping` | object | Trail import | Maps plugin-provided `metadata.providerCategory` values to local category IDs or category names. | +| `categoryMapping` | object | Trail import | Maps plugin-provided `metadata.providerCategory` values to local category or subcategory targets. | | `connectors` | object | Host request/media policy | Concrete settings for configured connectors. | The settings UI lets users edit `categoryMapping` per plugin instance for trail -import plugins. Unknown or empty provider categories still fall back to the -host's activity-type mapping. +import plugins. A mapping value can be a string for broad category-only +compatibility, or an object with `category` and optional `subcategory`. Category +and subcategory values may be local record IDs or canonical names. Unknown or +empty provider categories still fall back to the host's activity-type mapping. Example: @@ -549,7 +551,14 @@ Example: { "hostConfig": { "categoryMapping": { - "Ride": "Biking", + "Ride": { + "category": "Biking", + "subcategory": "Road" + }, + "GravelRide": { + "category": "Biking", + "subcategory": "Gravel" + }, "Hike": "Hiking" } }, diff --git a/docs/src/content/docs/run/backend-configuration/custom-categories.md b/docs/src/content/docs/run/backend-configuration/custom-categories.md index 6de63a74..313f8267 100644 --- a/docs/src/content/docs/run/backend-configuration/custom-categories.md +++ b/docs/src/content/docs/run/backend-configuration/custom-categories.md @@ -1,11 +1,12 @@ --- title: Custom categories -description: How to create custom trail categories +description: How to configure trail categories and subcategories --- wanderer uses categories to classify what kind of activity a trail belongs to. -Out of the box you get: Biking, Canoeing, Climbing, Hiking, Skiing and Walking. -However, you can adapt these categories to your needs or add completely new ones. +Out of the box you get: Biking, Canoeing, Climbing, Hiking, Running, Skiing and Walking. +Some broad categories also have subcategories, for example Biking can be refined into MTB, Gravel, Road or E-Bike. +You can adapt this taxonomy to your needs in the PocketBase admin panel. ## Modifying categories @@ -15,7 +16,69 @@ In the PocketBase admin panel, click on the `categories` table in the list on th All existing categories will be listed here. To edit one simply click on the row, edit the data you want to change, and click "Save". To delete a category check the box at the beginning of the row and click "Delete selected". -To create a new category click the "New record" button in the top right corner, give your new category a name and a background image, and click "Save". +To create a new category click the "New record" button in the top right corner, give your new category a name, optionally fill in display metadata such as `short_name`, `icon`, or localized `translations`, and click "Save". + +The category `name` is the canonical, language-independent identity. +Use stable names such as `Hiking` or `Biking`; display labels in different languages should be stored in `translations`. +Incoming federated trails and integration imports match categories by a normalized version of `name`, so changing a category name can affect future matching. + +### Category fields + +| Field | Description | +| ----- | ----------- | +| `name` | Canonical category name. This is used for matching across imports and federation. | +| `short_name` | Optional compact label for space-constrained UI. | +| `icon` | Optional Font Awesome Free icon name without the `fa-` prefix, for example `person-hiking`. | +| `translations` | Optional localized display labels. | +| `settings` | Optional JSON settings for category-specific backend behavior. | + +`translations` uses supported base locale codes such as `de`, `en`, `fr`, or `pt` as keys. +Do not use region-specific keys such as `de-CH` or `pt-BR`; the frontend resolves user locales to their base locale before looking up category translations. + +Example: + +```json +{ + "de": { + "name": "Radfahren", + "short_name": "RAD" + }, + "en": { + "name": "Biking", + "short_name": "BIKE" + } +} +``` + +## Modifying subcategories + +Subcategories live in the `subcategories` table and act as optional refinements below a single parent category. Their names only need to be unique within that parent, so `Road` can exist under both Biking and Running at the same time. + + + +To add one, create a new record in `subcategories`, choose its parent `category`, set a canonical `name`, and optionally add display metadata. + +### Subcategory fields + +| Field | Description | +| ----- | ----------- | +| `category` | Required parent category. | +| `name` | Canonical subcategory name, unique within the parent category after normalization. | +| `short_name` | Compact label shown in icon-based filters, for example `MTB`, `GRVL`, or `ROAD`. | +| `icon` | Optional Font Awesome Free icon name. If empty, the parent category icon is used. | +| `badge_icon` | Optional Font Awesome Free overlay icon, for example `snowflake`, `mountain`, `bolt`, or `cross`. | +| `translations` | Optional localized display labels, using the same structure as category translations. | + +Most subcategories should reuse the parent category's icon and rely on `short_name` — plus a `badge_icon` where it helps — to set themselves apart, rather than each carrying a distinct full icon. You can browse available icon names at [fontawesome.com](https://fontawesome.com/search?ic=free-collection). + +:::note +Unknown remote categories and subcategories are not automatically created during federation. +Raw remote values are stored on the trail and can be matched later when an admin creates a compatible local category or subcategory. +::: + +## Migrating old custom categories + +If your instance already had custom categories such as `MTB` or `Gravel` that now overlap with a default subcategory, you can reassign the affected trails in bulk from the web UI. See [Categories](/use/categories/#editing-several-trails-at-once) for the step-by-step migration path. ## Category settings diff --git a/docs/src/content/docs/use/categories.md b/docs/src/content/docs/use/categories.md new file mode 100644 index 00000000..fb33f2f6 --- /dev/null +++ b/docs/src/content/docs/use/categories.md @@ -0,0 +1,58 @@ +--- +title: Categories +description: How to use trail categories, subcategories, and category visibility settings +--- + +Every trail has a category that describes its broad activity type — Hiking, Biking, Running, Skiing, and so on. Many categories can be narrowed down further with a subcategory, such as Biking / Gravel or Hiking / Snowshoeing, whenever you want to be more specific. + +## Choosing a category + +When you create or edit a trail, pick the activity type with the **Category** selector in the trail form. It lists the broad categories together with their subcategories, so you can stay general or get specific: + +- Choose **Hiking** for an ordinary hiking trail. +- Choose **Hiking / Snowshoeing** to mark it as a snowshoe route. +- Choose **Biking / Gravel** for a gravel ride. + +A broad category on its own is always enough; a subcategory is optional. On trail cards and in lists, the category icon carries a small badge for subcategories that need one — for example a snowflake for winter variants — so you can tell refinements apart at a glance. + +## Filtering trails + +The filter panel shows each category as an icon. Click an icon to add that category to the filter. + + + +Categories that have subcategories reveal a subcategory overlay when you hover or focus the icon; on touch devices, long-press it instead. From there you can filter by: + +- the whole category, +- only trails that have no subcategory, or +- one or more specific subcategories. + +When a subcategory filter is active, a small indicator appears on the category icon — that's how you tell "all Biking trails" apart from "only the Biking subcategories I picked". + +## Editing several trails at once + +To reclassify many trails in one go, select them in the trail list, open the action menu, and choose **Adjust**. The modal lets you set a new category, subcategory, or difficulty for the whole selection. + + + +This is handy after upgrading an instance, when an older standalone category overlaps with a new subcategory: every trail previously filed under `Gravel`, for instance, can be moved to Biking / Gravel in a single step. + +## Category preferences + +Open **Settings → Categories** to control how categories behave for your account. + + + +Each category has one visibility toggle: + +- **Show** controls whether the category is part of your exploration and planning. While it is on, the category appears in search and discovery and is offered in the category picker when you create or edit a trail. Turn it off to hide all trails in that category from those places, including federated trails from other instances. + +You can **reorder** categories by dragging them. This order carries over to pickers and filters, and it also decides which category is preselected when you create a new trail. New accounts start with Hiking as the first category unless an older favourite-sport setting is migrated. + +Categories with subcategories can be expanded. Inside the expanded section, each subcategory has its own visibility toggle and can be reordered by dragging. Hidden subcategories appear muted and drop out of your pickers and filters; hiding a parent category also hides its subcategories. When a category is collapsed, the compact badges below the category name show which subcategories belong to it. + +These settings are personal. They never delete categories, change other users' settings, or remove category assignments that already exist on trails. + +:::note +Categories and subcategories themselves are defined by the instance administrator in PocketBase. As a regular user you choose from the available taxonomy and set your own visibility preferences, but you cannot create global categories from the web UI. +::: diff --git a/docs/src/content/docs/use/create-a-trail.md b/docs/src/content/docs/use/create-a-trail.md index 47ffbc51..8a58ba5a 100644 --- a/docs/src/content/docs/use/create-a-trail.md +++ b/docs/src/content/docs/use/create-a-trail.md @@ -65,7 +65,7 @@ While drawing or editing a route, the anchor list shows the route's start, inter - **Distance / Duration / Elevation** – These are automatically calculated but can be manually adjusted if needed. - **Tags** – Add descriptive tags to help categorize and search for your trail (e.g. forest, sunset, dog-friendly). Start typing to add a tag and press Enter to confirm. - **Difficulty** – Select the trail's difficulty (e.g. Easy, Moderate, Hard) -- **Category** – Choose the activity type (e.g. Hiking, Cycling) +- **[Category](/use/categories/)** – Choose the activity type. You can select a broad category such as Hiking, or a more specific subcategory such as Biking / Gravel. #### Visibility @@ -83,7 +83,7 @@ Waypoints are points of interest along the trail. - When you are not editing the route, click on the map to open a popup with a **Create waypoint** button at that location. - While drawing or editing the route, right-click on the map to open the same popup without placing a route anchor. This works only while waypoint markers are visible (see the waypoint toggle in the route editing toolbar). - Each waypoint can have a name, description, icon, and photos. -- Use Font Awesome icons for map markers. You can browse them at [fontawesome.com](https://fontawesome.com/search?q=share&o=r&m=free). +- Use Font Awesome icons for map markers. You can browse them at [fontawesome.com](https://fontawesome.com/search?ic=free-collection). Alternatively, click **From Photos** to upload photos with GPS metadata. Waypoints will be created automatically based on the photo locations. diff --git a/plugins/README.md b/plugins/README.md index 724262e3..1510f3b4 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -204,10 +204,24 @@ Manifest `configSchema` defines plugin-owned settings that are passed to plugin | `merge.available` | Controls whether the UI offers auto-merge for this plugin. Defaults to `true`. | | `merge.enabled` | Runs auto-merge after trail import. | | `createSummitLogForCompleted` | Creates summit logs for completed imports. | -| `categoryMapping` | Maps `metadata.providerCategory` to local category IDs or names. | +| `categoryMapping` | Maps `metadata.providerCategory` to local category or subcategory targets. | | `connectors` | Provides host-owned base URL, TLS, private-network, and storage redirect settings for configured connectors. | The settings UI lets users edit `categoryMapping` per plugin instance for trail import plugins. +Mapping values can be strings for broad category-only compatibility, or objects +with `category` and optional `subcategory`, using local record IDs or canonical +names: + +```json +{ + "categoryMapping": { + "Ride": { "category": "Biking", "subcategory": "Road" }, + "GravelRide": { "category": "Biking", "subcategory": "Gravel" }, + "Hike": "Hiking" + } +} +``` + Plugins may describe provider-owned category values for the settings UI with `metadata.providerCategories`. This is display-only metadata; `categoryMapping` keys still use the raw provider category values emitted as diff --git a/plugins/hammerhead/hammerhead.go b/plugins/hammerhead/hammerhead.go index 69737d31..26a8b0bf 100644 --- a/plugins/hammerhead/hammerhead.go +++ b/plugins/hammerhead/hammerhead.go @@ -11,6 +11,8 @@ import ( "github.com/open-wanderer/wanderer/plugins/sdk" ) +const hammerheadJSONMaxBytes int64 = 16 * 1024 * 1024 + type hammerheadClient struct { userID string token string @@ -37,7 +39,7 @@ func login(email string, password string) (string, error) { }, Expect: sdk.ResponseExpect{ ContentTypes: []string{"application/json"}, - MaxBytes: 1048576, + MaxBytes: hammerheadJSONMaxBytes, }, } response, body, err := sdk.HostRequest(spec) @@ -92,7 +94,7 @@ func (c hammerheadClient) get(path string, query []sdk.QueryParam, out any) erro }, Expect: sdk.ResponseExpect{ ContentTypes: []string{"application/json"}, - MaxBytes: 1048576, + MaxBytes: hammerheadJSONMaxBytes, }, }) if err != nil { diff --git a/plugins/hammerhead/main.go b/plugins/hammerhead/main.go index a4d8c85a..28290df1 100644 --- a/plugins/hammerhead/main.go +++ b/plugins/hammerhead/main.go @@ -165,7 +165,7 @@ func prepareTrailSendV1() int32 { }, Expect: sdk.ResponseExpect{ ContentTypes: []string{"application/json"}, - MaxBytes: 1048576, + MaxBytes: hammerheadJSONMaxBytes, }, }, } diff --git a/plugins/hammerhead/plugin.json b/plugins/hammerhead/plugin.json index da0a5fef..ec15411d 100644 --- a/plugins/hammerhead/plugin.json +++ b/plugins/hammerhead/plugin.json @@ -81,7 +81,7 @@ ] }, "downloads": { - "maxBytes": 1048576, + "maxBytes": 16777216, "contentTypes": [ "application/json" ] diff --git a/plugins/komoot/plugin.json b/plugins/komoot/plugin.json index 7bff07e1..85f2057c 100644 --- a/plugins/komoot/plugin.json +++ b/plugins/komoot/plugin.json @@ -101,31 +101,31 @@ "hostConfig": { "categoryMapping": { "hike": "Hiking", - "mountaineering": "Hiking", - "racebike": "Biking", - "e_racebike": "Biking", - "touringbicycle": "Biking", - "e_touringbicycle": "Biking", - "mtb": "Biking", - "e_mtb": "Biking", - "mtb_easy": "Biking", - "e_mtb_easy": "Biking", - "mtb_advanced": "Biking", - "e_mtb_advanced": "Biking", - "downhillbike": "Biking", + "mountaineering": { "category": "Hiking", "subcategory": "Alpine" }, + "racebike": { "category": "Biking", "subcategory": "Road" }, + "e_racebike": { "category": "Biking", "subcategory": "E-Bike" }, + "touringbicycle": { "category": "Biking", "subcategory": "Touring" }, + "e_touringbicycle": { "category": "Biking", "subcategory": "Touring" }, + "mtb": { "category": "Biking", "subcategory": "MTB" }, + "e_mtb": { "category": "Biking", "subcategory": "MTB" }, + "mtb_easy": { "category": "Biking", "subcategory": "MTB" }, + "e_mtb_easy": { "category": "Biking", "subcategory": "MTB" }, + "mtb_advanced": { "category": "Biking", "subcategory": "MTB" }, + "e_mtb_advanced": { "category": "Biking", "subcategory": "MTB" }, + "downhillbike": { "category": "Biking", "subcategory": "MTB" }, "unicycle": "Biking", - "citybike": "Biking", - "jogging": "Walking", + "citybike": { "category": "Biking", "subcategory": "Touring" }, + "jogging": { "category": "Running", "subcategory": "Road" }, "nordicwalking": "Walking", "skaten": "Walking", "other": "Walking", "climbing": "Climbing", - "nordic": "Skiing", + "nordic": { "category": "Skiing", "subcategory": "Cross-country" }, "skialpin": "Skiing", - "skitour": "Skiing", + "skitour": { "category": "Skiing", "subcategory": "Backcountry" }, "sled": "Skiing", "snowboard": "Skiing", - "snowshoe": "Skiing" + "snowshoe": { "category": "Hiking", "subcategory": "Snowshoeing" } } }, "metadata": { diff --git a/plugins/schema/plugin.schema.json b/plugins/schema/plugin.schema.json index 02d671d6..016c039a 100644 --- a/plugins/schema/plugin.schema.json +++ b/plugins/schema/plugin.schema.json @@ -412,7 +412,17 @@ "type": "boolean" }, "categoryMapping": { - "$ref": "#/definitions/stringMap" + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "string" + }, + { + "$ref": "#/definitions/categoryMappingTarget" + } + ] + } }, "connectors": { "type": "object", @@ -476,6 +486,18 @@ "$ref": "#/definitions/stringMap" } } + }, + "categoryMappingTarget": { + "type": "object", + "additionalProperties": false, + "properties": { + "category": { + "type": "string" + }, + "subcategory": { + "type": "string" + } + } } } } diff --git a/plugins/strava/activity_type.go b/plugins/strava/activity_type.go new file mode 100644 index 00000000..e3d1cbe6 --- /dev/null +++ b/plugins/strava/activity_type.go @@ -0,0 +1,22 @@ +package main + +func activityTypeFromProvider(value string) string { + switch value { + case "AlpineSki", "BackcountrySki", "IceSkate", "NordicSki", "RollerSki", "Snowboard": + return "skiing" + case "Canoeing", "Kayaking", "Kitesurf", "Rowing", "Sail", "StandUpPaddling", "Surfing", "Windsurf": + return "canoeing" + case "Hike", "Snowshoe": + return "hiking" + case "Run", "TrailRun", "VirtualRun": + return "running" + case "Walk", "Golf", "Skateboard", "Wheelchair": + return "walking" + case "Ride", "EBikeRide", "Handcycle", "InlineSkate", "Velomobile", "VirtualRide": + return "biking" + case "RockClimbing": + return "climbing" + default: + return value + } +} diff --git a/plugins/strava/mapper.go b/plugins/strava/mapper.go index 3ef2445f..deff7b31 100644 --- a/plugins/strava/mapper.go +++ b/plugins/strava/mapper.go @@ -208,21 +208,5 @@ func providerActivityType(activity *detailedActivity) string { } func activityType(activity *detailedActivity) string { - value := providerActivityType(activity) - switch value { - case "AlpineSki", "BackcountrySki", "IceSkate", "NordicSki", "RollerSki", "Snowboard": - return "skiing" - case "Canoeing", "Kayaking", "Kitesurf", "Rowing", "Sail", "StandUpPaddling", "Surfing", "Windsurf": - return "canoeing" - case "Hike", "Snowshoe": - return "hiking" - case "Run", "VirtualRun", "Walk", "Golf", "Skateboard", "Wheelchair": - return "walking" - case "Ride", "EBikeRide", "Handcycle", "InlineSkate", "Velomobile", "VirtualRide": - return "biking" - case "RockClimbing": - return "climbing" - default: - return value - } + return activityTypeFromProvider(providerActivityType(activity)) } diff --git a/plugins/strava/mapper_test.go b/plugins/strava/mapper_test.go new file mode 100644 index 00000000..3ec6e3ee --- /dev/null +++ b/plugins/strava/mapper_test.go @@ -0,0 +1,18 @@ +package main + +import "testing" + +func TestActivityTypeMapsRunsToRunning(t *testing.T) { + cases := map[string]string{ + "Run": "running", + "TrailRun": "running", + "VirtualRun": "running", + "Walk": "walking", + } + + for providerType, want := range cases { + if got := activityTypeFromProvider(providerType); got != want { + t.Fatalf("activityTypeFromProvider(%q) = %q, want %q", providerType, got, want) + } + } +} diff --git a/plugins/strava/plugin.json b/plugins/strava/plugin.json index e05c1cfa..506b626c 100644 --- a/plugins/strava/plugin.json +++ b/plugins/strava/plugin.json @@ -102,7 +102,7 @@ "oauth_access_token" ], "downloads": { - "maxBytes": 1048576, + "maxBytes": 16777216, "contentTypes": [ "application/json", "application/gpx+xml", @@ -145,15 +145,15 @@ "route:1": "Biking", "route:2": "Walking", "AlpineSki": "Skiing", - "BackcountrySki": "Skiing", + "BackcountrySki": { "category": "Skiing", "subcategory": "Backcountry" }, "Badminton": "Other", "Canoeing": "Canoeing", - "Crossfit": "Workout", - "EBikeRide": "Biking", - "EMountainBikeRide": "Biking", - "Elliptical": "Workout", + "Crossfit": "Other", + "EBikeRide": { "category": "Biking", "subcategory": "E-Bike" }, + "EMountainBikeRide": { "category": "Biking", "subcategory": "E-Bike" }, + "Elliptical": "Other", "Golf": "Other", - "GravelRide": "Biking", + "GravelRide": { "category": "Biking", "subcategory": "Gravel" }, "Handcycle": "Biking", "HighIntensityIntervalTraining": "Other", "Hike": "Hiking", @@ -161,40 +161,40 @@ "InlineSkate": "Walking", "Kayaking": "Canoeing", "Kitesurf": "Canoeing", - "MountainBikeRide": "Biking", - "NordicSki": "Skiing", + "MountainBikeRide": { "category": "Biking", "subcategory": "MTB" }, + "NordicSki": { "category": "Skiing", "subcategory": "Cross-country" }, "Pickleball": "Other", "Pilates": "Other", "Racquetball": "Other", - "Ride": "Biking", + "Ride": { "category": "Biking", "subcategory": "Road" }, "RockClimbing": "Climbing", - "RollerSki": "Skiing", + "RollerSki": { "category": "Skiing", "subcategory": "Cross-country" }, "Rowing": "Canoeing", - "Run": "Walking", + "Run": { "category": "Running", "subcategory": "Road" }, "Sail": "Canoeing", "Skateboard": "Walking", "Snowboard": "Skiing", - "Snowshoe": "Hiking", + "Snowshoe": { "category": "Hiking", "subcategory": "Snowshoeing" }, "Soccer": "Other", "Squash": "Other", - "StairStepper": "Workout", + "StairStepper": "Other", "StandUpPaddling": "Canoeing", "Surfing": "Canoeing", "Swim": "Other", "TableTennis": "Other", "Tennis": "Other", - "TrailRun": "Other", + "TrailRun": { "category": "Running", "subcategory": "Trail" }, "Training": "Other", "Velomobile": "Biking", "VirtualRide": "Biking", "VirtualRow": "Other", - "VirtualRun": "Walking", + "VirtualRun": "Running", "Walk": "Walking", - "WeightTraining": "Workout", + "WeightTraining": "Other", "Wheelchair": "Walking", "Windsurf": "Canoeing", - "Workout": "Workout", - "Yoga": "Workout" + "Workout": "Other", + "Yoga": "Other" } }, "metadata": { diff --git a/plugins/strava/strava.go b/plugins/strava/strava.go index be049b34..f694f8ab 100644 --- a/plugins/strava/strava.go +++ b/plugins/strava/strava.go @@ -12,6 +12,8 @@ import ( "github.com/open-wanderer/wanderer/plugins/sdk" ) +const stravaDownloadMaxBytes int64 = 16 * 1024 * 1024 + // Strava is migrating its API host: the new host "https://www.api-v3.strava.com" // is available from 2027-01-04 and the old one is retired on 2027-06-01 (June // 2026 Developer Program update). We cut over on 2027-03-01 — after the new host @@ -132,7 +134,7 @@ func (c *stravaClient) request(path string, query []sdk.QueryParam, contentTypes }, Expect: sdk.ResponseExpect{ ContentTypes: contentTypes, - MaxBytes: 1048576, + MaxBytes: stravaDownloadMaxBytes, }, }) } diff --git a/web/package-lock.json b/web/package-lock.json index cc70e3ba..60367661 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -9,11 +9,11 @@ "version": "0.19.2", "dependencies": { "@felte/validator-zod": "^1.0.18", - "@fortawesome/fontawesome-free": "^7.1.0", - "@sveltejs/adapter-node": "^5.5.2", - "@tailwindcss/vite": "^4.2.4", - "@threlte/core": "^8.5.9", - "@threlte/extras": "^9.14.6", + "@fortawesome/fontawesome-free": "^7.3.0", + "@sveltejs/adapter-node": "^5.5.7", + "@tailwindcss/vite": "^4.3.2", + "@threlte/core": "^8.5.16", + "@threlte/extras": "^9.21.0", "@tiptap/core": "^3.27.1", "@tiptap/extension-heading": "^3.27.1", "@tiptap/extension-link": "^3.27.1", @@ -22,73 +22,77 @@ "@tiptap/pm": "^3.27.1", "@tiptap/starter-kit": "^3.27.1", "@tiptap/suggestion": "^3.27.1", - "@turf/destination": "^7.3.3", - "@turf/distance": "^7.3.3", + "@turf/destination": "^7.3.5", + "@turf/distance": "^7.3.5", "@types/chart.js": "^4.0.1", "@types/supercluster": "^7.1.3", - "@types/three": "^0.183.1", - "@xmldom/xmldom": "^0.8.12", + "@types/three": "^0.185.0", + "@xmldom/xmldom": "^0.9.10", "activitypub-types": "^1.1.0", - "autoprefixer": "^10.4.24", + "autoprefixer": "^10.5.2", "canvas-confetti": "^1.9.4", "canvg": "^4.0.3", "chart.js": "^4.5.1", "chartjs-plugin-crosshair": "^2.0.0", - "chartjs-plugin-zoom": "^2.1.0", + "chartjs-plugin-zoom": "^2.2.0", "chokidar": "^5.0.0", "crypto-random-string": "^5.0.0", "felte": "^1.3.0", "heic2any": "^0.0.4", "instead": "^1.0.3", "isomorphic-xml2js": "^0.1.3", - "json-diff-ts": "^4.8.2", + "json-diff-ts": "^4.10.4", "jspdf": "^4.2.1", "jszip": "^3.10.1", - "maplibre-gl": "^4.7.1", - "marked": "^17.0.4", - "meilisearch": "^0.57.0", + "maplibre-gl": "^5.24.0", + "marked": "^18.0.5", + "meilisearch": "^0.58.0", "ngeohash": "^0.6.3", - "nouislider": "^15.7.1", - "pdfkit": "^0.17.2", - "photoswipe": "^5.4.3", - "pocketbase": "^0.26.8", - "qrcode": "^1.4.4", + "nouislider": "^15.8.1", + "pdfkit": "^0.19.1", + "photoswipe": "^5.4.4", + "pocketbase": "^0.27.0", + "qrcode": "^1.5.4", "supercluster": "^8.0.1", - "svelte-i18n": "^4.0.0", - "tailwindcss": "^4.2.4", - "three": "^0.183.1", - "vitest": "^4.1.4", + "svelte-i18n": "^4.0.1", + "tailwindcss": "^4.3.2", + "three": "^0.185.0", + "vitest": "^4.1.9", "zod": "^3.24.1" }, "devDependencies": { - "@playwright/test": "^1.58.2", + "@playwright/test": "^1.61.1", "@sveltejs/adapter-auto": "^7.0.1", - "@sveltejs/enhanced-img": "^0.10.4", - "@sveltejs/kit": "^2.60.1", - "@sveltejs/vite-plugin-svelte": "^7.0.0", - "@tailwindcss/typography": "^0.5.19", + "@sveltejs/enhanced-img": "^0.11.0", + "@sveltejs/kit": "^2.68.0", + "@sveltejs/vite-plugin-svelte": "^7.1.2", + "@tailwindcss/typography": "^0.5.20", "@types/canvas-confetti": "^1.9.0", - "@types/node": "^25.3.3", - "postcss": "^8.5.6", - "svelte": "^5.55.7", - "svelte-check": "^4.3.6", + "@types/node": "^26.0.1", + "postcss": "^8.5.16", + "svelte": "^5.56.4", + "svelte-check": "^4.7.1", "sveltekit-openapi-generator": "^0.1.6", - "tslib": "^2.4.1", - "typescript": "^5.9.3", - "vite": "^8.0.16" + "tslib": "^2.8.1", + "typescript": "^6.0.3", + "vite": "8.1.1" } }, "node_modules/@apidevtools/json-schema-ref-parser": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", - "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-14.0.1.tgz", + "integrity": "sha512-Oc96zvmxx1fqoSEdUmfmvvb59/KDOnUoJ7s2t7bISyAn0XEz57LCCw8k2Y4Pf3mwKaZLMciESALORLgfe2frCw==", "dev": true, "license": "MIT", "dependencies": { - "@jsdevtools/ono": "^7.1.3", - "@types/json-schema": "^7.0.6", - "call-me-maybe": "^1.0.1", + "@types/json-schema": "^7.0.15", "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" } }, "node_modules/@apidevtools/openapi-schemas": { @@ -109,27 +113,27 @@ "license": "MIT" }, "node_modules/@apidevtools/swagger-parser": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", - "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-12.1.0.tgz", + "integrity": "sha512-e5mJoswsnAX0jG+J09xHFYQXb/bUc5S3pLpMxUuRUA2H8T2kni3yEoyz2R3Dltw5f4A6j6rPNMpWTK+iVDFlng==", "dev": true, "license": "MIT", "dependencies": { - "@apidevtools/json-schema-ref-parser": "^9.0.6", - "@apidevtools/openapi-schemas": "^2.0.4", + "@apidevtools/json-schema-ref-parser": "14.0.1", + "@apidevtools/openapi-schemas": "^2.1.0", "@apidevtools/swagger-methods": "^3.0.2", - "@jsdevtools/ono": "^7.1.3", - "call-me-maybe": "^1.0.1", - "z-schema": "^5.0.1" + "ajv": "^8.17.1", + "ajv-draft-04": "^1.0.0", + "call-me-maybe": "^1.0.2" }, "peerDependencies": { "openapi-types": ">=7" } }, "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -142,20 +146,20 @@ "license": "Apache-2.0" }, "node_modules/@emnapi/core": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", - "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", "license": "MIT", "optional": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.1", + "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", - "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", "license": "MIT", "optional": true, "dependencies": { @@ -163,9 +167,9 @@ } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", - "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", "license": "MIT", "optional": true, "dependencies": { @@ -288,9 +292,9 @@ } }, "node_modules/@fortawesome/fontawesome-free": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-7.2.0.tgz", - "integrity": "sha512-3DguDv/oUE+7vjMeTSOjCSG+KeawgVQOHrKRnvUuqYh1mfArrh7s+s8hXW3e4RerBA1+Wh+hBqf8sJNpqNrBWg==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-7.3.0.tgz", + "integrity": "sha512-Yw4Qa43P6e4Xwh0TwEUkHQNRJInWaqIBo73VJmns3j2AIlZPAvUcR4yGIxCPqPRCgyJ5KIVIalF/I1GxhZ/Kgw==", "license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)", "engines": { "node": ">=6" @@ -849,50 +853,31 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jsdevtools/ono": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", - "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", - "dev": true, - "license": "MIT" - }, "node_modules/@kurkle/color": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", "license": "MIT" }, - "node_modules/@mapbox/geojson-rewind": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@mapbox/geojson-rewind/-/geojson-rewind-0.5.2.tgz", - "integrity": "sha512-tJaT+RbYGJYStt7wI3cq4Nl4SXxG8W7JDG5DMJu97V25RnbNg3QtQtf+KD+VLjNpWKYsRvXDNmNrBgEETr1ifA==", - "license": "ISC", - "dependencies": { - "get-stream": "^6.0.1", - "minimist": "^1.2.6" - }, - "bin": { - "geojson-rewind": "geojson-rewind" - } - }, "node_modules/@mapbox/jsonlint-lines-primitives": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.2.tgz", - "integrity": "sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@mapbox/jsonlint-lines-primitives/-/jsonlint-lines-primitives-2.0.3.tgz", + "integrity": "sha512-0SElaV0uMxEnxzBhhX9WTuPyUeMsAN/SS0i16tjuba4/mio63MG9khjC1a0JAiPGXAwvwm4UfHJURCN7nyudQg==", + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 22" } }, "node_modules/@mapbox/point-geometry": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-0.1.0.tgz", - "integrity": "sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@mapbox/point-geometry/-/point-geometry-1.1.0.tgz", + "integrity": "sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==", "license": "ISC" }, "node_modules/@mapbox/tiny-sdf": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.1.0.tgz", - "integrity": "sha512-uFJhNh36BR4OCuWIEiWaEix9CA2WzT6CAIcqVjWYpnx8+QDtS+oC4QehRrx5cX4mgWs37MmKnwUejeHxVymzNg==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@mapbox/tiny-sdf/-/tiny-sdf-2.2.0.tgz", + "integrity": "sha512-LVL4wgI9YAum5V+LNVQO6QgFBPw7/MIIY4XJPNsPDMrjEwcE+JfKk1LuIl8GnF197ejVdC9QdPaxrx5gfgdGXg==", "license": "BSD-2-Clause" }, "node_modules/@mapbox/unitbezier": { @@ -902,12 +887,14 @@ "license": "BSD-2-Clause" }, "node_modules/@mapbox/vector-tile": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-1.3.1.tgz", - "integrity": "sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@mapbox/vector-tile/-/vector-tile-2.0.5.tgz", + "integrity": "sha512-pXj8m7KTsqZt+1jsE0xIpGvqTSbblfkuEJL/NJmNePMtEwxO8V3XMDo9WMSfDeqHvCtBI9Lmt4mGcGR10zecmw==", "license": "BSD-3-Clause", "dependencies": { - "@mapbox/point-geometry": "~0.1.0" + "@mapbox/point-geometry": "~1.1.0", + "@types/geojson": "^7946.0.16", + "pbf": "^4.0.2" } }, "node_modules/@mapbox/whoots-js": { @@ -919,18 +906,26 @@ "node": ">=6.0.0" } }, + "node_modules/@maplibre/geojson-vt": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/@maplibre/geojson-vt/-/geojson-vt-6.1.1.tgz", + "integrity": "sha512-FVMOcmSP/yqol45t7StApEyTL5/vmqBCuFhH9n+fFuINenhaX+YgHHIt1yJ86S8kln3uJLcMvmEU2cfn6E2eCQ==", + "license": "ISC", + "dependencies": { + "kdbush": "^4.1.0" + } + }, "node_modules/@maplibre/maplibre-gl-style-spec": { - "version": "20.4.0", - "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-20.4.0.tgz", - "integrity": "sha512-AzBy3095fTFPjDjmWpR2w6HVRAZJ6hQZUCwk5Plz6EyfnfuQW1odeW5i2Ai47Y6TBA2hQnC+azscjBSALpaWgw==", + "version": "24.10.0", + "resolved": "https://registry.npmjs.org/@maplibre/maplibre-gl-style-spec/-/maplibre-gl-style-spec-24.10.0.tgz", + "integrity": "sha512-lichxSiagMEBBrqHF0trtMQH9RKh+9jUlIJl0qW0QHvt2H/tbvUWdE+ZzI2Jd0/pT7j/iavLonlPu7EQ/ixTOw==", "license": "ISC", "dependencies": { "@mapbox/jsonlint-lines-primitives": "~2.0.2", - "@mapbox/unitbezier": "^0.0.1", + "@mapbox/unitbezier": "^1.0.0", "json-stringify-pretty-compact": "^4.0.0", "minimist": "^1.2.8", - "quickselect": "^2.0.0", - "rw": "^1.3.3", + "quickselect": "^3.0.0", "tinyqueue": "^3.0.0" }, "bin": { @@ -939,20 +934,52 @@ "gl-style-validate": "dist/gl-style-validate.mjs" } }, - "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/quickselect": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/quickselect/-/quickselect-2.0.0.tgz", - "integrity": "sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==", - "license": "ISC" + "node_modules/@maplibre/maplibre-gl-style-spec/node_modules/@mapbox/unitbezier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@mapbox/unitbezier/-/unitbezier-1.0.0.tgz", + "integrity": "sha512-fqd515fjBmANKGGsQ286E2Wvj/XvDFpGzwJxq4CI6jMQue6Oy04uCKp+JWKF00xRTmk6cEu1jPJ9p3xqH8YWqQ==", + "license": "BSD-2-Clause" + }, + "node_modules/@maplibre/mlt": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@maplibre/mlt/-/mlt-1.1.12.tgz", + "integrity": "sha512-ZeK5w2TTeHOajcLaEQs1KZXw2V9wIKo1PmThlxlsHoXsQsYlBqLJzPOd6tJHRtGTChUY3DPPmjXRArYVvAbmZw==", + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "@mapbox/point-geometry": "^1.1.0" + } + }, + "node_modules/@maplibre/vt-pbf": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@maplibre/vt-pbf/-/vt-pbf-4.3.2.tgz", + "integrity": "sha512-j6p0AdjvAR19Z3XaCysle7A4ZSo08tYOzxD0Y9NQylwPAkwJJeYub5b2eVucdeDh7erhv69DahoLOevDRERRUw==", + "license": "MIT", + "dependencies": { + "@mapbox/point-geometry": "^1.1.0", + "@types/geojson": "^7946.0.16", + "pbf": "^5.1.0" + } + }, + "node_modules/@maplibre/vt-pbf/node_modules/pbf": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-5.1.0.tgz", + "integrity": "sha512-Wv0yo0+uZepnoNEKsquhar1F18LogB8oeEikIhUXG16udbiXG7JecHGySwoo6kuMgjmbQYzdrTZlO+/K9t8eZg==", + "license": "BSD-3-Clause", + "dependencies": { + "resolve-protobuf-schema": "^2.1.0" + }, + "bin": { + "pbf": "bin/pbf" + } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", - "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", "license": "MIT", "optional": true, "dependencies": { - "@tybys/wasm-util": "^0.10.1" + "@tybys/wasm-util": "^0.10.3" }, "funding": { "type": "github", @@ -963,10 +990,34 @@ "@emnapi/runtime": "^1.7.1" } }, + "node_modules/@noble/ciphers": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-1.3.0.tgz", + "integrity": "sha512-2I0gnIVPtfnMw9ee9h1dJG7tp81+8Ob3OJb3Mv37rx5L40/b0i7djjCVvGOVqc9AEIQyvyu1i6ypKdFw8R8gQw==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@oxc-project/types": { - "version": "0.133.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", - "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", + "version": "0.137.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.137.0.tgz", + "integrity": "sha512-WT+Gb24i8hmvo85AIv2oEYouEXkRlKAlT9WaCa3TfLgNCN+GhrJOGZuIlMouAh38Qe4QOx26eUOVsq70qXrywA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" @@ -984,13 +1035,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.59.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", - "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.61.1.tgz", + "integrity": "sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.59.1" + "playwright": "1.61.1" }, "bin": { "playwright": "cli.js" @@ -1006,9 +1057,9 @@ "license": "MIT" }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", - "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.3.tgz", + "integrity": "sha512-DT6Z3PhvioeHMvxo+xHc3KtqggrI7CCTXCmC2h/5zUlp5jVitv7XEy+9q5/7v8IolhlioawpMo8Kg0EEBy7J0g==", "cpu": [ "arm64" ], @@ -1022,9 +1073,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", - "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.3.tgz", + "integrity": "sha512-0NwgwsjM7LrsuVnXMK3koTpagBNOhloc/BNjKqZjv4V5zI5r13qx69uVhRx+o5Z0yy4Hzq+lpy7TAgUG/ocvrw==", "cpu": [ "arm64" ], @@ -1038,9 +1089,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", - "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.3.tgz", + "integrity": "sha512-YtiBp4disu6V560loT6PjMdiRaWmVvDNrUunAalbiFx2ggeJwxdAsgZMcoGP17uyAsTwAj5V1niksxlHnVQ1Sw==", "cpu": [ "x64" ], @@ -1054,9 +1105,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", - "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.3.tgz", + "integrity": "sha512-yD3EkEdXk2LypPxnf/kSZHirarsI8gcPzc62SukhR9VJTyvV+F9Q/GxWNuCojc7sXyuVC4DxRGhdDK4X8VSsbw==", "cpu": [ "x64" ], @@ -1070,9 +1121,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", - "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.3.tgz", + "integrity": "sha512-c+8vieQbsD7HNAHKIA34w0GJ9FedFFuJGD+7E6vz7Q3uqAIugL5p45fhlsj4UaAsHpcmlqugBWMhA0/j7o0sIg==", "cpu": [ "arm" ], @@ -1086,9 +1137,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", - "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.3.tgz", + "integrity": "sha512-50jD0uUwLvur7Zz9LHz17kaAdTPjn5wN93hEgjvmYFRZwiR7ZJYovTd5ipyWJDAnXKvZ+wgc+/Ika6dwSF5OcA==", "cpu": [ "arm64" ], @@ -1102,9 +1153,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", - "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.3.tgz", + "integrity": "sha512-BO9+oPL8K9poZJBfYPsXNtYjPE5uM3qeehT3aFcW4LITOl+iSqhp0abzjR2nWBUNjIZeKXjAEWBZ64WjNoHd6w==", "cpu": [ "arm64" ], @@ -1118,9 +1169,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", - "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.3.tgz", + "integrity": "sha512-f3VpLB1vQ0Eo6ecr/6cekLnvYMFF4YBFoVGkfkvPLq1bAkbAwHYQPZKoAmG6OJyTcxxoC+AvezGx/S1obNC0Mw==", "cpu": [ "ppc64" ], @@ -1134,9 +1185,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", - "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.3.tgz", + "integrity": "sha512-AmurZ26Pqx/RI9N1gzEOCklkKXl927yjfXWUUS0O7Puh8ARM/Ob8qfrD3qnWksScdw6cSrW5PSHE9DyLu7+PtA==", "cpu": [ "s390x" ], @@ -1150,9 +1201,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", - "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.3.tgz", + "integrity": "sha512-JJpqs8bRGITDOdbkNKnlojzBabbOHrqjSvDr0IVsZObE1lBcPjxItUEY9eWIDbxaJ3cGrXPWGfGkIxFijg/URg==", "cpu": [ "x64" ], @@ -1166,9 +1217,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", - "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.3.tgz", + "integrity": "sha512-rSJcdjPxzA/by/6/rYs+v+bXU7UjvnbUWz8MJb6kh6+knqB1dCrtHg0uu7C/4haqJvqdkYHQ5IGn+tCH9GLW/g==", "cpu": [ "x64" ], @@ -1182,9 +1233,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", - "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.3.tgz", + "integrity": "sha512-hQ3/PYkDJICgevvyNcVrihVeqq7k1Pp3VZ9lY+dauAYUJKO+auqApvANhvR1An9BhmqYKvW2Mu1F9u4DXSMLxQ==", "cpu": [ "arm64" ], @@ -1198,27 +1249,27 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", - "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.3.tgz", + "integrity": "sha512-Elcv/BtML9lXrV6JuKITc/grN2kYV9gjsQpW8Jfw4ioK0TOkjBjye0nnyqQNy9STNaI20lXNaQBRrD5gSgR0Yg==", "cpu": [ "wasm32" ], "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "1.10.0", - "@emnapi/runtime": "1.10.0", - "@napi-rs/wasm-runtime": "^1.1.4" + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" }, "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", - "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.3.tgz", + "integrity": "sha512-2DrEfhluH9yhiaFApmsjsjwrSYbNcY1oFTzYSP1a535jDbV98zCFanA/96TBUd0iDFcxGmw9QRExwGCXz3U+/g==", "cpu": [ "arm64" ], @@ -1232,9 +1283,9 @@ } }, "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", - "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.3.tgz", + "integrity": "sha512-OL4OMk7UPXOeVGGd3qo5zJyPIljf4AFgk5QAkPPS+OoLuOOozhuaQGC18MxVTnw/06q93gShAJzlwnSCY9YtqA==", "cpu": [ "x64" ], @@ -1254,9 +1305,9 @@ "license": "MIT" }, "node_modules/@rollup/plugin-commonjs": { - "version": "29.0.2", - "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-29.0.2.tgz", - "integrity": "sha512-S/ggWH1LU7jTyi9DxZOKyxpVd4hF/OZ0JrEbeLjXk/DFXwRny0tjD2c992zOUYQobLrVkRVMDdmHP16HKP7GRg==", + "version": "29.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-29.0.3.tgz", + "integrity": "sha512-ZaOxZceP7SOUW7Lqw5IRVweSQYWaeIPnXIGLiB690EBA3FGJTO40EEr2L5yZplJWsgTCogILRSpcAe7+U0Otdg==", "license": "MIT", "dependencies": { "@rollup/pluginutils": "^5.0.1", @@ -1323,10 +1374,31 @@ } } }, + "node_modules/@rollup/plugin-replace": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.3.tgz", + "integrity": "sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==", + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/pluginutils": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz", - "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", @@ -1346,9 +1418,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", - "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz", + "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==", "cpu": [ "arm" ], @@ -1359,9 +1431,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", - "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz", + "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==", "cpu": [ "arm64" ], @@ -1372,9 +1444,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", - "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz", + "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==", "cpu": [ "arm64" ], @@ -1385,9 +1457,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", - "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz", + "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==", "cpu": [ "x64" ], @@ -1398,9 +1470,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", - "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz", + "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==", "cpu": [ "arm64" ], @@ -1411,9 +1483,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", - "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz", + "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==", "cpu": [ "x64" ], @@ -1424,9 +1496,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", - "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz", + "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==", "cpu": [ "arm" ], @@ -1437,9 +1509,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", - "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz", + "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==", "cpu": [ "arm" ], @@ -1450,9 +1522,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", - "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz", + "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==", "cpu": [ "arm64" ], @@ -1463,9 +1535,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", - "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz", + "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==", "cpu": [ "arm64" ], @@ -1476,9 +1548,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", - "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz", + "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==", "cpu": [ "loong64" ], @@ -1489,9 +1561,9 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", - "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz", + "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==", "cpu": [ "loong64" ], @@ -1502,9 +1574,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", - "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz", + "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==", "cpu": [ "ppc64" ], @@ -1515,9 +1587,9 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", - "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz", + "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==", "cpu": [ "ppc64" ], @@ -1528,9 +1600,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", - "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz", + "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==", "cpu": [ "riscv64" ], @@ -1541,9 +1613,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", - "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz", + "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==", "cpu": [ "riscv64" ], @@ -1554,9 +1626,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", - "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz", + "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==", "cpu": [ "s390x" ], @@ -1567,9 +1639,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", - "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz", + "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==", "cpu": [ "x64" ], @@ -1580,9 +1652,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", - "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz", + "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==", "cpu": [ "x64" ], @@ -1593,9 +1665,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", - "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz", + "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==", "cpu": [ "x64" ], @@ -1606,9 +1678,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", - "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz", + "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==", "cpu": [ "arm64" ], @@ -1619,9 +1691,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", - "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz", + "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==", "cpu": [ "arm64" ], @@ -1632,9 +1704,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", - "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz", + "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==", "cpu": [ "ia32" ], @@ -1645,9 +1717,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", - "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz", + "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==", "cpu": [ "x64" ], @@ -1658,9 +1730,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", - "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz", + "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==", "cpu": [ "x64" ], @@ -1677,9 +1749,9 @@ "license": "MIT" }, "node_modules/@sveltejs/acorn-typescript": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.9.tgz", - "integrity": "sha512-lVJX6qEgs/4DOcRTpo56tmKzVPtoWAaVbL4hfO7t7NVwl9AAXzQR6cihesW1BmNMPl+bK6dreu2sOKBP2Q9CIA==", + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@sveltejs/acorn-typescript/-/acorn-typescript-1.0.10.tgz", + "integrity": "sha512-4WfKk68eTih+MiJD4fSbxN7E8kVBmTMPWHUPYjvl2N0rMs53YLTT8/YjKU5Dtnz5LqDjl7LEw4U7lXR2W3J5WA==", "license": "MIT", "peerDependencies": { "acorn": "^8.9.0" @@ -1696,14 +1768,15 @@ } }, "node_modules/@sveltejs/adapter-node": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.5.4.tgz", - "integrity": "sha512-45X92CXW+2J8ZUzPv3eLlKWEzINKiiGeFWTjyER4ZN4sGgNoaoeSkCY/QYNxHpPXy71QPsctwccBo9jJs0ySPQ==", + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/@sveltejs/adapter-node/-/adapter-node-5.5.7.tgz", + "integrity": "sha512-uOfc9eVlI3A37RRSaKcgrheBYPrfJwC9VMqDp8x/O6tlKdcLLvHThSWD0KNIbjQ/d+7bwLGx3vx6aowAcRfd2g==", "license": "MIT", "dependencies": { "@rollup/plugin-commonjs": "^29.0.0", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.0", + "@rollup/plugin-replace": "^6.0.3", "rollup": "^4.59.0" }, "peerDependencies": { @@ -1711,9 +1784,9 @@ } }, "node_modules/@sveltejs/enhanced-img": { - "version": "0.10.4", - "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.10.4.tgz", - "integrity": "sha512-Am5nmAKUo7Nboqq7Dhtfn7dcXA087d7gIz6Vecn1opB41aJ680+0q9U9KvEcMgduOyeiwckTIOQOx4Mmq9GcvA==", + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@sveltejs/enhanced-img/-/enhanced-img-0.11.0.tgz", + "integrity": "sha512-TN7VzGoqwFqvA4Faj1brUPHLtcEkyFa08nrn9bfm98yVrzr0g4bOZHjlBoPYHHX+sZNGE+cxznhTVgarECPQsA==", "dev": true, "license": "MIT", "dependencies": { @@ -1730,15 +1803,15 @@ } }, "node_modules/@sveltejs/kit": { - "version": "2.60.1", - "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.60.1.tgz", - "integrity": "sha512-mQjlkNo+rJvpln7V2IGY2j99BqhcFbS4UN0AQNKNYfhBAFZTuCDAdW3a1sgf330mvtNvsBXn3HpAhcmvdJTcIQ==", + "version": "2.68.0", + "resolved": "https://registry.npmjs.org/@sveltejs/kit/-/kit-2.68.0.tgz", + "integrity": "sha512-PdKiWsqinAoubVsSiRgVFkg3MHzGhQPnwQ8VxnGQKpZYijpapZ3UHHBje0GeByt2TvfjHPw+kxV+dNK2RIZg9g==", "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.0.0", - "@sveltejs/acorn-typescript": "^1.0.5", + "@sveltejs/acorn-typescript": "^1.0.9", "@types/cookie": "^0.6.0", - "acorn": "^8.14.1", + "acorn": "^8.16.0", "cookie": "^0.6.0", "devalue": "^5.8.1", "esm-env": "^1.2.2", @@ -1770,10 +1843,20 @@ } } }, + "node_modules/@sveltejs/load-config": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@sveltejs/load-config/-/load-config-0.2.0.tgz", + "integrity": "sha512-1LgZ/qUqSoq+QorD83lk2hka79Px0wXNW2q5V1nZlxGhQgw1jrsIbVz5YiCeucVLo4XvFLjXukUaQjIiqowkcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + } + }, "node_modules/@sveltejs/vite-plugin-svelte": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.0.0.tgz", - "integrity": "sha512-ILXmxC7HAsnkK2eslgPetrqqW1BKSL7LktsFgqzNj83MaivMGZzluWq32m25j2mDOjmSKX7GGWahePhuEs7P/g==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-7.1.2.tgz", + "integrity": "sha512-DrUBA2UXRfDmUX/ZTiEopd3X40yavsJF1FX2RygcuIScHL7o5YX1fMvoYnDhjeJQC4weCOklirpNWlcb2NiSeA==", "license": "MIT", "dependencies": { "deepmerge": "^4.3.1", @@ -1790,56 +1873,56 @@ } }, "node_modules/@swc/helpers": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.21.tgz", - "integrity": "sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==", + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", + "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.8.0" } }, "node_modules/@tailwindcss/node": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.4.tgz", - "integrity": "sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.2.tgz", + "integrity": "sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==", "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.5", - "enhanced-resolve": "^5.19.0", - "jiti": "^2.6.1", + "enhanced-resolve": "5.21.6", + "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", - "tailwindcss": "4.2.4" + "tailwindcss": "4.3.2" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.4.tgz", - "integrity": "sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.2.tgz", + "integrity": "sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==", "license": "MIT", "engines": { "node": ">= 20" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.2.4", - "@tailwindcss/oxide-darwin-arm64": "4.2.4", - "@tailwindcss/oxide-darwin-x64": "4.2.4", - "@tailwindcss/oxide-freebsd-x64": "4.2.4", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.4", - "@tailwindcss/oxide-linux-arm64-gnu": "4.2.4", - "@tailwindcss/oxide-linux-arm64-musl": "4.2.4", - "@tailwindcss/oxide-linux-x64-gnu": "4.2.4", - "@tailwindcss/oxide-linux-x64-musl": "4.2.4", - "@tailwindcss/oxide-wasm32-wasi": "4.2.4", - "@tailwindcss/oxide-win32-arm64-msvc": "4.2.4", - "@tailwindcss/oxide-win32-x64-msvc": "4.2.4" + "@tailwindcss/oxide-android-arm64": "4.3.2", + "@tailwindcss/oxide-darwin-arm64": "4.3.2", + "@tailwindcss/oxide-darwin-x64": "4.3.2", + "@tailwindcss/oxide-freebsd-x64": "4.3.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.2", + "@tailwindcss/oxide-linux-x64-musl": "4.3.2", + "@tailwindcss/oxide-wasm32-wasi": "4.3.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.2" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.4.tgz", - "integrity": "sha512-e7MOr1SAn9U8KlZzPi1ZXGZHeC5anY36qjNwmZv9pOJ8E4Q6jmD1vyEHkQFmNOIN7twGPEMXRHmitN4zCMN03g==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.2.tgz", + "integrity": "sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==", "cpu": [ "arm64" ], @@ -1853,9 +1936,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.4.tgz", - "integrity": "sha512-tSC/Kbqpz/5/o/C2sG7QvOxAKqyd10bq+ypZNf+9Fi2TvbVbv1zNpcEptcsU7DPROaSbVgUXmrzKhurFvo5eDg==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.2.tgz", + "integrity": "sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==", "cpu": [ "arm64" ], @@ -1869,9 +1952,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.4.tgz", - "integrity": "sha512-yPyUXn3yO/ufR6+Kzv0t4fCg2qNr90jxXc5QqBpjlPNd0NqyDXcmQb/6weunH/MEDXW5dhyEi+agTDiqa3WsGg==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.2.tgz", + "integrity": "sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==", "cpu": [ "x64" ], @@ -1885,9 +1968,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.4.tgz", - "integrity": "sha512-BoMIB4vMQtZsXdGLVc2z+P9DbETkiopogfWZKbWwM8b/1Vinbs4YcUwo+kM/KeLkX3Ygrf4/PsRndKaYhS8Eiw==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.2.tgz", + "integrity": "sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==", "cpu": [ "x64" ], @@ -1901,9 +1984,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.4.tgz", - "integrity": "sha512-7pIHBLTHYRAlS7V22JNuTh33yLH4VElwKtB3bwchK/UaKUPpQ0lPQiOWcbm4V3WP2I6fNIJ23vABIvoy2izdwA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.2.tgz", + "integrity": "sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==", "cpu": [ "arm" ], @@ -1917,9 +2000,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.4.tgz", - "integrity": "sha512-+E4wxJ0ZGOzSH325reXTWB48l42i93kQqMvDyz5gqfRzRZ7faNhnmvlV4EPGJU3QJM/3Ab5jhJ5pCRUsKn6OQw==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.2.tgz", + "integrity": "sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==", "cpu": [ "arm64" ], @@ -1933,9 +2016,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.4.tgz", - "integrity": "sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.2.tgz", + "integrity": "sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==", "cpu": [ "arm64" ], @@ -1949,9 +2032,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.4.tgz", - "integrity": "sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.2.tgz", + "integrity": "sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==", "cpu": [ "x64" ], @@ -1965,9 +2048,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.4.tgz", - "integrity": "sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.2.tgz", + "integrity": "sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==", "cpu": [ "x64" ], @@ -1981,9 +2064,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.4.tgz", - "integrity": "sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.2.tgz", + "integrity": "sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -1998,11 +2081,11 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.8.1", - "@emnapi/runtime": "^1.8.1", - "@emnapi/wasi-threads": "^1.1.0", - "@napi-rs/wasm-runtime": "^1.1.1", - "@tybys/wasm-util": "^0.10.1", + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", "tslib": "^2.8.1" }, "engines": { @@ -2010,9 +2093,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.4.tgz", - "integrity": "sha512-L9BXqxC4ToVgwMFqj3pmZRqyHEztulpUJzCxUtLjobMCzTPsGt1Fa9enKbOpY2iIyVtaHNeNvAK8ERP/64sqGQ==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.2.tgz", + "integrity": "sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==", "cpu": [ "arm64" ], @@ -2026,9 +2109,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.4.tgz", - "integrity": "sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.2.tgz", + "integrity": "sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ==", "cpu": [ "x64" ], @@ -2042,27 +2125,27 @@ } }, "node_modules/@tailwindcss/typography": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz", - "integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==", + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.20.tgz", + "integrity": "sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==", "dev": true, "license": "MIT", "dependencies": { "postcss-selector-parser": "6.0.10" }, "peerDependencies": { - "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" + "tailwindcss": ">=3.0.0 || >=4.0.0 || insiders" } }, "node_modules/@tailwindcss/vite": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.4.tgz", - "integrity": "sha512-pCvohwOCspk3ZFn6eJzrrX3g4n2JY73H6MmYC87XfGPyTty4YsCjYTMArRZm/zOI8dIt3+EcrLHAFPe5A4bgtw==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.2.tgz", + "integrity": "sha512-eHpMeX4JXfVNJDEcsouTeCBubJBTcTLigeaw/NTUW6PB5ATKKXdyonnXgTBX2VuRbjz1hjfz6C5XAhr52ImQXA==", "license": "MIT", "dependencies": { - "@tailwindcss/node": "4.2.4", - "@tailwindcss/oxide": "4.2.4", - "tailwindcss": "4.2.4" + "@tailwindcss/node": "4.3.2", + "@tailwindcss/oxide": "4.3.2", + "tailwindcss": "4.3.2" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" @@ -2084,9 +2167,9 @@ } }, "node_modules/@threlte/core": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/@threlte/core/-/core-8.5.9.tgz", - "integrity": "sha512-EncyIKITwXnOUQpzstq+7AxqjW7sWhrg662BdHi/iSMXaIvjUaF3VPLBzGvVm0MCsokxFG69jfW39AG5GBbHbg==", + "version": "8.5.16", + "resolved": "https://registry.npmjs.org/@threlte/core/-/core-8.5.16.tgz", + "integrity": "sha512-+aa9Zgz0/Qat82H0hTm1pTEtS2BIP1Nfu7pKaei+3+TvDqER0YnZ4hdgI9fYU9Z5r1lrzY3JxMyrdISp4B0kfw==", "license": "MIT", "peerDependencies": { "svelte": ">=5", @@ -2094,9 +2177,9 @@ } }, "node_modules/@threlte/extras": { - "version": "9.14.8", - "resolved": "https://registry.npmjs.org/@threlte/extras/-/extras-9.14.8.tgz", - "integrity": "sha512-0QVIYoJPo+Nc4S4Il9asHVzpt4iHX88jRgyrWi7Uk5iWK+xSAFLyHkyL0kSvdFoBXSJZLE0LG6qP78n69//scA==", + "version": "9.21.0", + "resolved": "https://registry.npmjs.org/@threlte/extras/-/extras-9.21.0.tgz", + "integrity": "sha512-ZkBx4IH+7vcxVM09Rex9XJNMKKbQt4JZ5J/3TnBd/Fge8IzSgl0ydgn70riDf/8tDLMDID6VdZPPrlr5vCHHJA==", "license": "MIT", "dependencies": { "@threejs-kit/instanced-sprite-mesh": "^2.5.1", @@ -2586,9 +2669,9 @@ "license": "MIT" }, "node_modules/@tybys/wasm-util": { - "version": "0.10.1", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", - "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", "license": "MIT", "optional": true, "dependencies": { @@ -2635,9 +2718,9 @@ "license": "MIT" }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "license": "MIT" }, "node_modules/@types/geojson": { @@ -2646,15 +2729,6 @@ "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", "license": "MIT" }, - "node_modules/@types/geojson-vt": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/@types/geojson-vt/-/geojson-vt-3.2.5.tgz", - "integrity": "sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==", - "license": "MIT", - "dependencies": { - "@types/geojson": "*" - } - }, "node_modules/@types/hammerjs": { "version": "2.0.46", "resolved": "https://registry.npmjs.org/@types/hammerjs/-/hammerjs-2.0.46.tgz", @@ -2675,30 +2749,13 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/mapbox__point-geometry": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/@types/mapbox__point-geometry/-/mapbox__point-geometry-0.1.4.tgz", - "integrity": "sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==", - "license": "MIT" - }, - "node_modules/@types/mapbox__vector-tile": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@types/mapbox__vector-tile/-/mapbox__vector-tile-1.3.4.tgz", - "integrity": "sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==", - "license": "MIT", - "dependencies": { - "@types/geojson": "*", - "@types/mapbox__point-geometry": "*", - "@types/pbf": "*" - } - }, "node_modules/@types/node": { - "version": "25.6.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.6.0.tgz", - "integrity": "sha512-+qIYRKdNYJwY3vRCZMdJbPLJAtGjQBudzZzdzwQYkEPQd+PJGixUL5QfvCLDaULoLv+RhT3LDkwEfKaAkgSmNQ==", + "version": "26.0.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-26.0.1.tgz", + "integrity": "sha512-fc3KiUoBt6kie0N9bIW3E47vZsuaMf0PM2AaUpLCLT0s/LvX1nxAim6Fc049cNxODPpGm6qRAuUOB86SkRuPQw==", "license": "MIT", "dependencies": { - "undici-types": "~7.19.0" + "undici-types": "~8.3.0" } }, "node_modules/@types/pako": { @@ -2707,12 +2764,6 @@ "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==", "license": "MIT" }, - "node_modules/@types/pbf": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/pbf/-/pbf-3.0.5.tgz", - "integrity": "sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==", - "license": "MIT" - }, "node_modules/@types/raf": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/@types/raf/-/raf-3.4.3.tgz", @@ -2741,18 +2792,17 @@ } }, "node_modules/@types/three": { - "version": "0.183.1", - "resolved": "https://registry.npmjs.org/@types/three/-/three-0.183.1.tgz", - "integrity": "sha512-f2Pu5Hrepfgavttdye3PsH5RWyY/AvdZQwIVhrc4uNtvF7nOWJacQKcoVJn0S4f0yYbmAE6AR+ve7xDcuYtMGw==", + "version": "0.185.0", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.185.0.tgz", + "integrity": "sha512-O2Uy8Cj4Nonr8dWUUbifMdPe8B0Mq7EdOHb89S4+kjUw/KhbjTZrUuYlrQ1bpUKG+EP9QJnN7qNxbHGlGoLHMA==", "license": "MIT", "dependencies": { "@dimforge/rapier3d-compat": "~0.12.0", "@tweenjs/tween.js": "~23.1.3", "@types/stats.js": "*", "@types/webxr": ">=0.5.17", - "@webgpu/types": "*", "fflate": "~0.8.2", - "meshoptimizer": "~1.0.1" + "meshoptimizer": "~1.1.1" } }, "node_modules/@types/trusted-types": { @@ -2777,15 +2827,15 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.4.tgz", - "integrity": "sha512-iPBpra+VDuXmBFI3FMKHSFXp3Gx5HfmSCE8X67Dn+bwephCnQCaB7qWK2ldHa+8ncN8hJU8VTMcxjPpyMkUjww==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", + "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", - "@vitest/spy": "4.1.4", - "@vitest/utils": "4.1.4", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -2794,12 +2844,12 @@ } }, "node_modules/@vitest/mocker": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.4.tgz", - "integrity": "sha512-R9HTZBhW6yCSGbGQnDnH3QHfJxokKN4KB+Yvk9Q1le7eQNYwiCyKxmLmurSpFy6BzJanSLuEUDrD+j97Q+ZLPg==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", + "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.4", + "@vitest/spy": "4.1.9", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -2829,9 +2879,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.4.tgz", - "integrity": "sha512-ddmDHU0gjEUyEVLxtZa7xamrpIefdEETu3nZjWtHeZX4QxqJ7tRxSteHVXJOcr8jhiLoGAhkK4WJ3WqBpjx42A==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", + "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", "license": "MIT", "dependencies": { "tinyrainbow": "^3.1.0" @@ -2841,12 +2891,12 @@ } }, "node_modules/@vitest/runner": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.4.tgz", - "integrity": "sha512-xTp7VZ5aXP5ZJrn15UtJUWlx6qXLnGtF6jNxHepdPHpMfz/aVPx+htHtgcAL2mDXJgKhpoo2e9/hVJsIeFbytQ==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", + "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.4", + "@vitest/utils": "4.1.9", "pathe": "^2.0.3" }, "funding": { @@ -2854,13 +2904,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.4.tgz", - "integrity": "sha512-MCjCFgaS8aZz+m5nTcEcgk/xhWv0rEH4Yl53PPlMXOZ1/Ka2VcZU6CJ+MgYCZbcJvzGhQRjVrGQNZqkGPttIKw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", + "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.4", - "@vitest/utils": "4.1.4", + "@vitest/pretty-format": "4.1.9", + "@vitest/utils": "4.1.9", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -2869,21 +2919,21 @@ } }, "node_modules/@vitest/spy": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.4.tgz", - "integrity": "sha512-XxNdAsKW7C+FLydqFJLb5KhJtl3PGCMmYwFRfhvIgxJvLSXhhVI1zM8f1qD3Zg7RCjTSzDVyct6sghs9UEgBEQ==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", + "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", "license": "MIT", "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.4.tgz", - "integrity": "sha512-13QMT+eysM5uVGa1rG4kegGYNp6cnQcsTc67ELFbhNLQO+vgsygtYJx2khvdt4gVQqSSpC/KT5FZZxUpP3Oatw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", + "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.4", + "@vitest/pretty-format": "4.1.9", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -2891,25 +2941,19 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/@webgpu/types": { - "version": "0.1.69", - "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.69.tgz", - "integrity": "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==", - "license": "BSD-3-Clause" - }, "node_modules/@xmldom/xmldom": { - "version": "0.8.13", - "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.13.tgz", - "integrity": "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw==", + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.10.tgz", + "integrity": "sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==", "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">=14.6" } }, "node_modules/acorn": { - "version": "8.16.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", - "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", "license": "MIT", "bin": { "acorn": "bin/acorn" @@ -2924,6 +2968,38 @@ "integrity": "sha512-06XrXpzW0Y7kS9ZS61qtV12s0oTCdcuWBO2/cPjPzGqlSBYqDvP8oiTIkSIrrynIrox5S5+/t8atDbi0Wo1xKA==", "license": "MIT" }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -2988,9 +3064,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", - "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", + "version": "10.5.2", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.2.tgz", + "integrity": "sha512-rD5t5DwOjJdmSORcTq64j8MawTC+tbQ+HHqjR4NDumamy/ambn1UJrlKL+KdwujWxMkFjPM3pPHOEA9tl4767Q==", "funding": [ { "type": "opencollective", @@ -3007,8 +3083,8 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.28.2", - "caniuse-lite": "^1.0.30001787", + "browserslist": "^4.28.4", + "caniuse-lite": "^1.0.30001799", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" @@ -3070,9 +3146,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.20", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.20.tgz", - "integrity": "sha512-1AaXxEPfXT+GvTBJFuy4yXVHWJBXa4OdbIebGN/wX5DlsIkU0+wzGnd2lOzokSk51d5LUmqjgBLRLlypLUqInQ==", + "version": "2.10.40", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.40.tgz", + "integrity": "sha512-BSSLZ9/Cjjv7Gtj5B68ZzXcXUg8iOf3fme+FCuh8rC/Go+Kmh8cox7M3A8dolou16s64QjLPOSdngh7GxXvkSw==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -3091,9 +3167,9 @@ } }, "node_modules/brace-expansion": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", - "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", "dev": true, "license": "MIT", "dependencies": { @@ -3125,9 +3201,9 @@ "license": "(MIT AND Zlib)" }, "node_modules/browserslist": { - "version": "4.28.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", - "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "version": "4.28.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.4.tgz", + "integrity": "sha512-MTc8i/x9jBQd1iMw2CFGS+rwMa07eYjLR0CCTLDACl9xhxy+nIs3KeML/biicXtk9JrZ6dnnTatmc7ErPXIxqw==", "funding": [ { "type": "opencollective", @@ -3144,10 +3220,10 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.10.12", - "caniuse-lite": "^1.0.30001782", - "electron-to-chromium": "^1.5.328", - "node-releases": "^2.0.36", + "baseline-browser-mapping": "^2.10.38", + "caniuse-lite": "^1.0.30001799", + "electron-to-chromium": "^1.5.376", + "node-releases": "^2.0.48", "update-browserslist-db": "^1.2.3" }, "bin": { @@ -3187,9 +3263,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001788", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", - "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==", + "version": "1.0.30001800", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001800.tgz", + "integrity": "sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==", "funding": [ { "type": "opencollective", @@ -3439,13 +3515,6 @@ "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", "license": "MIT" }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -3494,35 +3563,6 @@ "node": ">= 8" } }, - "node_modules/cross-spawn/node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/cross-spawn/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crypto-js": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz", - "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==", - "license": "MIT" - }, "node_modules/crypto-random-string": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-5.0.0.tgz", @@ -3645,9 +3685,9 @@ } }, "node_modules/dompurify": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.0.tgz", - "integrity": "sha512-nolgK9JcaUXMSmW+j1yaSvaEaoXYHwWyGJlkoCTghc97KgGDDSnpoU/PlEnw63Ah+TGKFOyY+X5LnxaWbCSfXg==", + "version": "3.4.11", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz", + "integrity": "sha512-zhlUV12GsaRzMsf9q5M254YhA4+VuF0fG+QFqu6aYpoGlKtz+w8//jBcGVYBgQkR5GHjUomejY84AV+/uPbWdw==", "license": "(MPL-2.0 OR Apache-2.0)", "optional": true, "optionalDependencies": { @@ -3668,9 +3708,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.340", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.340.tgz", - "integrity": "sha512-908qahOGocRMinT2nM3ajCEM99H4iPdv84eagPP3FfZy/1ZGeOy2CZYzjhms81ckOPCXPlW7LkY4XpxD8r1DrA==", + "version": "1.5.383", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.383.tgz", + "integrity": "sha512-I2484/KkAvl8lm9VyjH2JnbOIV0d/UCqT7gbzs6l+o6Vmn9wgB66uVcKX+Vk6HrXtY6fbWTOEXuv8waDTuFNCw==", "license": "ISC" }, "node_modules/emoji-regex": { @@ -3681,13 +3721,13 @@ "license": "MIT" }, "node_modules/enhanced-resolve": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", - "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "version": "5.21.6", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz", + "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", - "tapable": "^2.3.0" + "tapable": "^2.3.3" }, "engines": { "node": ">=10.13.0" @@ -3703,9 +3743,9 @@ } }, "node_modules/es-module-lexer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", - "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.2.0.tgz", + "integrity": "sha512-3lGxdTXCLfe1MYfTz1y2ksAAUM4NAOP6rPEjxGJVKO7TZ5+tvHCaQWGpC4Y3IXvW3ece0Cz1cIP4FWBxOnGCTQ==", "license": "MIT" }, "node_modules/es5-ext": { @@ -3791,9 +3831,9 @@ } }, "node_modules/esrap": { - "version": "2.2.5", - "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.5.tgz", - "integrity": "sha512-/yLB1538mag+dn0wsePTe8C0rDIjUOaJpMs2McodSzmM2msWcZsBSdRtg6HOBt0A/r82BN+Md3pgwSc/uWt2Ig==", + "version": "2.2.13", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-2.2.13.tgz", + "integrity": "sha512-m8jH5hZgJE2RRUK/jjkGPcJEDAV+dYnZYFkosQaPTcE+Yw4xynXHOo6FUdwaWBtdR3b1MMa7wEDTSHeR2VWsGA==", "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -3834,9 +3874,9 @@ } }, "node_modules/expect-type": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", - "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", "license": "Apache-2.0", "engines": { "node": ">=12.0.0" @@ -3868,6 +3908,23 @@ "pako": "^2.1.0" } }, + "node_modules/fast-uri": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.3.tgz", + "integrity": "sha512-i70LwGWUduXqzicKXWshooq+sWL1K3WUU5rKZNG/0i3a1OSoX3HqhH5WbWwTmqWfor4urUakGPiRQcleRZTwOg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", @@ -3901,9 +3958,9 @@ } }, "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", "license": "MIT" }, "node_modules/find-up": { @@ -3966,13 +4023,6 @@ "url": "https://github.com/sponsors/rawify" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", @@ -3996,12 +4046,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/geojson-vt": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/geojson-vt/-/geojson-vt-4.0.2.tgz", - "integrity": "sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==", - "license": "ISC" - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -4011,18 +4055,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/gl-matrix": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/gl-matrix/-/gl-matrix-3.4.4.tgz", @@ -4051,20 +4083,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/global-prefix": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-4.0.0.tgz", - "integrity": "sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==", - "license": "MIT", - "dependencies": { - "ini": "^4.1.3", - "kind-of": "^6.0.3", - "which": "^4.0.0" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/globalyzer": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", @@ -4093,9 +4111,9 @@ } }, "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -4124,26 +4142,6 @@ "node": ">=8.0.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, "node_modules/imagetools-core": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/imagetools-core/-/imagetools-core-9.1.0.tgz", @@ -4160,33 +4158,12 @@ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", "license": "MIT" }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, - "node_modules/ini": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", - "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/instead": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/instead/-/instead-1.0.3.tgz", @@ -4215,12 +4192,12 @@ "license": "MIT" }, "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "hasown": "^2.0.3" }, "engines": { "node": ">= 0.4" @@ -4266,13 +4243,11 @@ "license": "MIT" }, "node_modules/isexe": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.5.tgz", - "integrity": "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w==", - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=18" - } + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" }, "node_modules/isomorphic-xml2js": { "version": "0.1.3", @@ -4301,26 +4276,35 @@ } }, "node_modules/jiti": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", - "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", "license": "MIT", "bin": { "jiti": "lib/jiti-cli.mjs" } }, - "node_modules/jpeg-exif": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/jpeg-exif/-/jpeg-exif-1.1.4.tgz", - "integrity": "sha512-a+bKEcCjtuW5WTdgeXFzswSrdqi0jk4XlEtZlx5A94wCoBpFjfFTbo/Tra5SpNCl/YFZPvcV1dJc+TAYeg6ROQ==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "node_modules/js-md5": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.8.3.tgz", + "integrity": "sha512-qR0HB5uP6wCuRMrWPTrkMaev7MJZwJuuw4fnwAzRgP4J4/F8RwtodOKpGp4XpqsLBFzzgqIO42efFAyz2Et6KQ==", "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", + "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -4335,6 +4319,13 @@ "integrity": "sha512-/Ipg6migsLtNQQz+I3jm+hnPGZx4x47cVXTr4tgvxepQJKIB5iDmyguxicZ/59wIg3OlGgIGwIMftn+LqT8lNQ==", "license": "MIT" }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, "node_modules/json-stringify-pretty-compact": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz", @@ -4407,20 +4398,11 @@ "license": "(MIT AND Zlib)" }, "node_modules/kdbush": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz", - "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.1.0.tgz", + "integrity": "sha512-e9vurzrXJQrFX6ckpHP3bvj5l+9CnYzkxDNnNQ1h2QTqdWsUAJgXiKdGNcOa1EY85dU8KbQ+z/FdQdB7P+9yfQ==", "license": "ISC" }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", @@ -4738,22 +4720,6 @@ "dev": true, "license": "MIT" }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", - "dev": true, - "license": "MIT" - }, "node_modules/lodash.mergewith": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", @@ -4797,37 +4763,30 @@ } }, "node_modules/maplibre-gl": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-4.7.1.tgz", - "integrity": "sha512-lgL7XpIwsgICiL82ITplfS7IGwrB1OJIw/pCvprDp2dhmSSEBgmPzYRvwYYYvJGJD7fxUv1Tvpih4nZ6VrLuaA==", + "version": "5.24.0", + "resolved": "https://registry.npmjs.org/maplibre-gl/-/maplibre-gl-5.24.0.tgz", + "integrity": "sha512-ALyFxgtd5R+65UqZ/++lOqwWcC0SNho9c27fYSyLmG7AfnAul2o46F05aDJGPbFU57wos9dgcIySHs0Xe6ia3A==", "license": "BSD-3-Clause", "dependencies": { - "@mapbox/geojson-rewind": "^0.5.2", "@mapbox/jsonlint-lines-primitives": "^2.0.2", - "@mapbox/point-geometry": "^0.1.0", - "@mapbox/tiny-sdf": "^2.0.6", + "@mapbox/point-geometry": "^1.1.0", + "@mapbox/tiny-sdf": "^2.1.0", "@mapbox/unitbezier": "^0.0.1", - "@mapbox/vector-tile": "^1.3.1", + "@mapbox/vector-tile": "^2.0.4", "@mapbox/whoots-js": "^3.1.0", - "@maplibre/maplibre-gl-style-spec": "^20.3.1", - "@types/geojson": "^7946.0.14", - "@types/geojson-vt": "3.2.5", - "@types/mapbox__point-geometry": "^0.1.4", - "@types/mapbox__vector-tile": "^1.3.4", - "@types/pbf": "^3.0.5", - "@types/supercluster": "^7.1.3", - "earcut": "^3.0.0", - "geojson-vt": "^4.0.2", - "gl-matrix": "^3.4.3", - "global-prefix": "^4.0.0", + "@maplibre/geojson-vt": "^6.1.0", + "@maplibre/maplibre-gl-style-spec": "^24.8.1", + "@maplibre/mlt": "^1.1.8", + "@maplibre/vt-pbf": "^4.3.0", + "@types/geojson": "^7946.0.16", + "earcut": "^3.0.2", + "gl-matrix": "^3.4.4", "kdbush": "^4.0.2", "murmurhash-js": "^1.0.0", - "pbf": "^3.3.0", - "potpack": "^2.0.0", + "pbf": "^4.0.1", + "potpack": "^2.1.0", "quickselect": "^3.0.0", - "supercluster": "^8.0.1", - "tinyqueue": "^3.0.0", - "vt-pbf": "^3.1.3" + "tinyqueue": "^3.0.0" }, "engines": { "node": ">=16.14.0", @@ -4838,15 +4797,15 @@ } }, "node_modules/maplibre-gl/node_modules/earcut": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.0.2.tgz", - "integrity": "sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-3.2.0.tgz", + "integrity": "sha512-qy0wTgkOKsKC0cHVd4cKFfaO5u7egzs0F2bQ/9l5Pvm0EJM4XDdUMFoCJrxmWc2Fc3sRFa8l6gds8tx+6BcN8g==", "license": "ISC" }, "node_modules/marked": { - "version": "17.0.6", - "resolved": "https://registry.npmjs.org/marked/-/marked-17.0.6.tgz", - "integrity": "sha512-gB0gkNafnonOw0obSTEGZTT86IuhILt2Wfx0mWH/1Au83kybTayroZ/V6nS25mN7u8ASy+5fMhgB3XPNrOZdmA==", + "version": "18.0.5", + "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.5.tgz", + "integrity": "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==", "license": "MIT", "bin": { "marked": "bin/marked.js" @@ -4856,9 +4815,9 @@ } }, "node_modules/meilisearch": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/meilisearch/-/meilisearch-0.57.0.tgz", - "integrity": "sha512-J3ZdX6MjguEIG9Yo+duRZeVsbMdRuwM2OFvJUs8wZSeBADYZ2qc1eajlClKLRqS/mAhGh2bLiU5SbmhKXKuWSw==", + "version": "0.58.0", + "resolved": "https://registry.npmjs.org/meilisearch/-/meilisearch-0.58.0.tgz", + "integrity": "sha512-MXFlU+JVG2I3tDI4brS3UHxEv2fjOROP24TPP16c8dGHTulb1kxMgXvKP0x4ImvUcsVVJFi4QXP0JNDS/TaWaw==", "license": "MIT", "engines": { "node": "^20.19.0 || >=22.12.0" @@ -4884,9 +4843,9 @@ } }, "node_modules/meshoptimizer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.0.1.tgz", - "integrity": "sha512-Vix+QlA1YYT3FwmBBZ+49cE5y/b+pRrcXKqGpS5ouh33d3lSp2PoTpCw19E0cKDFWalembrHnIaZetf27a+W2g==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.1.1.tgz", + "integrity": "sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==", "license": "MIT" }, "node_modules/minimatch": { @@ -4949,9 +4908,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.12", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", - "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "version": "3.3.15", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", + "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", "funding": [ { "type": "github", @@ -4982,10 +4941,13 @@ } }, "node_modules/node-releases": { - "version": "2.0.37", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", - "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", - "license": "MIT" + "version": "2.0.50", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.50.tgz", + "integrity": "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==", + "license": "MIT", + "engines": { + "node": ">=18" + } }, "node_modules/nouislider": { "version": "15.8.1", @@ -4994,23 +4956,16 @@ "license": "MIT" }, "node_modules/obug": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", - "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", + "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", "funding": [ "https://github.com/sponsors/sxzz", "https://opencollective.com/debug" ], - "license": "MIT" - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" + "license": "MIT", + "engines": { + "node": ">=12.20.0" } }, "node_modules/openapi-merge": { @@ -5083,9 +5038,19 @@ "license": "BlueOak-1.0.0" }, "node_modules/pako": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz", - "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pako/-/pako-2.2.0.tgz", + "integrity": "sha512-zJq6RP/5q+TO2OpFV3FHzlPnFjmkb7Nc99a5SNjJE+uu/PkpChs+NIZSSzbBoD+6kjiISXjfYdwj1ZRQ81dz/w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "(MIT AND Zlib)" }, "node_modules/path-exists": { @@ -5097,16 +5062,6 @@ "node": ">=8" } }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -5147,12 +5102,11 @@ "license": "MIT" }, "node_modules/pbf": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/pbf/-/pbf-3.3.0.tgz", - "integrity": "sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pbf/-/pbf-4.0.2.tgz", + "integrity": "sha512-J0ajxARhZfpUEebxYs1vhMGMuLSXtBe1e+fFPDrf2uA2hgo+UshKfNUWOz92HJNz6/NFEXseQPddnHkTreWRqg==", "license": "BSD-3-Clause", "dependencies": { - "ieee754": "^1.1.12", "resolve-protobuf-schema": "^2.1.0" }, "bin": { @@ -5160,16 +5114,17 @@ } }, "node_modules/pdfkit": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.17.2.tgz", - "integrity": "sha512-UnwF5fXy08f0dnp4jchFYAROKMNTaPqb/xgR8GtCzIcqoTnbOqtp3bwKvO4688oHI6vzEEs8Q6vqqEnC5IUELw==", + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/pdfkit/-/pdfkit-0.19.1.tgz", + "integrity": "sha512-6Gzk+wDwTs4VSxsR5rCMTnIl5nlmkye1oWB0l2hDB1EX6ZNSIBroKQEv+2+fPPn+stVjyqzmsqRJVDfB9fo5DA==", "license": "MIT", "dependencies": { - "crypto-js": "^4.2.0", + "@noble/ciphers": "^1.0.0", + "@noble/hashes": "^1.6.0", "fontkit": "^2.0.4", - "jpeg-exif": "^1.1.4", + "js-md5": "^0.8.3", "linebreak": "^1.1.0", - "png-js": "^1.0.0" + "png-js": "^1.1.0" } }, "node_modules/performance-now": { @@ -5206,13 +5161,13 @@ } }, "node_modules/playwright": { - "version": "1.59.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", - "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.59.1" + "playwright-core": "1.61.1" }, "bin": { "playwright": "cli.js" @@ -5225,9 +5180,9 @@ } }, "node_modules/playwright-core": { - "version": "1.59.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", - "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", "dev": true, "license": "Apache-2.0", "bin": { @@ -5255,15 +5210,15 @@ } }, "node_modules/pocketbase": { - "version": "0.26.8", - "resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.26.8.tgz", - "integrity": "sha512-aQ/ewvS7ncvAE8wxoW10iAZu6ElgbeFpBhKPnCfvRovNzm2gW8u/sQNPGN6vNgVEagz44kK//C61oKjfa+7Low==", + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/pocketbase/-/pocketbase-0.27.0.tgz", + "integrity": "sha512-K5N6d93UP/BNMbMnlZ6BUfy9VPCIvLyqhJFOsNI8OsZwzvKWEAfyD36boi5K4ECIOl5HMlo0TzuaeGdKpMwizQ==", "license": "MIT" }, "node_modules/postcss": { - "version": "8.5.15", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", - "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "version": "8.5.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", + "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", "funding": [ { "type": "opencollective", @@ -5449,12 +5404,12 @@ } }, "node_modules/prosemirror-view": { - "version": "1.41.8", - "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.8.tgz", - "integrity": "sha512-TnKDdohEatgyZNGCDWIdccOHXhYloJwbwU+phw/a23KBvJIR9lWQWW7WHHK3vBdOLDNuF7TaX98GObUZOWkOnA==", + "version": "1.41.9", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.9.tgz", + "integrity": "sha512-clTunTX+eaLbr87L1V1QPheRlEQJyTlL3gXe9x3jQIk3rL0RVWxviDGz8tFaydwIVm+hKhYCyr+R/zBtWr9s6A==", "license": "MIT", "dependencies": { - "prosemirror-model": "^1.20.0", + "prosemirror-model": "^1.25.8", "prosemirror-state": "^1.0.0", "prosemirror-transform": "^1.1.0" } @@ -5602,12 +5557,12 @@ } }, "node_modules/rolldown": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", - "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.3.tgz", + "integrity": "sha512-1F1eEtUBtFvcGm1HQ9TiUIUHPQG7mSAODrhIzjxoUEFuo8OcbrGLiVLkevNgj84TE4lnHvnumwFjhJO5Eu135g==", "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.133.0", + "@oxc-project/types": "=0.137.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -5617,30 +5572,30 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.3", - "@rolldown/binding-darwin-arm64": "1.0.3", - "@rolldown/binding-darwin-x64": "1.0.3", - "@rolldown/binding-freebsd-x64": "1.0.3", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", - "@rolldown/binding-linux-arm64-gnu": "1.0.3", - "@rolldown/binding-linux-arm64-musl": "1.0.3", - "@rolldown/binding-linux-ppc64-gnu": "1.0.3", - "@rolldown/binding-linux-s390x-gnu": "1.0.3", - "@rolldown/binding-linux-x64-gnu": "1.0.3", - "@rolldown/binding-linux-x64-musl": "1.0.3", - "@rolldown/binding-openharmony-arm64": "1.0.3", - "@rolldown/binding-wasm32-wasi": "1.0.3", - "@rolldown/binding-win32-arm64-msvc": "1.0.3", - "@rolldown/binding-win32-x64-msvc": "1.0.3" + "@rolldown/binding-android-arm64": "1.1.3", + "@rolldown/binding-darwin-arm64": "1.1.3", + "@rolldown/binding-darwin-x64": "1.1.3", + "@rolldown/binding-freebsd-x64": "1.1.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.3", + "@rolldown/binding-linux-arm64-gnu": "1.1.3", + "@rolldown/binding-linux-arm64-musl": "1.1.3", + "@rolldown/binding-linux-ppc64-gnu": "1.1.3", + "@rolldown/binding-linux-s390x-gnu": "1.1.3", + "@rolldown/binding-linux-x64-gnu": "1.1.3", + "@rolldown/binding-linux-x64-musl": "1.1.3", + "@rolldown/binding-openharmony-arm64": "1.1.3", + "@rolldown/binding-wasm32-wasi": "1.1.3", + "@rolldown/binding-win32-arm64-msvc": "1.1.3", + "@rolldown/binding-win32-x64-msvc": "1.1.3" } }, "node_modules/rollup": { - "version": "4.60.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz", - "integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==", + "version": "4.62.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", + "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==", "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@types/estree": "1.0.9" }, "bin": { "rollup": "dist/bin/rollup" @@ -5650,31 +5605,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.60.2", - "@rollup/rollup-android-arm64": "4.60.2", - "@rollup/rollup-darwin-arm64": "4.60.2", - "@rollup/rollup-darwin-x64": "4.60.2", - "@rollup/rollup-freebsd-arm64": "4.60.2", - "@rollup/rollup-freebsd-x64": "4.60.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", - "@rollup/rollup-linux-arm-musleabihf": "4.60.2", - "@rollup/rollup-linux-arm64-gnu": "4.60.2", - "@rollup/rollup-linux-arm64-musl": "4.60.2", - "@rollup/rollup-linux-loong64-gnu": "4.60.2", - "@rollup/rollup-linux-loong64-musl": "4.60.2", - "@rollup/rollup-linux-ppc64-gnu": "4.60.2", - "@rollup/rollup-linux-ppc64-musl": "4.60.2", - "@rollup/rollup-linux-riscv64-gnu": "4.60.2", - "@rollup/rollup-linux-riscv64-musl": "4.60.2", - "@rollup/rollup-linux-s390x-gnu": "4.60.2", - "@rollup/rollup-linux-x64-gnu": "4.60.2", - "@rollup/rollup-linux-x64-musl": "4.60.2", - "@rollup/rollup-openbsd-x64": "4.60.2", - "@rollup/rollup-openharmony-arm64": "4.60.2", - "@rollup/rollup-win32-arm64-msvc": "4.60.2", - "@rollup/rollup-win32-ia32-msvc": "4.60.2", - "@rollup/rollup-win32-x64-gnu": "4.60.2", - "@rollup/rollup-win32-x64-msvc": "4.60.2", + "@rollup/rollup-android-arm-eabi": "4.62.2", + "@rollup/rollup-android-arm64": "4.62.2", + "@rollup/rollup-darwin-arm64": "4.62.2", + "@rollup/rollup-darwin-x64": "4.62.2", + "@rollup/rollup-freebsd-arm64": "4.62.2", + "@rollup/rollup-freebsd-x64": "4.62.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.2", + "@rollup/rollup-linux-arm-musleabihf": "4.62.2", + "@rollup/rollup-linux-arm64-gnu": "4.62.2", + "@rollup/rollup-linux-arm64-musl": "4.62.2", + "@rollup/rollup-linux-loong64-gnu": "4.62.2", + "@rollup/rollup-linux-loong64-musl": "4.62.2", + "@rollup/rollup-linux-ppc64-gnu": "4.62.2", + "@rollup/rollup-linux-ppc64-musl": "4.62.2", + "@rollup/rollup-linux-riscv64-gnu": "4.62.2", + "@rollup/rollup-linux-riscv64-musl": "4.62.2", + "@rollup/rollup-linux-s390x-gnu": "4.62.2", + "@rollup/rollup-linux-x64-gnu": "4.62.2", + "@rollup/rollup-linux-x64-musl": "4.62.2", + "@rollup/rollup-openbsd-x64": "4.62.2", + "@rollup/rollup-openharmony-arm64": "4.62.2", + "@rollup/rollup-win32-arm64-msvc": "4.62.2", + "@rollup/rollup-win32-ia32-msvc": "4.62.2", + "@rollup/rollup-win32-x64-gnu": "4.62.2", + "@rollup/rollup-win32-x64-msvc": "4.62.2", "fsevents": "~2.3.2" } }, @@ -5684,12 +5639,6 @@ "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==", "license": "MIT" }, - "node_modules/rw": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", - "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==", - "license": "BSD-3-Clause" - }, "node_modules/sade": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/sade/-/sade-1.8.1.tgz", @@ -5718,9 +5667,9 @@ } }, "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "dev": true, "license": "ISC", "bin": { @@ -5737,9 +5686,9 @@ "license": "ISC" }, "node_modules/set-cookie-parser": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.0.tgz", - "integrity": "sha512-kjnC1DXBHcxaOaOXBHBeRtltsDG2nUiUni+jP92M9gYdW12rsmx92UsfpH7o5tDRs7I1ZZPSQJQGv3UaRfCiuw==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-3.1.1.tgz", + "integrity": "sha512-vM9SUhjsUYs6UeJUmygc5Ofm5eQGe85riob5ju6XCgFGJI5PLV4nrDAQpQjd+LkFBpAkADn5BQQpZ9EUNkyLuA==", "license": "MIT" }, "node_modules/setimmediate": { @@ -6014,14 +5963,14 @@ } }, "node_modules/svelte": { - "version": "5.55.7", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.55.7.tgz", - "integrity": "sha512-ymI5ykLPwIHW839E053FQbI1G+jnRFJEw3Kv5Y4njixVWywQBx+NUFpkkKyk5LIb36Fg9DVXSYpqiGekLD0hyw==", + "version": "5.56.4", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.56.4.tgz", + "integrity": "sha512-/d0QHehmRuJW8gVz395MTkPcPozxzdjBMBE8oEYGz8O3b9KTMzzQ9ZHJQLuFKOHOPQbU6kx/X4iid/EBBzH7iw==", "license": "MIT", "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", - "@sveltejs/acorn-typescript": "^1.0.5", + "@sveltejs/acorn-typescript": "^1.0.10", "@types/estree": "^1.0.5", "@types/trusted-types": "^2.0.7", "acorn": "^8.12.1", @@ -6030,7 +5979,7 @@ "clsx": "^2.1.1", "devalue": "^5.8.1", "esm-env": "^1.2.1", - "esrap": "^2.2.4", + "esrap": "^2.2.12", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", @@ -6041,13 +5990,14 @@ } }, "node_modules/svelte-check": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.4.6.tgz", - "integrity": "sha512-kP1zG81EWaFe9ZyTv4ZXv44Csi6Pkdpb7S3oj6m+K2ec/IcDg/a8LsFsnVLqm2nxtkSwsd5xPj/qFkTBgXHXjg==", + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-4.7.1.tgz", + "integrity": "sha512-FGUOmAqxXdN/H9Zm8slrqO7SLtFisXRB7rfOsHNJ3MLTD2po/+Stg8XyErkpumPHbuUiYTcqrEIzxpVWKTLqtg==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", + "@sveltejs/load-config": "^0.2.0", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", @@ -6575,70 +6525,141 @@ } }, "node_modules/swagger-jsdoc": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", - "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.3.0.tgz", + "integrity": "sha512-I+iQjVGV3t28pOkQUJv2MncthvOtkEactOn8R76SvSYhxgtIn7FoqfDHwQaN+GBnQdXQLrhgDXseKitmJcHMsA==", "dev": true, "license": "MIT", "dependencies": { + "@apidevtools/swagger-parser": "^12.1.0", "commander": "6.2.0", "doctrine": "3.0.0", - "glob": "7.1.6", + "glob": "11.1.0", "lodash.mergewith": "^4.6.2", - "swagger-parser": "^10.0.3", "yaml": "2.0.0-1" }, "bin": { "swagger-jsdoc": "bin/swagger-jsdoc.js" }, "engines": { - "node": ">=12.0.0" + "node": ">=20.0.0" + } + }, + "node_modules/swagger-jsdoc/node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/swagger-jsdoc/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/swagger-jsdoc/node_modules/brace-expansion": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", - "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" } }, "node_modules/swagger-jsdoc/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": "*" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/swagger-jsdoc/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "node_modules/swagger-jsdoc/node_modules/jackspeak": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^1.1.7" + "@isaacs/cliui": "^9.0.0" }, "engines": { - "node": "*" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/swagger-jsdoc/node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/swagger-jsdoc/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/swagger-jsdoc/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/swagger-jsdoc/node_modules/yaml": { @@ -6651,23 +6672,10 @@ "node": ">= 6" } }, - "node_modules/swagger-parser": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", - "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@apidevtools/swagger-parser": "10.0.3" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/tailwindcss": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.4.tgz", - "integrity": "sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.2.tgz", + "integrity": "sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==", "license": "MIT" }, "node_modules/tapable": { @@ -6694,9 +6702,9 @@ } }, "node_modules/three": { - "version": "0.183.2", - "resolved": "https://registry.npmjs.org/three/-/three-0.183.2.tgz", - "integrity": "sha512-di3BsL2FEQ1PA7Hcvn4fyJOlxRRgFYBpMTcyOgkwJIaDOdJMebEFPA+t98EvjuljDx4hNulAGwF6KIjtwI5jgQ==", + "version": "0.185.0", + "resolved": "https://registry.npmjs.org/three/-/three-0.185.0.tgz", + "integrity": "sha512-+yRrcRO2iZa8uzvNNl0d7cL4huhgKgBvVJ0njcTe8xFqZ6DMAFZdCKDP91SEAuj25bNAj7k1QQdf+srZywVK6w==", "license": "MIT" }, "node_modules/three-instanced-uniforms-mesh": { @@ -6712,9 +6720,9 @@ } }, "node_modules/three-mesh-bvh": { - "version": "0.9.9", - "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.9.9.tgz", - "integrity": "sha512-FJKitcjvbALmeQRK+Sc+nLGorCpkrZBrbgJZFzhdyWboak37DZikn46hvQkNqSbJPm227ahYmS6k3N/GXaAyXw==", + "version": "0.9.10", + "resolved": "https://registry.npmjs.org/three-mesh-bvh/-/three-mesh-bvh-0.9.10.tgz", + "integrity": "sha512-UOlTgPIeqUURcwaG8knxvBaruwZlC4X3/WSHEFO7rYvMVv/YNUrkfFEszvfj36pXV88dCHoHNnIp0PifkirnTQ==", "license": "MIT", "peerDependencies": { "three": ">= 0.159.0" @@ -6778,9 +6786,9 @@ "license": "MIT" }, "node_modules/tinyexec": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", - "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", "license": "MIT", "engines": { "node": ">=18" @@ -6897,9 +6905,9 @@ } }, "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", "devOptional": true, "license": "Apache-2.0", "bin": { @@ -6911,9 +6919,9 @@ } }, "node_modules/undici-types": { - "version": "7.19.2", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.19.2.tgz", - "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz", + "integrity": "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==", "license": "MIT" }, "node_modules/unicode-properties": { @@ -6995,26 +7003,16 @@ "base64-arraybuffer": "^1.0.2" } }, - "node_modules/validator": { - "version": "13.15.35", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.35.tgz", - "integrity": "sha512-TQ5pAGhd5whStmqWvYF4OjQROlmv9SMFVt37qoCBdqRffuuklWYQlCNnEs2ZaIBD1kZRNnikiZOS1eqgkar0iw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, "node_modules/vite": { - "version": "8.0.16", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", - "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.1.tgz", + "integrity": "sha512-X/05/cT+VITy2AeDc1der6smvGWWREtL4hPbPTaVbjSBuuWkmNOjR6HP3NzqcQA2nF6VHGUPaFRJyft/2AE9Kg==", "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", - "postcss": "^8.5.15", - "rolldown": "1.0.3", + "postcss": "^8.5.16", + "rolldown": "~1.1.3", "tinyglobby": "^0.2.17" }, "bin": { @@ -7031,7 +7029,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.1.18", + "@vitejs/devtools": "^0.3.0", "esbuild": "^0.27.0 || ^0.28.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -7131,18 +7129,18 @@ } }, "node_modules/vitest": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.4.tgz", - "integrity": "sha512-tFuJqTxKb8AvfyqMfnavXdzfy3h3sWZRWwfluGbkeR7n0HUev+FmNgZ8SDrRBTVrVCjgH5cA21qGbCffMNtWvg==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", + "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", "license": "MIT", "dependencies": { - "@vitest/expect": "4.1.4", - "@vitest/mocker": "4.1.4", - "@vitest/pretty-format": "4.1.4", - "@vitest/runner": "4.1.4", - "@vitest/snapshot": "4.1.4", - "@vitest/spy": "4.1.4", - "@vitest/utils": "4.1.4", + "@vitest/expect": "4.1.9", + "@vitest/mocker": "4.1.9", + "@vitest/pretty-format": "4.1.9", + "@vitest/runner": "4.1.9", + "@vitest/snapshot": "4.1.9", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -7170,12 +7168,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.1.4", - "@vitest/browser-preview": "4.1.4", - "@vitest/browser-webdriverio": "4.1.4", - "@vitest/coverage-istanbul": "4.1.4", - "@vitest/coverage-v8": "4.1.4", - "@vitest/ui": "4.1.4", + "@vitest/browser-playwright": "4.1.9", + "@vitest/browser-preview": "4.1.9", + "@vitest/browser-webdriverio": "4.1.9", + "@vitest/coverage-istanbul": "4.1.9", + "@vitest/coverage-v8": "4.1.9", + "@vitest/ui": "4.1.9", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -7219,17 +7217,6 @@ } } }, - "node_modules/vt-pbf": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz", - "integrity": "sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==", - "license": "MIT", - "dependencies": { - "@mapbox/point-geometry": "0.1.0", - "@mapbox/vector-tile": "^1.3.1", - "pbf": "^3.2.1" - } - }, "node_modules/w3c-keyname": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", @@ -7243,18 +7230,19 @@ "license": "MIT" }, "node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, "license": "ISC", "dependencies": { - "isexe": "^3.1.1" + "isexe": "^2.0.0" }, "bin": { - "node-which": "bin/which.js" + "node-which": "bin/node-which" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": ">= 8" } }, "node_modules/which-module": { @@ -7377,13 +7365,6 @@ "node": ">=8" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, "node_modules/xml2js": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.23.tgz", @@ -7488,38 +7469,6 @@ "node": ">=8" } }, - "node_modules/z-schema": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", - "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash.get": "^4.4.2", - "lodash.isequal": "^4.5.0", - "validator": "^13.7.0" - }, - "bin": { - "z-schema": "bin/z-schema" - }, - "engines": { - "node": ">=8.0.0" - }, - "optionalDependencies": { - "commander": "^9.4.1" - } - }, - "node_modules/z-schema/node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": "^12.20.0 || >=14" - } - }, "node_modules/zimmerframe": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.4.tgz", diff --git a/web/package.json b/web/package.json index 8695224b..07d4ad51 100644 --- a/web/package.json +++ b/web/package.json @@ -15,30 +15,30 @@ "test:unit": "vitest --passWithNoTests" }, "devDependencies": { - "@playwright/test": "^1.58.2", + "@playwright/test": "^1.61.1", "@sveltejs/adapter-auto": "^7.0.1", - "@sveltejs/enhanced-img": "^0.10.4", - "@sveltejs/kit": "^2.60.1", - "@sveltejs/vite-plugin-svelte": "^7.0.0", - "@tailwindcss/typography": "^0.5.19", + "@sveltejs/enhanced-img": "^0.11.0", + "@sveltejs/kit": "^2.68.0", + "@sveltejs/vite-plugin-svelte": "^7.1.2", + "@tailwindcss/typography": "^0.5.20", "@types/canvas-confetti": "^1.9.0", - "@types/node": "^25.3.3", - "postcss": "^8.5.6", - "svelte": "^5.55.7", - "svelte-check": "^4.3.6", + "@types/node": "^26.0.1", + "postcss": "^8.5.16", + "svelte": "^5.56.4", + "svelte-check": "^4.7.1", "sveltekit-openapi-generator": "^0.1.6", - "tslib": "^2.4.1", - "typescript": "^5.9.3", - "vite": "^8.0.16" + "tslib": "^2.8.1", + "typescript": "^6.0.3", + "vite": "8.1.1" }, "type": "module", "dependencies": { "@felte/validator-zod": "^1.0.18", - "@fortawesome/fontawesome-free": "^7.1.0", - "@sveltejs/adapter-node": "^5.5.2", - "@tailwindcss/vite": "^4.2.4", - "@threlte/core": "^8.5.9", - "@threlte/extras": "^9.14.6", + "@fortawesome/fontawesome-free": "^7.3.0", + "@sveltejs/adapter-node": "^5.5.7", + "@tailwindcss/vite": "^4.3.2", + "@threlte/core": "^8.5.16", + "@threlte/extras": "^9.21.0", "@tiptap/core": "^3.27.1", "@tiptap/extension-heading": "^3.27.1", "@tiptap/extension-link": "^3.27.1", @@ -47,42 +47,42 @@ "@tiptap/pm": "^3.27.1", "@tiptap/starter-kit": "^3.27.1", "@tiptap/suggestion": "^3.27.1", - "@turf/destination": "^7.3.3", - "@turf/distance": "^7.3.3", + "@turf/destination": "^7.3.5", + "@turf/distance": "^7.3.5", "@types/chart.js": "^4.0.1", "@types/supercluster": "^7.1.3", - "@types/three": "^0.183.1", - "@xmldom/xmldom": "^0.8.12", + "@types/three": "^0.185.0", + "@xmldom/xmldom": "^0.9.10", "activitypub-types": "^1.1.0", - "autoprefixer": "^10.4.24", + "autoprefixer": "^10.5.2", "canvas-confetti": "^1.9.4", "canvg": "^4.0.3", "chart.js": "^4.5.1", "chartjs-plugin-crosshair": "^2.0.0", - "chartjs-plugin-zoom": "^2.1.0", + "chartjs-plugin-zoom": "^2.2.0", "chokidar": "^5.0.0", "crypto-random-string": "^5.0.0", "felte": "^1.3.0", "heic2any": "^0.0.4", "instead": "^1.0.3", "isomorphic-xml2js": "^0.1.3", - "json-diff-ts": "^4.8.2", + "json-diff-ts": "^4.10.4", "jspdf": "^4.2.1", "jszip": "^3.10.1", - "maplibre-gl": "^4.7.1", - "marked": "^17.0.4", - "meilisearch": "^0.57.0", + "maplibre-gl": "^5.24.0", + "marked": "^18.0.5", + "meilisearch": "^0.58.0", "ngeohash": "^0.6.3", - "nouislider": "^15.7.1", - "pdfkit": "^0.17.2", - "photoswipe": "^5.4.3", - "pocketbase": "^0.26.8", - "qrcode": "^1.4.4", + "nouislider": "^15.8.1", + "pdfkit": "^0.19.1", + "photoswipe": "^5.4.4", + "pocketbase": "^0.27.0", + "qrcode": "^1.5.4", "supercluster": "^8.0.1", - "svelte-i18n": "^4.0.0", - "tailwindcss": "^4.2.4", - "three": "^0.183.1", - "vitest": "^4.1.4", + "svelte-i18n": "^4.0.1", + "tailwindcss": "^4.3.2", + "three": "^0.185.0", + "vitest": "^4.1.9", "zod": "^3.24.1" }, "overrides": { diff --git a/web/src/css/components.css b/web/src/css/components.css index dc1ea2dc..fc24ae1b 100644 --- a/web/src/css/components.css +++ b/web/src/css/components.css @@ -1,6 +1,15 @@ @import 'tailwindcss'; @reference "./app.css"; +:root { + --tooltip-background: rgba(36, 39, 52, 0.75); + --tooltip-border-radius: 4px; + --tooltip-color: #fff; + --tooltip-font-size: 12px; + --tooltip-offset-top: 24px; + --tooltip-padding: 6px 10px; +} + .btn-primary { @apply min-h-10 text-white rounded-lg px-4 py-2 bg-primary font-semibold transition-all hover:bg-primary-hover focus:ring-4 ring-input-ring } @@ -58,13 +67,13 @@ } .tooltip:before { - background: rgba(36, 39, 52, 0.75); - border-radius: 4px; - color: #fff; + background: var(--tooltip-background); + border-radius: var(--tooltip-border-radius); + color: var(--tooltip-color); content: attr(data-title); - font-size: 12px; - padding: 6px 10px; - top: 24px; + font-size: var(--tooltip-font-size); + padding: var(--tooltip-padding); + top: var(--tooltip-offset-top); white-space: nowrap; z-index: 10 } diff --git a/web/src/lib/components/base/calendar.svelte b/web/src/lib/components/base/calendar.svelte index fd886e59..ba026068 100644 --- a/web/src/lib/components/base/calendar.svelte +++ b/web/src/lib/components/base/calendar.svelte @@ -1,8 +1,9 @@ - -
{text}
+ {#if children} + {@render children()} + {:else} +{text}
+ {/if} {/snippet} {#snippet footer()}- {$_(category)} + {displayCategoryName( + category, + $locale, + )} + {#if subcategory} + + / {displaySubcategoryLabel( + subcategory, + $locale, + )} + + {/if}
{/if} {#if location} diff --git a/web/src/lib/components/settings/plugins/plugin_instance_settings_modal.svelte b/web/src/lib/components/settings/plugins/plugin_instance_settings_modal.svelte index be1e10de..ad9f7916 100644 --- a/web/src/lib/components/settings/plugins/plugin_instance_settings_modal.svelte +++ b/web/src/lib/components/settings/plugins/plugin_instance_settings_modal.svelte @@ -6,11 +6,18 @@ import TextField from "$lib/components/base/text_field.svelte"; import Toggle from "$lib/components/base/toggle.svelte"; import PluginMergeSettings from "$lib/components/settings/plugins/plugin_merge_settings.svelte"; + import CategoryPicker from "$lib/components/trail/category_picker.svelte"; import type { Category } from "$lib/models/category"; import type { PluginInstance } from "$lib/models/plugin_instance"; import type { ConfigField, PluginProvider } from "$lib/models/plugin_provider"; + import type { Subcategory } from "$lib/models/subcategory"; import { plugin_auth_validate, plugin_oauth_start } from "$lib/stores/plugin_instance_store"; import { show_toast } from "$lib/stores/toast_store.svelte"; + import { + categoryMappingTargetFromUnknown, + categoryMappingTargetToPickerValue, + type CategoryMappingTarget, + } from "$lib/util/category_util"; import { translatePluginAPIError } from "$lib/util/plugin_error_i18n"; import { configFieldDescription, @@ -34,12 +41,13 @@ interface Props { plugin: PluginProvider; categories?: Category[]; + subcategories?: Subcategory[]; instance?: PluginInstance; onbeforecategorymappingsave?: (instance: PluginInstanceForm) => Promise- {$_( - trail.expand?.category?.name ?? trail.category ?? "-", - )} + + + {#if displayTrailCategoryBadgeIcon(trail)} + + {/if} + {displayCategoryName( + trail.expand?.category ?? { name: trail.category ?? "" }, + $locale, + ) || "-"} + {#if trail.expand?.subcategory} + + / {displaySubcategoryLabel( + trail.expand.subcategory, + $locale, + )} + + {/if}
{/if} {#if trail.location} diff --git a/web/src/lib/components/trail/trail_picker.svelte b/web/src/lib/components/trail/trail_picker.svelte index deb2c46d..c322c27e 100644 --- a/web/src/lib/components/trail/trail_picker.svelte +++ b/web/src/lib/components/trail/trail_picker.svelte @@ -36,7 +36,7 @@ attributionControl: false, dragPan: false, scrollZoom: false, - preserveDrawingBuffer: true, + canvasContextAttributes: { preserveDrawingBuffer: true } }); } diff --git a/web/src/lib/i18n/locales/de.json b/web/src/lib/i18n/locales/de.json index 105966f2..5f2ffdec 100644 --- a/web/src/lib/i18n/locales/de.json +++ b/web/src/lib/i18n/locales/de.json @@ -3,9 +3,10 @@ "Canoeing": "Kanufahren", "Climbing": "Klettern", "Hiking": "Wandern", + "Running": "Laufen", "Other": "Sonstiges", "Skiing": "Skifahren", - "Walking": "Laufen", + "Walking": "Spazieren", "about": "Über", "account-delete-confirm": "Du bist dabei, dein Konto zu löschen. Alle deine Routen werden ebenfalls gelöscht. Möchtest du fortfahren?", "account-privacy": "Privatsphäre des Kontos", @@ -17,6 +18,7 @@ "add-waypoint": "Wegpunkt hinzufügen", "added-trail-to": "Route hinzugefügt zu", "added-trails-to": "Routen hinzugefügt zu", + "adjust": "Anpassen", "after": "Nach", "all-activities": "Alle Aktivitäten", "allow-auto-geolocate": "Beginne das Zeichnen einer neuen Route am aktuellen Standort", @@ -31,6 +33,7 @@ "append-waypoint-description": "Kommentar anhängen", "append-waypoint-photos": "Fotos hinzufügen", "append-waypoint-title": "Titel anhängen", + "apply": "Anwenden", "apply-user-settings": "Benutzereinstellungen anwenden", "attraction": "Sehenswürdigkeit", "author": "Autor", @@ -49,6 +52,8 @@ "bicycle-rental": "Fahrradverleih", "bicycle-shop": "Fahrrad-Reparatur", "bike-type": "Fahrradtyp", + "bulk-edit-selected-trails": "{n, plural, =1 {1 ausgewählte Route} other {# ausgewählte Routen}}", + "bulk-edit-updated-trails": "{n, plural, =1 {1 Route aktualisiert} other {# Routen aktualisiert}}", "bus-stop": "Bushaltestelle", "by": "von", "calendar": { @@ -70,6 +75,24 @@ "card": "{n, plural, =1 {Karte} other {Karten}}", "categories": "Kategorien", "category": "Kategorie", + "category-preference-visible": "Anzeigen", + "category-preferences": "Kategorie-Einstellungen", + "category-preferences-description": "Definiere, welche Kategorien und Unterkategorien für deine Routen relevant sind und in welcher Reihenfolge Kategorien erscheinen sollen.", + "confirm-disable-category-with-trails-title": "Kategorie ausblenden?", + "confirm-disable-category-intro": "Diese Kategorie wird noch an einigen Stellen verwendet. Prüfe die Konflikte, bevor du sie ausblendest.", + "confirm-disable-category-with-trails": "{count, plural, =1 {1 deiner eigenen Routen verwendet „{name}“.} other {# deiner eigenen Routen verwenden „{name}“.}}", + "confirm-disable-category-active-plugin-mappings": "Aktive Plugins referenzieren diese Kategorie in ihren Zuordnungen: {plugins}.", + "confirm-disable-category-inactive-plugin-mappings": "Vorbereitete, aber inaktive Plugins referenzieren diese Kategorie ebenfalls: {plugins}.", + "confirm-disable-category-anyway": "Diese Kategorie trotzdem ausblenden?", + "confirm-disable-subcategory-with-trails-title": "Unterkategorie ausblenden?", + "confirm-disable-subcategory-intro": "Diese Unterkategorie wird noch an einigen Stellen verwendet. Prüfe die Konflikte, bevor du sie ausblendest.", + "confirm-disable-subcategory-with-trails": "{count, plural, =1 {1 deiner eigenen Routen verwendet „{name}“.} other {# deiner eigenen Routen verwenden „{name}“.}}", + "confirm-disable-subcategory-active-plugin-mappings": "Aktive Plugins referenzieren diese Unterkategorie in ihren Zuordnungen: {plugins}.", + "confirm-disable-subcategory-inactive-plugin-mappings": "Vorbereitete, aber inaktive Plugins referenzieren diese Unterkategorie ebenfalls: {plugins}.", + "confirm-disable-subcategory-anyway": "Diese Unterkategorie trotzdem ausblenden?", + "conflicts": "Konflikte", + "collapse-subcategories": "Unterkategorien einklappen", + "category-filter-hidden-active": "{categories} ist in deinen Kategorie-Einstellungen ausgeblendet.", "category-mapping": "Kategorie-Zuordnung", "category-mapping-help": "Entfernte Provider-Kategorien werden bewusst nicht zugeordnet und erhalten beim Import keine Kategorie.", "change": "Ändern", @@ -175,6 +198,7 @@ "error-printing-map": "Fehler beim Drucken der Karte", "error-reading-file": "Fehler beim Lesen der Datei", "error-saving-list": "Fehler beim Speichern der Liste", + "error-saving-settings": "Fehler beim Speichern der Einstellungen", "error-saving-trail": "Fehler beim Speichern der Route", "error-setting-up-plugin": "Fehler beim Einrichten des {provider}-Plugins", "error-starting-oauth": "Fehler beim Starten der OAuth-Verbindung", @@ -185,9 +209,14 @@ "error-uploading-trail-to-hammerhead": "Fehler beim Hochladen der Route zu Hammerhead", "est-duration": "Gesch. Dauer", "everyone-with-the-link": "Jeder mit dem Link", + "expand-subcategories": "Unterkategorien ausklappen", "expand-trail-list": "", "expiration": "", "expires": "", + "exclude-federated": "Föderierte ausschließen", + "exclude-search": "Von Suche ausschließen", + "include-federated": "Föderierte einschließen", + "include-search": "In Suche einschließen", "explore": "Erkunden", "explore-some-trails": "Erkunde einige Routen", "export": "Exportieren", @@ -223,6 +252,9 @@ "get-started": "Los geht’s", "grid": "Gitter", "grocery-store": "Lebensmittelgeschäft", + "hide-design": "Im Routeneditor ausblenden", + "show-design": "Im Routeneditor auswählbar", + "hammerhead-integration-after-date-hint": "Wenn Ihr Hammerhead Konto bereits mit anderen Trail-Datenbanken wie komoot oder Strava synchronisiert ist, kann die zusätzliche Synchronisierung Ihrer Hammerhead-Daten zu Duplikaten führen. Um dies zu vermeiden, können Sie unten ein Startdatum festlegen, sodass nur Aktivitäten synchronisiert werden, die nach diesem Datum aufgezeichnet wurden.", "heading": "Überschrift", "height": "Höhe", "help": "Hilfe", @@ -362,6 +394,7 @@ "no-preference": "Keine Präferenz", "no-results": "Keine Ergebnisse gefunden", "no-routes-added": "Keine Routen hinzugefügt", + "no-subcategory": "Keine Unterkategorie", "no-waypoints-yet": "Noch keine Wegpunkte", "norwegian": "Norwegisch", "not-a-valid-email-address": "Keine gültige Email-Adresse", @@ -407,6 +440,7 @@ "profile": "Profil", "provider-category": "Provider-Kategorie", "public": "Öffentlich", + "priority": "Priorität", "public-access": "Öffentlicher Zugriff", "public-share-everyone": "Jeder im Internet mit dem Link kann diese Route sehen", "public-share-limited": "Nur Leute mit Zugriff können diese Route sehen", @@ -503,6 +537,7 @@ "stop-drawing": "Zeichnen beenden", "stop-editing": "Bearbeiten beenden", "subway-stop": "U-Bahn Eingang", + "subcategory": "Unterkategorie", "summit": "Gipfel", "summit-book": "Gipfelbuch", "summit-log": "{n, plural, =1 {Gipfelbuch-Eintrag} other {Gipfelbuch-Einträge}}", @@ -588,6 +623,7 @@ "tram-stop": "Tram Haltestelle", "unchanged": "unverändert", "units": "Einheiten", + "unprioritized": "Nicht priorisiert", "unlink": "Trennen", "upload-file": "Datei hochladen", "upload-gpx": "GPX hochladen", @@ -602,6 +638,7 @@ "username": "Nutzername", "username-not-unique": "Dieser Nutzername ist bereits vergeben. Bitte versuche es mit einem anderen.", "view": "Ansehen", + "view-affected-trails": "Betroffene Routen ansehen", "viewpoint": "Aussichtspunkt", "visibilty": "", "visibilty-status": "Sichtbarkeit", diff --git a/web/src/lib/i18n/locales/en.json b/web/src/lib/i18n/locales/en.json index f49ea388..7c6dd870 100644 --- a/web/src/lib/i18n/locales/en.json +++ b/web/src/lib/i18n/locales/en.json @@ -3,6 +3,7 @@ "Canoeing": "Canoeing", "Climbing": "Climbing", "Hiking": "Hiking", + "Running": "Running", "Other": "Other", "Skiing": "", "Walking": "Walking", @@ -17,6 +18,7 @@ "add-waypoint": "Add Waypoint", "added-trail-to": "Added trail to", "added-trails-to": "Added trails to", + "adjust": "Adjust", "after": "After", "all-activities": "All activities", "allow-auto-geolocate": "Begin drawing a new trail from your current location", @@ -31,6 +33,7 @@ "append-waypoint-description": "Append description", "append-waypoint-photos": "Add photos", "append-waypoint-title": "Append title", + "apply": "Apply", "apply-user-settings": "Apply user settings", "attraction": "Attraction", "author": "Author", @@ -49,6 +52,8 @@ "bicycle-rental": "Bicycle Rental", "bicycle-shop": "Bicycle Shop", "bike-type": "Bike Type", + "bulk-edit-selected-trails": "{n, plural, =1 {1 selected trail} other {# selected trails}}", + "bulk-edit-updated-trails": "{n, plural, =1 {1 trail updated} other {# trails updated}}", "bus-stop": "Bus stop", "by": "by", "calendar": { @@ -70,6 +75,24 @@ "card": "{n, plural, =1 {Card} other {Cards}}", "categories": "Categories", "category": "Category", + "category-preference-visible": "Show", + "category-preferences": "Category preferences", + "category-preferences-description": "Define which categories and subcategories are relevant to your trails and the order in which categories appear.", + "confirm-disable-category-with-trails-title": "Hide category?", + "confirm-disable-category-intro": "This category is still used in a few places. Review the conflicts before hiding it.", + "confirm-disable-category-with-trails": "{count, plural, =1 {1 of your own trails uses “{name}”.} other {# of your own trails use “{name}”.}}", + "confirm-disable-category-active-plugin-mappings": "Active plugins reference this category in their mappings: {plugins}.", + "confirm-disable-category-inactive-plugin-mappings": "Prepared but inactive plugins also reference this category: {plugins}.", + "confirm-disable-category-anyway": "Hide this category anyway?", + "confirm-disable-subcategory-with-trails-title": "Hide subcategory?", + "confirm-disable-subcategory-intro": "This subcategory is still used in a few places. Review the conflicts before hiding it.", + "confirm-disable-subcategory-with-trails": "{count, plural, =1 {1 of your own trails uses “{name}”.} other {# of your own trails use “{name}”.}}", + "confirm-disable-subcategory-active-plugin-mappings": "Active plugins reference this subcategory in their mappings: {plugins}.", + "confirm-disable-subcategory-inactive-plugin-mappings": "Prepared but inactive plugins also reference this subcategory: {plugins}.", + "confirm-disable-subcategory-anyway": "Hide this subcategory anyway?", + "conflicts": "Conflicts", + "collapse-subcategories": "Collapse subcategories", + "category-filter-hidden-active": "{categories} is hidden in your category preferences.", "category-mapping": "Category mapping", "category-mapping-help": "Removed provider categories are intentionally left unmapped and will be imported without a category.", "change": "Change", @@ -175,6 +198,7 @@ "error-printing-map": "Error printing map", "error-reading-file": "Error reading file", "error-saving-list": "Error saving list", + "error-saving-settings": "Error saving settings", "error-saving-trail": "Error saving trail", "error-setting-up-plugin": "Error setting up {provider} plugin", "error-starting-oauth": "Error starting OAuth connection", @@ -185,9 +209,14 @@ "error-uploading-trail-to-hammerhead": "Error uploading trail to Hammerhead", "est-duration": "Est. duration", "everyone-with-the-link": "Everyone with the link", + "expand-subcategories": "Expand subcategories", "expand-trail-list": "Expand trail list", "expiration": "Expiration", "expires": "Expires", + "exclude-federated": "Exclude federated", + "exclude-search": "Exclude from search", + "include-federated": "Include federated", + "include-search": "Include in search", "explore": "Explore", "explore-some-trails": "Explore some trails", "export": "Export", @@ -223,6 +252,9 @@ "get-started": "Get started", "grid": "Grid", "grocery-store": "Grocery store", + "hide-design": "Hide in trail editor", + "show-design": "Available in trail editor", + "hammerhead-integration-after-date-hint": "If your hammerhead account is already synced with other trail databases, such as komoot or Strava, start syncing your Hammerhead data may result in duplicates. To avoid this, you can set an start date below, meaning only activities recorded after this date will be synced.", "heading": "Heading", "height": "Height", "help": "Help", @@ -362,6 +394,7 @@ "no-preference": "No preference", "no-results": "No results found", "no-routes-added": "No routes added", + "no-subcategory": "No subcategory", "no-waypoints-yet": "No waypoints yet", "norwegian": "Norwegian", "not-a-valid-email-address": "Not a valid email address", @@ -407,6 +440,7 @@ "profile": "Profile", "provider-category": "Provider category", "public": "Public", + "priority": "Priority", "public-access": "Public access", "public-share-everyone": "Everyone on the internet with the link can see this trail", "public-share-limited": "Only people with access can open the link", @@ -503,6 +537,7 @@ "stop-drawing": "Stop drawing", "stop-editing": "Stop editing", "subway-stop": "Subway entrance", + "subcategory": "Subcategory", "summit": "Summit", "summit-book": "Summit Book", "summit-log": "{n, plural, =1 {Summit log} other {Summit logs}}", @@ -588,6 +623,7 @@ "tram-stop": "Tram stop", "unchanged": "unchanged", "units": "Units", + "unprioritized": "Unprioritized", "unlink": "Unlink", "upload-file": "Upload file", "upload-gpx": "Upload GPX", @@ -602,6 +638,7 @@ "username": "Username", "username-not-unique": "This username is already taken. Please try another.", "view": "View", + "view-affected-trails": "View affected trails", "viewpoint": "Viewpoint", "visibilty": "Visibility", "visibilty-status": "Visibility status", diff --git a/web/src/lib/models/api/category_preference_schema.ts b/web/src/lib/models/api/category_preference_schema.ts new file mode 100644 index 00000000..3ad52055 --- /dev/null +++ b/web/src/lib/models/api/category_preference_schema.ts @@ -0,0 +1,15 @@ +import { z } from "zod"; + +const UserCategoryPreferenceUpsertSchema = z.object({ + category: z.string().length(15), + visible: z.boolean(), +}); + +const UserCategoryPreferenceReorderSchema = z.object({ + categories: z.array(z.string().length(15)), +}); + +export { + UserCategoryPreferenceReorderSchema, + UserCategoryPreferenceUpsertSchema, +}; diff --git a/web/src/lib/models/api/openapi_schemas.ts b/web/src/lib/models/api/openapi_schemas.ts index aa2a218a..7f18af3d 100644 --- a/web/src/lib/models/api/openapi_schemas.ts +++ b/web/src/lib/models/api/openapi_schemas.ts @@ -158,6 +158,195 @@ * type: string * description: Tag name * + * CategoryTranslation: + * type: object + * properties: + * name: + * type: string + * short_name: + * type: string + * + * Category: + * type: object + * required: + * - id + * - name + * properties: + * id: + * type: string + * description: Category ID (15 chars) + * name: + * type: string + * short_name: + * type: string + * nullable: true + * icon: + * type: string + * nullable: true + * translations: + * type: object + * nullable: true + * additionalProperties: + * $ref: '#/components/schemas/CategoryTranslation' + * settings: + * type: object + * nullable: true + * properties: + * wp_merge_enabled: + * type: boolean + * wp_merge_radius: + * type: number + * created: + * type: string + * format: date-time + * updated: + * type: string + * format: date-time + * + * Subcategory: + * type: object + * required: + * - id + * - category + * - name + * properties: + * id: + * type: string + * description: Subcategory ID (15 chars) + * category: + * type: string + * description: Parent category ID (15 chars) + * name: + * type: string + * short_name: + * type: string + * nullable: true + * icon: + * type: string + * nullable: true + * badge_icon: + * type: string + * nullable: true + * translations: + * type: object + * nullable: true + * additionalProperties: + * $ref: '#/components/schemas/CategoryTranslation' + * created: + * type: string + * format: date-time + * updated: + * type: string + * format: date-time + * + * UserCategoryPreference: + * type: object + * required: + * - id + * - user + * - category + * - visible + * properties: + * id: + * type: string + * description: Preference ID (15 chars) + * user: + * type: string + * description: User ID (15 chars) + * category: + * type: string + * description: Category ID (15 chars) + * visible: + * type: boolean + * priority: + * type: integer + * nullable: true + * created: + * type: string + * format: date-time + * updated: + * type: string + * format: date-time + * + * UserCategoryPreferenceUpsertInput: + * type: object + * required: + * - category + * - visible + * properties: + * category: + * type: string + * description: Category ID (15 chars) + * visible: + * type: boolean + * + * UserCategoryPreferenceReorderInput: + * type: object + * required: + * - categories + * properties: + * categories: + * type: array + * items: + * type: string + * description: Category ID (15 chars) + * + * UserSubcategoryPreference: + * type: object + * required: + * - id + * - user + * - subcategory + * - visible + * properties: + * id: + * type: string + * description: Preference ID (15 chars) + * user: + * type: string + * description: User ID (15 chars) + * subcategory: + * type: string + * description: Subcategory ID (15 chars) + * visible: + * type: boolean + * priority: + * type: integer + * nullable: true + * created: + * type: string + * format: date-time + * updated: + * type: string + * format: date-time + * + * UserSubcategoryPreferenceUpsertInput: + * type: object + * required: + * - subcategory + * - visible + * properties: + * subcategory: + * type: string + * description: Subcategory ID (15 chars) + * visible: + * type: boolean + * + * UserSubcategoryPreferenceReorderInput: + * type: object + * required: + * - category + * - subcategories + * properties: + * category: + * type: string + * description: Category ID (15 chars) + * subcategories: + * type: array + * items: + * type: string + * description: Subcategory ID (15 chars) + * * Trail: * type: object * required: @@ -220,6 +409,9 @@ * category: * type: string * description: Category ID (15 chars) + * subcategory: + * type: string + * description: Subcategory ID (15 chars) * tags: * type: array * items: @@ -290,6 +482,8 @@ * default: 0 * category: * type: string + * subcategory: + * type: string * tags: * type: array * items: @@ -348,6 +542,8 @@ * default: 0 * category: * type: string + * subcategory: + * type: string * tags: * type: array * items: diff --git a/web/src/lib/models/api/subcategory_preference_schema.ts b/web/src/lib/models/api/subcategory_preference_schema.ts new file mode 100644 index 00000000..ac36e940 --- /dev/null +++ b/web/src/lib/models/api/subcategory_preference_schema.ts @@ -0,0 +1,16 @@ +import { z } from "zod"; + +const UserSubcategoryPreferenceUpsertSchema = z.object({ + subcategory: z.string().length(15), + visible: z.boolean(), +}); + +const UserSubcategoryPreferenceReorderSchema = z.object({ + category: z.string().length(15), + subcategories: z.array(z.string().length(15)), +}); + +export { + UserSubcategoryPreferenceReorderSchema, + UserSubcategoryPreferenceUpsertSchema, +}; diff --git a/web/src/lib/models/api/trail_schema.ts b/web/src/lib/models/api/trail_schema.ts index 609b6380..5824daf8 100644 --- a/web/src/lib/models/api/trail_schema.ts +++ b/web/src/lib/models/api/trail_schema.ts @@ -21,6 +21,7 @@ const TrailCreateSchema = z.object({ thumbnail: z.number().int().nonnegative().optional(), like_count: z.number().int().min(0).optional().default(0), category: z.string().length(15).optional().or(z.literal('')), + subcategory: z.string().length(15).optional().or(z.literal('')), tags: z.array(z.string()).default([]), gpx: z.string().optional(), author: z.string().length(15), @@ -47,6 +48,7 @@ const TrailUpdateSchema = z.object({ thumbnail: z.number().int().nonnegative().optional(), like_count: z.number().int().min(0).optional(), category: z.string().optional(), + subcategory: z.string().optional(), tags: z.array(z.string()).optional(), gpx: z.string().optional(), }) satisfies ZodType+ {$_("category-preferences-description")} +
+{disableIntroTextForPendingDisable()}
+ +{ownTrailMessageForPendingDisable()}
+{activePluginMessageForPendingDisable()}
+{inactivePluginMessageForPendingDisable()}
+{disableAnywayTextForPendingDisable()}
+ {/if} +