From eefd51cc4d2ff127006f2be843908ae82d1b413b Mon Sep 17 00:00:00 2001 From: "Alessandro (Ale) Segala" <43508+ItalyPaleAle@users.noreply.github.com> Date: Sun, 5 Jul 2026 14:14:39 -0700 Subject: [PATCH] fix: various bugs in observability / OTel (#1564) --- backend/go.mod | 21 +- backend/go.sum | 64 +- backend/internal/bootstrap/bootstrap.go | 89 +- backend/internal/bootstrap/db_bootstrap.go | 262 +--- .../internal/bootstrap/db_bootstrap_test.go | 349 ------ .../bootstrap/observability_boostrap.go | 78 +- .../internal/bootstrap/router_bootstrap.go | 25 +- backend/internal/common/env_config.go | 6 +- backend/internal/common/env_config_test.go | 2 - .../controller/app_config_controller.go | 8 + backend/internal/job/scheduler.go | 116 +- backend/internal/middleware/rate_limit.go | 12 +- .../internal/service/app_config_service.go | 19 +- backend/internal/service/app_lock_service.go | 64 +- backend/internal/service/audit_log_service.go | 8 +- .../service/one_time_access_service.go | 8 +- .../internal/tracing/telemetry_controller.go | 82 ++ backend/internal/tracing/tracing.go | 62 + .../utils/networked_filesystem_linux.go | 35 - .../utils/networked_filesystem_nonlinux.go | 8 - frontend/package.json | 5 + frontend/src/lib/services/api-service.ts | 33 +- .../types/application-configuration.type.ts | 1 + frontend/src/lib/utils/tracing-util.ts | 183 +++ frontend/src/routes/+layout.svelte | 5 + frontend/src/routes/+layout.ts | 5 + frontend/vite.config.ts | 3 + pnpm-lock.yaml | 1104 +++++++++-------- pnpm-workspace.yaml | 2 + 29 files changed, 1358 insertions(+), 1301 deletions(-) delete mode 100644 backend/internal/bootstrap/db_bootstrap_test.go create mode 100644 backend/internal/tracing/telemetry_controller.go create mode 100644 backend/internal/tracing/tracing.go delete mode 100644 backend/internal/utils/networked_filesystem_linux.go delete mode 100644 backend/internal/utils/networked_filesystem_nonlinux.go create mode 100644 frontend/src/lib/utils/tracing-util.ts diff --git a/backend/go.mod b/backend/go.mod index aabada2d..1d8267a2 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -27,6 +27,7 @@ require ( github.com/hashicorp/go-uuid v1.0.3 github.com/italypaleale/francis v0.1.0-beta.6 github.com/italypaleale/go-kit v0.0.0-20260705144331-cc6661f9a8cf + github.com/italypaleale/go-sql-utils v0.2.4 github.com/jackc/pgx/v5 v5.10.0 github.com/jinzhu/copier v0.4.0 github.com/joho/godotenv v1.5.1 @@ -48,7 +49,6 @@ require ( go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 go.opentelemetry.io/otel v1.44.0 go.opentelemetry.io/otel/log v0.20.0 - go.opentelemetry.io/otel/metric v1.44.0 go.opentelemetry.io/otel/sdk v1.44.0 go.opentelemetry.io/otel/sdk/log v0.20.0 go.opentelemetry.io/otel/sdk/metric v1.44.0 @@ -58,13 +58,18 @@ require ( golang.org/x/sync v0.21.0 golang.org/x/text v0.38.0 gorm.io/driver/postgres v1.6.0 - gorm.io/gorm v1.31.2 + gorm.io/gorm v1.31.1 + gorm.io/plugin/opentelemetry v0.1.16 modernc.org/sqlite v1.53.0 ) require ( + filippo.io/edwards25519 v1.2.0 // indirect github.com/Azure/go-ntlmssp v0.1.1 // indirect + github.com/ClickHouse/ch-go v0.61.5 // indirect + github.com/ClickHouse/clickhouse-go/v2 v2.30.0 // indirect github.com/alphadose/haxmap v1.4.1 // indirect + github.com/andybalholm/brotli v1.1.1 // indirect github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.13 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.29 // indirect @@ -107,10 +112,13 @@ require ( github.com/gin-contrib/sse v1.1.1 // indirect github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect github.com/go-errors/errors v1.5.1 // indirect + github.com/go-faster/city v1.0.1 // indirect + github.com/go-faster/errors v0.7.1 // indirect github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-sql-driver/mysql v1.9.3 // indirect github.com/go-viper/mapstructure/v2 v2.5.0 // indirect github.com/go-webauthn/x v0.2.6 // indirect github.com/go-xmlfmt/xmlfmt v1.1.3 // indirect @@ -128,8 +136,8 @@ require ( github.com/h2non/filetype v1.1.3 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect github.com/hashicorp/go-retryablehttp v0.7.8 // indirect + github.com/hashicorp/go-version v1.6.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/italypaleale/go-sql-utils v0.2.4-0.20260702021114-e1dfdfb67d0e // indirect github.com/jackc/pgerrcode v0.0.0-20250907135507-afb5586c32a6 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect @@ -139,6 +147,7 @@ require ( github.com/jinzhu/now v1.1.5 // indirect github.com/jonboulle/clockwork v0.5.0 // indirect github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/compress v1.18.5 // indirect github.com/klauspost/cpuid/v2 v2.3.0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lestrrat-go/blackmagic v1.0.4 // indirect @@ -160,8 +169,10 @@ require ( github.com/ory/go-convenience v0.1.0 // indirect github.com/ory/pop/v6 v6.4.1 // indirect github.com/ory/x v0.0.729 // indirect + github.com/paulmach/orb v0.11.1 // indirect github.com/pelletier/go-toml/v2 v2.3.1 // indirect github.com/philhofer/fwd v1.2.0 // indirect + github.com/pierrec/lz4/v4 v4.1.21 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.23.2 // indirect @@ -177,6 +188,7 @@ require ( github.com/sagikazarmark/locafero v0.12.0 // indirect github.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 // indirect github.com/segmentio/asm v1.2.1 // indirect + github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.4 // indirect github.com/spf13/afero v1.15.0 // indirect github.com/spf13/cast v1.10.0 // indirect @@ -210,6 +222,7 @@ require ( go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.44.0 // indirect go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.44.0 // indirect go.opentelemetry.io/otel/exporters/zipkin v1.44.0 // indirect + go.opentelemetry.io/otel/metric v1.44.0 // indirect go.opentelemetry.io/proto/otlp v1.10.0 // indirect go.uber.org/mock v0.6.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect @@ -227,6 +240,8 @@ require ( google.golang.org/protobuf v1.36.11 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + gorm.io/driver/clickhouse v0.7.0 // indirect + gorm.io/driver/mysql v1.5.7 // indirect k8s.io/utils v0.0.0-20260617174310-a95e086a2553 // indirect modernc.org/libc v1.73.5 // indirect modernc.org/mathutil v1.7.1 // indirect diff --git a/backend/go.sum b/backend/go.sum index d61bba9e..284e950a 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -1,13 +1,21 @@ +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ntlmssp v0.1.1 h1:l+FM/EEMb0U9QZE7mKNEDw5Mu3mFiaa2GKOoTSsNDPw= github.com/Azure/go-ntlmssp v0.1.1/go.mod h1:NYqdhxd/8aAct/s4qSYZEerdPuH1liG2/X9DiVTbhpk= +github.com/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeEI4= +github.com/ClickHouse/ch-go v0.61.5/go.mod h1:s1LJW/F/LcFs5HJnuogFMta50kKDO0lf9zzfrbl0RQg= +github.com/ClickHouse/clickhouse-go/v2 v2.30.0 h1:AG4D/hW39qa58+JHQIFOSnxyL46H6h2lrmGGk17dhFo= +github.com/ClickHouse/clickhouse-go/v2 v2.30.0/go.mod h1:i9ZQAojcayW3RsdCb3YR+n+wC2h65eJsZCscZ1Z1wyo= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e h1:4dAU9FXIyQktpoUAgOJK3OTFc/xug0PCXYCqU0FgDKI= github.com/alexbrainman/sspi v0.0.0-20250919150558-7d374ff0d59e/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= github.com/alphadose/haxmap v1.4.1 h1:VtD6VCxUkjNIfJk/aWdYFfOzrRddDFjmvmRmILg7x8Q= github.com/alphadose/haxmap v1.4.1/go.mod h1:rjHw1IAqbxm0S3U5tD16GoKsiAd8FWx5BJ2IYqXwgmM= +github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= +github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go-v2 v1.42.0 h1:XvXMJTkFQtpBKIWZnmr9ZEOc2InWM2yldjXEJ/bymhA= @@ -160,6 +168,10 @@ github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWE github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= github.com/go-errors/errors v1.5.1 h1:ZwEMSLRCapFLflTpT7NKaAc7ukJ8ZPEjzlxt8rPN8bk= github.com/go-errors/errors v1.5.1/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw= +github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw= +github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg= +github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo= github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-ldap/ldap/v3 v3.4.13 h1:+x1nG9h+MZN7h/lUi5Q3UZ0fJ1GyDQYbPvbuH38baDQ= @@ -177,6 +189,9 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.30.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8= github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= +github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-webauthn/webauthn v0.17.4 h1:KFTSz3R2RYDiUn/0cDi3XTJgFenSG74eKTTHlqWhlxk= @@ -204,9 +219,12 @@ github.com/golang/geo v0.0.0-20250319145452-ed1c8b99c3d7 h1:kG/6mhO8OwbQrA/0XEPw github.com/golang/geo v0.0.0-20250319145452-ed1c8b99c3d7/go.mod h1:J+F9/3Ofc8ysEOY2/cNjxTMl2eB1gvPIywEHUplPgDA= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= @@ -239,6 +257,8 @@ github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVU github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= @@ -247,8 +267,8 @@ github.com/italypaleale/francis v0.1.0-beta.6 h1:88XGnFMwQEMsGSJvI9vrGGvp//WKipX github.com/italypaleale/francis v0.1.0-beta.6/go.mod h1:3QQ1iSRGzvxJZ5gfVlVR1RyPDUAgzyN+m2vHeeR7OVs= github.com/italypaleale/go-kit v0.0.0-20260705144331-cc6661f9a8cf h1:8tk4010URtPgVqwZb7CDiNsuQI71qc8t7VSS4IaxoTY= github.com/italypaleale/go-kit v0.0.0-20260705144331-cc6661f9a8cf/go.mod h1:pl0r3F+thZIyDsyDo8aOUsAIVcsRuAeP1bB4GuAHLoY= -github.com/italypaleale/go-sql-utils v0.2.4-0.20260702021114-e1dfdfb67d0e h1:HDhmMkWAlLy/Elr/+T7diAZgAI+sDY2hYAyIAyefUYk= -github.com/italypaleale/go-sql-utils v0.2.4-0.20260702021114-e1dfdfb67d0e/go.mod h1:BJStxMfB6fzYVcOe0oZQCjGIPZQu76UBmg1Wuy6Z/7I= +github.com/italypaleale/go-sql-utils v0.2.4 h1:6CN8y3qEdNzvYlS/JK6N65E8cL9F8a6OBCJjzaQIv3c= +github.com/italypaleale/go-sql-utils v0.2.4/go.mod h1:BJStxMfB6fzYVcOe0oZQCjGIPZQu76UBmg1Wuy6Z/7I= github.com/jackc/pgerrcode v0.0.0-20250907135507-afb5586c32a6 h1:D/V0gu4zQ3cL2WKeVNVM4r2gLxGGf6McLwgXzRTo2RQ= github.com/jackc/pgerrcode v0.0.0-20250907135507-afb5586c32a6/go.mod h1:a/s9Lp5W7n/DD0VrVoyJ00FbP2ytTPDVOivvn2bMlds= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= @@ -288,6 +308,7 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= @@ -300,8 +321,11 @@ github.com/knadh/koanf/providers/rawbytes v0.1.0 h1:dpzgu2KO6uf6oCb4aP05KDmKmAmI github.com/knadh/koanf/providers/rawbytes v0.1.0/go.mod h1:mMTB1/IcJ/yE++A2iEZbY1MLygX7vttU+C+S/YmPu9c= github.com/knadh/koanf/v2 v2.3.2 h1:Ee6tuzQYFwcZXQpc2MiVeC6qHMandf5SMUJJNoFp/c4= github.com/knadh/koanf/v2 v2.3.2/go.mod h1:gRb40VRAbd4iJMYYD5IxZ6hfuopFcXBpc9bbQpZwo28= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= @@ -353,6 +377,7 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= @@ -387,10 +412,15 @@ github.com/ory/x v0.0.729 h1:7ttCYNCjCdspI6X0oaxGAXoiYWSBrwGRz6w/IG8s3I4= github.com/ory/x v0.0.729/go.mod h1:qdUK3Sp4K4nRbYJG0sEnFO1tDLN/Ct53G+ymre0JhCU= github.com/oschwald/maxminddb-golang/v2 v2.4.0 h1:3ftnrR1/XwiQ788bWIRhsE1DK3GOgJ6tm6S2qTktLm8= github.com/oschwald/maxminddb-golang/v2 v2.4.0/go.mod h1:7jcFtmhWVDEV+UopVv9NjcPm200uMyEHN14LIVV4hW8= +github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU= +github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU= +github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY= github.com/pelletier/go-toml/v2 v2.3.1 h1:MYEvvGnQjeNkRF1qUuGolNtNExTDwct51yp7olPtrEc= github.com/pelletier/go-toml/v2 v2.3.1/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/philhofer/fwd v1.2.0 h1:e6DnBTl7vGY+Gz322/ASL4Gyp1FspeMvx1RNDoToZuM= github.com/philhofer/fwd v1.2.0/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= +github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -429,6 +459,8 @@ github.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761 h1: github.com/seatgeek/logrus-gelf-formatter v0.0.0-20210414080842-5b05eb8ff761/go.mod h1:/THDZYi7F/BsVEcYzYPqdcWFQ+1C2InkawTKfLOAnzg= github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= @@ -449,6 +481,7 @@ github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/ github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4= github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= @@ -462,6 +495,7 @@ github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU= github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= @@ -482,11 +516,18 @@ github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAh github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zitadel/exifremove v0.1.0 h1:qD50ezWsfeeqfcvs79QyyjVfK+snN12v0U0deaU8aKg= github.com/zitadel/exifremove v0.1.0/go.mod h1:rzKJ3woL/Rz2KthVBiSBKIBptNTvgmk9PLaeUKTm+ek= +go.mongodb.org/mongo-driver v1.11.4/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= go.mongodb.org/mongo-driver/v2 v2.6.0 h1:b9sJOYrkmt4l8bY43ZenFBcPlhYIjaOfYHLtbB/5qi8= go.mongodb.org/mongo-driver/v2 v2.6.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= @@ -570,6 +611,7 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= @@ -602,6 +644,7 @@ golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= @@ -618,6 +661,7 @@ golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= @@ -631,6 +675,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/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.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -656,6 +701,7 @@ golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= 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.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= @@ -691,9 +737,12 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20260630182238-925bb5da69e7 h1: google.golang.org/genproto/googleapis/rpc v0.0.0-20260630182238-925bb5da69e7/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= google.golang.org/grpc v1.82.0 h1:vguDnZUPjE26w09A63VoxZPnvPjB5Riyc0mkXPFmAIU= google.golang.org/grpc v1.82.0/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -703,12 +752,19 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/clickhouse v0.7.0 h1:BCrqvgONayvZRgtuA6hdya+eAW5P2QVagV3OlEp1vtA= +gorm.io/driver/clickhouse v0.7.0/go.mod h1:TmNo0wcVTsD4BBObiRnCahUgHJHjBIwuRejHwYt3JRs= +gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= +gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= -gorm.io/gorm v1.31.2 h1:3o8FXNo9v9S858gil+3LlZA1LkCOzgb4g5BL64FgaCo= -gorm.io/gorm v1.31.2/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= +gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= +gorm.io/plugin/opentelemetry v0.1.16 h1:Kypj2YYAliJqkIczDZDde6P6sFMhKSlG5IpngMFQGpc= +gorm.io/plugin/opentelemetry v0.1.16/go.mod h1:P3RmTeZXT+9n0F1ccUqR5uuTvEXDxF8k2UpO7mTIB2Y= k8s.io/utils v0.0.0-20260617174310-a95e086a2553 h1:hmGqDecjc8d7HVzWzRFl0QD9bYuYKbBEG7t8xwnVxfI= k8s.io/utils v0.0.0-20260617174310-a95e086a2553/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= modernc.org/cc/v4 v4.29.0 h1:CXgwL8cvxmyzBQZzbSl/6xFtMCryb6u8IOqDci39cgc= diff --git a/backend/internal/bootstrap/bootstrap.go b/backend/internal/bootstrap/bootstrap.go index 461e8848..b781e705 100644 --- a/backend/internal/bootstrap/bootstrap.go +++ b/backend/internal/bootstrap/bootstrap.go @@ -19,29 +19,18 @@ import ( ) func Bootstrap(ctx context.Context) error { - var ( - shutdownFns []servicerunner.Service - closeDatabasePool func() - ) - defer func() { //nolint:contextcheck - // Invoke all shutdown functions on exit - shutdownCtx, cancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second) - defer cancel() - shutdownErr := servicerunner.NewServiceRunner(shutdownFns...).Run(shutdownCtx) - if shutdownErr != nil { - slog.Error("Error during graceful shutdown", "error", shutdownErr) - } - // Close the database connection pool only after the shutdown functions have run: some of them (e.g. releasing the application lock) still need to query the database. - if closeDatabasePool != nil { - closeDatabasePool() - } - }() + // List of services to run + services := make([]servicerunner.Service, 0, 3) + shutdowns := &shutdownManager{ + fns: make([]servicerunner.Service, 0, 4), + } // Initialize the observability stack, including the logger, distributed tracing, and metrics - shutdownFns, httpClient, err := initObservability(ctx, common.EnvConfig.MetricsEnabled, common.EnvConfig.TracingEnabled) + shutdownFns, httpClient, err := initObservability(ctx) if err != nil { return fmt.Errorf("failed to initialize OpenTelemetry: %w", err) } + shutdowns.Add(shutdownFns...) slog.InfoContext(ctx, "Pocket ID is starting") @@ -51,7 +40,10 @@ func Bootstrap(ctx context.Context) error { return fmt.Errorf("failed to initialize database: %w", err) } if pg != nil { - closeDatabasePool = pg.Close + defer func() { + // Close the database connection pool only after the shutdown functions have run: some of them (e.g. releasing the application lock) still need to query the database. + pg.Close() + }() } // Init storage @@ -77,6 +69,7 @@ func Bootstrap(ctx context.Context) error { if err != nil { return fmt.Errorf("failed to initialize services: %w", err) } + services = append(services, svc.appLockService.RunRenewal) // Acquire the lock from the app lock service waitUntil, err := svc.appLockService.Acquire(ctx, false) @@ -92,14 +85,13 @@ func Bootstrap(ctx context.Context) error { case <-time.After(time.Until(waitUntil)): } - shutdownFn := func(shutdownCtx context.Context) error { + shutdowns.Add(func(shutdownCtx context.Context) error { sErr := svc.appLockService.Release(shutdownCtx) if sErr != nil { return fmt.Errorf("failed to release application lock: %w", sErr) } return nil - } - shutdownFns = append(shutdownFns, shutdownFn) + }) // Init the actors actorsOpts := NewActorsOpts{ @@ -121,11 +113,15 @@ func Bootstrap(ctx context.Context) error { if err != nil { return fmt.Errorf("failed to initialize actors: %w", err) } + services = append(services, actors.Run) - // Register scheduled jobs - err = registerScheduledJobs(ctx, db, svc, scheduler) - if err != nil { - return fmt.Errorf("failed to register scheduled jobs: %w", err) + // Register scheduled jobs, only in non-test mode + if common.EnvConfig.AppEnv != "test" { + err = registerScheduledJobs(ctx, db, svc, scheduler) + if err != nil { + return fmt.Errorf("failed to register scheduled jobs: %w", err) + } + services = append(services, scheduler.Run) } // Init the router @@ -135,24 +131,18 @@ func Bootstrap(ctx context.Context) error { if err != nil { return fmt.Errorf("failed to initialize router: %w", err) } + services = append(services, router) // Run all background services // This call blocks until the context is canceled - services := []servicerunner.Service{ - svc.appLockService.RunRenewal, - actors.Run, - router, - } - - if common.EnvConfig.AppEnv != "test" { - services = append(services, scheduler.Run) - } - err = servicerunner.NewServiceRunner(services...).Run(ctx) if err != nil { return fmt.Errorf("failed to run services: %w", err) } + // Run all shutdown functions + shutdowns.Run(ctx) + return nil } @@ -183,3 +173,30 @@ func InitStorage(ctx context.Context, db *gorm.DB) (fileStorage storage.FileStor return fileStorage, nil } + +type shutdownManager struct { + fns []servicerunner.Service +} + +func (s *shutdownManager) Add(fns ...servicerunner.Service) { + for _, fn := range fns { + if fn == nil { + continue + } + + s.fns = append(s.fns, fn) + } +} + +func (s *shutdownManager) Run(ctx context.Context) { + // Cleanup functions are one-shot and must each run to completion independently, so we set WaitAll to true + sr := servicerunner.NewServiceRunner(s.fns...) + sr.WaitAll = true + + shutdownCtx, shutdownCancel := context.WithTimeout(context.WithoutCancel(ctx), 5*time.Second) + defer shutdownCancel() + err := sr.Run(shutdownCtx) + if err != nil { + slog.ErrorContext(ctx, "Error shutting down services", slog.Any("error", err)) + } +} diff --git a/backend/internal/bootstrap/db_bootstrap.go b/backend/internal/bootstrap/db_bootstrap.go index 85e5ce5e..4d998999 100644 --- a/backend/internal/bootstrap/db_bootstrap.go +++ b/backend/internal/bootstrap/db_bootstrap.go @@ -6,13 +6,11 @@ import ( "errors" "fmt" "log/slog" - "net/url" - "os" "path/filepath" - "strings" "time" _ "github.com/golang-migrate/migrate/v4/source/github" + sqlitekit "github.com/italypaleale/go-sql-utils/sqlite" "github.com/jackc/pgx/v5/pgxpool" "github.com/jackc/pgx/v5/stdlib" "github.com/libtnb/sqlite" @@ -20,6 +18,7 @@ import ( "gorm.io/driver/postgres" "gorm.io/gorm" gormLogger "gorm.io/gorm/logger" + tracingGorm "gorm.io/plugin/opentelemetry/tracing" "github.com/pocket-id/pocket-id/backend/internal/common" "github.com/pocket-id/pocket-id/backend/internal/utils" @@ -59,19 +58,19 @@ func ConnectDatabase(ctx context.Context) (db *gorm.DB, pg *pgxpool.Pool, err er sqliteutil.RegisterSqliteFunctions() - connString, dbPath, isMemoryDB, err := parseSqliteConnectionString(common.EnvConfig.DbConnectionString) + connString, dbPath, isMemoryDB, err := sqlitekit.ParseConnectionString(common.EnvConfig.DbConnectionString, slog.Default()) if err != nil { return nil, nil, err } if !isMemoryDB { - err = ensureSqliteDatabaseDir(dbPath) + err = sqlitekit.EnsureDatabaseDir(dbPath) if err != nil { return nil, nil, err } var sqliteNetworkFilesystem bool - sqliteNetworkFilesystem, err = utils.IsNetworkedFileSystem(filepath.Dir(dbPath)) + sqliteNetworkFilesystem, err = sqlitekit.IsNetworkedFileSystem(filepath.Dir(dbPath)) if err != nil { // Log the error only slog.Warn("Failed to detect filesystem type for the SQLite database directory", slog.String("path", filepath.Dir(dbPath)), slog.Any("error", err)) @@ -81,7 +80,7 @@ func ConnectDatabase(ctx context.Context) (db *gorm.DB, pg *pgxpool.Pool, err er } // Before we connect, also make sure that there's a temporary folder for SQLite to write its data - err = ensureSqliteTempDir(filepath.Dir(dbPath)) + err = sqlitekit.EnsureTempDir(filepath.Dir(dbPath), slog.Default()) if err != nil { return nil, nil, err } @@ -132,6 +131,12 @@ func ConnectDatabase(ctx context.Context) (db *gorm.DB, pg *pgxpool.Pool, err er if err == nil { slog.Info("Connected to database", slog.String("provider", string(common.EnvConfig.DbProvider))) + // Configure tracing and metrics + err = db.Use(tracingGorm.NewPlugin()) + if err != nil { + return nil, nil, fmt.Errorf("failed to configure tracing for DB: %w", err) + } + // Invoke the onConnFn callback if any if onConnFn != nil { conn, err := db.DB() @@ -162,249 +167,6 @@ func ConnectDatabase(ctx context.Context) (db *gorm.DB, pg *pgxpool.Pool, err er return nil, nil, err } -func parseSqliteConnectionString(connString string) (parsedConnString string, dbPath string, isMemoryDB bool, err error) { - if !strings.HasPrefix(connString, "file:") { - connString = "file:" + connString - } - - // Check if we're using an in-memory database - isMemoryDB = isSqliteInMemory(connString) - - // Parse the connection string - connStringUrl, err := url.Parse(connString) - if err != nil { - return "", "", false, fmt.Errorf("failed to parse SQLite connection string: %w", err) - } - - // Convert options for the old SQLite driver to the new one - convertSqlitePragmaArgs(connStringUrl) - - // Add the default and required params - err = addSqliteDefaultParameters(connStringUrl, isMemoryDB) - if err != nil { - return "", "", false, fmt.Errorf("invalid SQLite connection string: %w", err) - } - - // Get the absolute path to the database - // Here, we know for a fact that the ? is present - parsedConnString = connStringUrl.String() - idx := strings.IndexRune(parsedConnString, '?') - dbPath, err = filepath.Abs(parsedConnString[len("file:"):idx]) - if err != nil { - return "", "", false, fmt.Errorf("failed to determine absolute path to the database: %w", err) - } - - return parsedConnString, dbPath, isMemoryDB, nil -} - -// The official C implementation of SQLite allows some additional properties in the connection string -// that are not supported in the in the modernc.org/sqlite driver, and which must be passed as PRAGMA args instead. -// To ensure that people can use similar args as in the C driver, which was also used by Pocket ID -// previously (via github.com/mattn/go-sqlite3), we are converting some options. -// Note this function updates connStringUrl. -func convertSqlitePragmaArgs(connStringUrl *url.URL) { - // Reference: https://github.com/mattn/go-sqlite3?tab=readme-ov-file#connection-string - // This only includes a subset of options, excluding those that are not relevant to us - qs := make(url.Values, len(connStringUrl.Query())) - for k, v := range connStringUrl.Query() { - switch strings.ToLower(k) { - case "_auto_vacuum", "_vacuum": - qs.Add("_pragma", "auto_vacuum("+v[0]+")") - case "_busy_timeout", "_timeout": - qs.Add("_pragma", "busy_timeout("+v[0]+")") - case "_case_sensitive_like", "_cslike": - qs.Add("_pragma", "case_sensitive_like("+v[0]+")") - case "_foreign_keys", "_fk": - qs.Add("_pragma", "foreign_keys("+v[0]+")") - case "_locking_mode", "_locking": - qs.Add("_pragma", "locking_mode("+v[0]+")") - case "_secure_delete": - qs.Add("_pragma", "secure_delete("+v[0]+")") - case "_synchronous", "_sync": - qs.Add("_pragma", "synchronous("+v[0]+")") - default: - // Pass other query-string args as-is - qs[k] = v - } - } - - // Update the connStringUrl object - connStringUrl.RawQuery = qs.Encode() -} - -// Adds the default (and some required) parameters to the SQLite connection string. -// Note this function updates connStringUrl. -func addSqliteDefaultParameters(connStringUrl *url.URL, isMemoryDB bool) error { - // This function include code adapted from https://github.com/dapr/components-contrib/blob/v1.14.6/ - // Copyright (C) 2023 The Dapr Authors - // License: Apache2 - const defaultBusyTimeout = 2500 * time.Millisecond - - // Get the "query string" from the connection string if present - qs := connStringUrl.Query() - if len(qs) == 0 { - qs = make(url.Values, 2) - } - - // Check if the database is read-only or immutable - isReadOnly := false - if len(qs["mode"]) > 0 { - // Keep the first value only - qs["mode"] = []string{ - strings.ToLower(qs["mode"][0]), - } - if qs["mode"][0] == "ro" { - isReadOnly = true - } - } - if len(qs["immutable"]) > 0 { - // Keep the first value only - qs["immutable"] = []string{ - strings.ToLower(qs["immutable"][0]), - } - if qs["immutable"][0] == "1" { - isReadOnly = true - } - } - - // We do not want to override a _txlock if set, but we'll show a warning if it's not "immediate" - if len(qs["_txlock"]) > 0 { - // Keep the first value only - qs["_txlock"] = []string{ - strings.ToLower(qs["_txlock"][0]), - } - if qs["_txlock"][0] != "immediate" { - slog.Warn("SQLite connection is being created with a _txlock different from the recommended value 'immediate'") - } - } else { - qs["_txlock"] = []string{"immediate"} - } - - // Add pragma values - var hasBusyTimeout, hasJournalMode bool - if len(qs["_pragma"]) == 0 { - qs["_pragma"] = make([]string, 0, 3) - } else { - for _, p := range qs["_pragma"] { - p = strings.ToLower(p) - switch { - case strings.HasPrefix(p, "busy_timeout"): - hasBusyTimeout = true - case strings.HasPrefix(p, "journal_mode"): - hasJournalMode = true - case strings.HasPrefix(p, "foreign_keys"): - return errors.New("found forbidden option '_pragma=foreign_keys' in the connection string") - } - } - } - if !hasBusyTimeout { - qs["_pragma"] = append(qs["_pragma"], fmt.Sprintf("busy_timeout(%d)", defaultBusyTimeout.Milliseconds())) - } - if !hasJournalMode { - switch { - case isMemoryDB: - // For in-memory databases, set the journal to MEMORY, the only allowed option besides OFF (which would make transactions ineffective) - qs["_pragma"] = append(qs["_pragma"], "journal_mode(MEMORY)") - case isReadOnly: - // Set the journaling mode to "DELETE" (the default) if the database is read-only - qs["_pragma"] = append(qs["_pragma"], "journal_mode(DELETE)") - default: - // Enable WAL - qs["_pragma"] = append(qs["_pragma"], "journal_mode(WAL)") - } - } - - // Forcefully enable foreign keys - qs["_pragma"] = append(qs["_pragma"], "foreign_keys(1)") - - // Update the connStringUrl object - connStringUrl.RawQuery = qs.Encode() - - return nil -} - -// isSqliteInMemory returns true if the connection string is for an in-memory database. -func isSqliteInMemory(connString string) bool { - lc := strings.ToLower(connString) - - // First way to define an in-memory database is to use ":memory:" or "file::memory:" as connection string - if strings.HasPrefix(lc, ":memory:") || strings.HasPrefix(lc, "file::memory:") { - return true - } - - // Another way is to pass "mode=memory" in the "query string" - idx := strings.IndexRune(lc, '?') - if idx < 0 { - return false - } - qs, _ := url.ParseQuery(lc[(idx + 1):]) - - return len(qs["mode"]) > 0 && qs["mode"][0] == "memory" -} - -// ensureSqliteDatabaseDir creates the parent directory for the SQLite database file if it doesn't exist yet -func ensureSqliteDatabaseDir(dbPath string) error { - dir := filepath.Dir(dbPath) - - info, err := os.Stat(dir) - switch { - case err == nil: - if !info.IsDir() { - return fmt.Errorf("SQLite database directory '%s' is not a directory", dir) - } - return nil - case os.IsNotExist(err): - if err := os.MkdirAll(dir, 0700); err != nil { - return fmt.Errorf("failed to create SQLite database directory '%s': %w", dir, err) - } - return nil - default: - return fmt.Errorf("failed to check SQLite database directory '%s': %w", dir, err) - } -} - -// ensureSqliteTempDir ensures that SQLite has a directory where it can write temporary files if needed -// The default directory may not be writable when using a container with a read-only root file system -// See: https://www.sqlite.org/tempfiles.html -func ensureSqliteTempDir(dbPath string) error { - // Per docs, SQLite tries these folders in order (excluding those that aren't applicable to us): - // - // - The SQLITE_TMPDIR environment variable - // - The TMPDIR environment variable - // - /var/tmp - // - /usr/tmp - // - /tmp - // - // Source: https://www.sqlite.org/tempfiles.html#temporary_file_storage_locations - // - // First, let's check if SQLITE_TMPDIR or TMPDIR are set, in which case we trust the user has taken care of the problem already - if os.Getenv("SQLITE_TMPDIR") != "" || os.Getenv("TMPDIR") != "" { - return nil - } - - // Now, let's check if /var/tmp, /usr/tmp, or /tmp exist and are writable - for _, dir := range []string{"/var/tmp", "/usr/tmp", "/tmp"} { - ok, err := utils.IsWritableDir(dir) - if err != nil { - return fmt.Errorf("failed to check if %s is writable: %w", dir, err) - } - if ok { - // We found a folder that's writable - return nil - } - } - - // If we're here, there's no temporary directory that's writable (not unusual for containers with a read-only root file system), so we set SQLITE_TMPDIR to the folder where the SQLite database is set - err := os.Setenv("SQLITE_TMPDIR", dbPath) - if err != nil { - return fmt.Errorf("failed to set SQLITE_TMPDIR environmental variable: %w", err) - } - - slog.Debug("Set SQLITE_TMPDIR to the database directory", "path", dbPath) - - return nil -} - func getGormLogger() gormLogger.Interface { loggerOpts := make([]slogGorm.Option, 0, 5) loggerOpts = append(loggerOpts, diff --git a/backend/internal/bootstrap/db_bootstrap_test.go b/backend/internal/bootstrap/db_bootstrap_test.go deleted file mode 100644 index 81fc0e3f..00000000 --- a/backend/internal/bootstrap/db_bootstrap_test.go +++ /dev/null @@ -1,349 +0,0 @@ -package bootstrap - -import ( - "net/url" - "os" - "path/filepath" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestIsSqliteInMemory(t *testing.T) { - tests := []struct { - name string - connStr string - expected bool - }{ - { - name: "memory database with :memory:", - connStr: ":memory:", - expected: true, - }, - { - name: "memory database with file::memory:", - connStr: "file::memory:", - expected: true, - }, - { - name: "memory database with :MEMORY: (uppercase)", - connStr: ":MEMORY:", - expected: true, - }, - { - name: "memory database with FILE::MEMORY: (uppercase)", - connStr: "FILE::MEMORY:", - expected: true, - }, - { - name: "memory database with mixed case", - connStr: ":Memory:", - expected: true, - }, - { - name: "has mode=memory", - connStr: "file:data?mode=memory", - expected: true, - }, - { - name: "file database", - connStr: "data.db", - expected: false, - }, - { - name: "file database with path", - connStr: "/path/to/data.db", - expected: false, - }, - { - name: "file database with file: prefix", - connStr: "file:data.db", - expected: false, - }, - { - name: "empty string", - connStr: "", - expected: false, - }, - { - name: "string containing memory but not at start", - connStr: "data:memory:.db", - expected: false, - }, - { - name: "has mode=ro", - connStr: "file:data?mode=ro", - expected: false, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - result := isSqliteInMemory(tt.connStr) - assert.Equal(t, tt.expected, result) - }) - } -} - -func TestEnsureSqliteDatabaseDir(t *testing.T) { - t.Run("creates missing directory", func(t *testing.T) { - tempDir := t.TempDir() - dbPath := filepath.Join(tempDir, "nested", "pocket-id.db") - - err := ensureSqliteDatabaseDir(dbPath) - require.NoError(t, err) - - info, err := os.Stat(filepath.Dir(dbPath)) - require.NoError(t, err) - assert.True(t, info.IsDir()) - }) - - t.Run("fails when parent is file", func(t *testing.T) { - tempDir := t.TempDir() - filePath := filepath.Join(tempDir, "file.txt") - require.NoError(t, os.WriteFile(filePath, []byte("test"), 0o600)) - - err := ensureSqliteDatabaseDir(filepath.Join(filePath, "data.db")) - require.Error(t, err) - }) -} - -func TestConvertSqlitePragmaArgs(t *testing.T) { - tests := []struct { - name string - input string - expected string - }{ - { - name: "basic file path", - input: "file:test.db", - expected: "file:test.db", - }, - { - name: "converts _busy_timeout to pragma", - input: "file:test.db?_busy_timeout=5000", - expected: "file:test.db?_pragma=busy_timeout%285000%29", - }, - { - name: "converts _timeout to pragma", - input: "file:test.db?_timeout=5000", - expected: "file:test.db?_pragma=busy_timeout%285000%29", - }, - { - name: "converts _foreign_keys to pragma", - input: "file:test.db?_foreign_keys=1", - expected: "file:test.db?_pragma=foreign_keys%281%29", - }, - { - name: "converts _fk to pragma", - input: "file:test.db?_fk=1", - expected: "file:test.db?_pragma=foreign_keys%281%29", - }, - { - name: "converts _synchronous to pragma", - input: "file:test.db?_synchronous=NORMAL", - expected: "file:test.db?_pragma=synchronous%28NORMAL%29", - }, - { - name: "converts _sync to pragma", - input: "file:test.db?_sync=NORMAL", - expected: "file:test.db?_pragma=synchronous%28NORMAL%29", - }, - { - name: "converts _auto_vacuum to pragma", - input: "file:test.db?_auto_vacuum=FULL", - expected: "file:test.db?_pragma=auto_vacuum%28FULL%29", - }, - { - name: "converts _vacuum to pragma", - input: "file:test.db?_vacuum=FULL", - expected: "file:test.db?_pragma=auto_vacuum%28FULL%29", - }, - { - name: "converts _case_sensitive_like to pragma", - input: "file:test.db?_case_sensitive_like=1", - expected: "file:test.db?_pragma=case_sensitive_like%281%29", - }, - { - name: "converts _cslike to pragma", - input: "file:test.db?_cslike=1", - expected: "file:test.db?_pragma=case_sensitive_like%281%29", - }, - { - name: "converts _locking_mode to pragma", - input: "file:test.db?_locking_mode=EXCLUSIVE", - expected: "file:test.db?_pragma=locking_mode%28EXCLUSIVE%29", - }, - { - name: "converts _locking to pragma", - input: "file:test.db?_locking=EXCLUSIVE", - expected: "file:test.db?_pragma=locking_mode%28EXCLUSIVE%29", - }, - { - name: "converts _secure_delete to pragma", - input: "file:test.db?_secure_delete=1", - expected: "file:test.db?_pragma=secure_delete%281%29", - }, - { - name: "preserves unrecognized parameters", - input: "file:test.db?mode=rw&cache=shared", - expected: "file:test.db?cache=shared&mode=rw", - }, - { - name: "handles multiple parameters", - input: "file:test.db?_fk=1&mode=rw&_timeout=5000", - expected: "file:test.db?_pragma=foreign_keys%281%29&_pragma=busy_timeout%285000%29&mode=rw", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - resultURL, _ := url.Parse(tt.input) - convertSqlitePragmaArgs(resultURL) - - // Parse both URLs to compare components independently - expectedURL, err := url.Parse(tt.expected) - require.NoError(t, err) - - // Compare scheme and path components - compareQueryStrings(t, expectedURL, resultURL) - }) - } -} - -func TestAddSqliteDefaultParameters(t *testing.T) { - tests := []struct { - name string - input string - isMemoryDB bool - expected string - expectError bool - }{ - { - name: "basic file database", - input: "file:test.db", - isMemoryDB: false, - expected: "file:test.db?_pragma=busy_timeout%282500%29&_pragma=foreign_keys%281%29&_pragma=journal_mode%28WAL%29&_txlock=immediate", - }, - { - name: "in-memory database", - input: "file::memory:", - isMemoryDB: true, - expected: "file::memory:?_pragma=busy_timeout%282500%29&_pragma=foreign_keys%281%29&_pragma=journal_mode%28MEMORY%29&_txlock=immediate", - }, - { - name: "read-only database with mode=ro", - input: "file:test.db?mode=ro", - isMemoryDB: false, - expected: "file:test.db?_pragma=busy_timeout%282500%29&_pragma=foreign_keys%281%29&_pragma=journal_mode%28DELETE%29&_txlock=immediate&mode=ro", - }, - { - name: "immutable database", - input: "file:test.db?immutable=1", - isMemoryDB: false, - expected: "file:test.db?_pragma=busy_timeout%282500%29&_pragma=foreign_keys%281%29&_pragma=journal_mode%28DELETE%29&_txlock=immediate&immutable=1", - }, - { - name: "database with existing _txlock", - input: "file:test.db?_txlock=deferred", - isMemoryDB: false, - expected: "file:test.db?_pragma=busy_timeout%282500%29&_pragma=foreign_keys%281%29&_pragma=journal_mode%28WAL%29&_txlock=deferred", - }, - { - name: "database with existing busy_timeout pragma", - input: "file:test.db?_pragma=busy_timeout%285000%29", - isMemoryDB: false, - expected: "file:test.db?_pragma=busy_timeout%285000%29&_pragma=foreign_keys%281%29&_pragma=journal_mode%28WAL%29&_txlock=immediate", - }, - { - name: "database with existing journal_mode pragma", - input: "file:test.db?_pragma=journal_mode%28DELETE%29", - isMemoryDB: false, - expected: "file:test.db?_pragma=busy_timeout%282500%29&_pragma=foreign_keys%281%29&_pragma=journal_mode%28DELETE%29&_txlock=immediate", - }, - { - name: "database with forbidden foreign_keys pragma", - input: "file:test.db?_pragma=foreign_keys%280%29", - isMemoryDB: false, - expectError: true, - }, - { - name: "database with multiple existing pragmas", - input: "file:test.db?_pragma=busy_timeout%283000%29&_pragma=journal_mode%28TRUNCATE%29&_pragma=synchronous%28NORMAL%29", - isMemoryDB: false, - expected: "file:test.db?_pragma=busy_timeout%283000%29&_pragma=foreign_keys%281%29&_pragma=journal_mode%28TRUNCATE%29&_pragma=synchronous%28NORMAL%29&_txlock=immediate", - }, - { - name: "database with mode=rw (not read-only)", - input: "file:test.db?mode=rw", - isMemoryDB: false, - expected: "file:test.db?_pragma=busy_timeout%282500%29&_pragma=foreign_keys%281%29&_pragma=journal_mode%28WAL%29&_txlock=immediate&mode=rw", - }, - { - name: "database with immutable=0 (not immutable)", - input: "file:test.db?immutable=0", - isMemoryDB: false, - expected: "file:test.db?_pragma=busy_timeout%282500%29&_pragma=foreign_keys%281%29&_pragma=journal_mode%28WAL%29&_txlock=immediate&immutable=0", - }, - { - name: "database with mixed case mode=RO", - input: "file:test.db?mode=RO", - isMemoryDB: false, - expected: "file:test.db?_pragma=busy_timeout%282500%29&_pragma=foreign_keys%281%29&_pragma=journal_mode%28DELETE%29&_txlock=immediate&mode=ro", - }, - { - name: "database with mixed case immutable=1", - input: "file:test.db?immutable=1", - isMemoryDB: false, - expected: "file:test.db?_pragma=busy_timeout%282500%29&_pragma=foreign_keys%281%29&_pragma=journal_mode%28DELETE%29&_txlock=immediate&immutable=1", - }, - { - name: "complex database configuration", - input: "file:test.db?cache=shared&mode=rwc&_txlock=immediate&_pragma=synchronous%28FULL%29", - isMemoryDB: false, - expected: "file:test.db?_pragma=busy_timeout%282500%29&_pragma=foreign_keys%281%29&_pragma=journal_mode%28WAL%29&_pragma=synchronous%28FULL%29&_txlock=immediate&cache=shared&mode=rwc", - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - resultURL, err := url.Parse(tt.input) - require.NoError(t, err) - - err = addSqliteDefaultParameters(resultURL, tt.isMemoryDB) - - if tt.expectError { - require.Error(t, err) - return - } - - require.NoError(t, err) - - expectedURL, err := url.Parse(tt.expected) - require.NoError(t, err) - - compareQueryStrings(t, expectedURL, resultURL) - }) - } -} - -func compareQueryStrings(t *testing.T, expectedURL *url.URL, resultURL *url.URL) { - t.Helper() - - // Compare scheme and path components - assert.Equal(t, expectedURL.Scheme, resultURL.Scheme) - assert.Equal(t, expectedURL.Path, resultURL.Path) - - // Compare query parameters regardless of order - expectedQuery := expectedURL.Query() - resultQuery := resultURL.Query() - - assert.Len(t, expectedQuery, len(resultQuery)) - - for key, expectedValues := range expectedQuery { - resultValues, ok := resultQuery[key] - _ = assert.True(t, ok) && - assert.ElementsMatch(t, expectedValues, resultValues) - } -} diff --git a/backend/internal/bootstrap/observability_boostrap.go b/backend/internal/bootstrap/observability_boostrap.go index 8331928b..6cb6874a 100644 --- a/backend/internal/bootstrap/observability_boostrap.go +++ b/backend/internal/bootstrap/observability_boostrap.go @@ -17,14 +17,12 @@ import ( "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" "go.opentelemetry.io/otel" globallog "go.opentelemetry.io/otel/log/global" - metricnoop "go.opentelemetry.io/otel/metric/noop" "go.opentelemetry.io/otel/propagation" sdklog "go.opentelemetry.io/otel/sdk/log" "go.opentelemetry.io/otel/sdk/metric" "go.opentelemetry.io/otel/sdk/resource" sdktrace "go.opentelemetry.io/otel/sdk/trace" semconv "go.opentelemetry.io/otel/semconv/v1.30.0" - tracenoop "go.opentelemetry.io/otel/trace/noop" "github.com/pocket-id/pocket-id/backend/internal/common" ) @@ -39,13 +37,13 @@ func defaultResource() (*resource.Resource, error) { ) } -func initObservability(ctx context.Context, metrics, traces bool) (shutdownFns []servicerunner.Service, httpClient *http.Client, err error) { +func initObservability(ctx context.Context) (shutdownFns []servicerunner.Service, httpClient *http.Client, err error) { resource, err := defaultResource() if err != nil { return nil, nil, fmt.Errorf("failed to create OpenTelemetry resource: %w", err) } - shutdownFns = make([]servicerunner.Service, 0, 2) + shutdownFns = make([]servicerunner.Service, 0, 3) httpClient = &http.Client{} defaultTransport, ok := http.DefaultTransport.(*http.Transport) @@ -56,13 +54,15 @@ func initObservability(ctx context.Context, metrics, traces bool) (shutdownFns [ httpClient.Transport = defaultTransport.Clone() // Logging - err = initOtelLogging(ctx, resource) + loggingShutdownFn, err := initOtelLogging(ctx, resource) if err != nil { return nil, nil, err + } else if loggingShutdownFn != nil { + shutdownFns = append(shutdownFns, loggingShutdownFn) } // Tracing - tracingShutdownFn, err := initOtelTracing(ctx, traces, resource, httpClient) + tracingShutdownFn, err := initOtelTracing(ctx, resource, httpClient) if err != nil { return nil, nil, err } else if tracingShutdownFn != nil { @@ -70,7 +70,7 @@ func initObservability(ctx context.Context, metrics, traces bool) (shutdownFns [ } // Metrics - metricsShutdownFn, err := initOtelMetrics(ctx, metrics, resource) + metricsShutdownFn, err := initOtelMetrics(ctx, resource) if err != nil { return nil, nil, err } else if metricsShutdownFn != nil { @@ -80,19 +80,19 @@ func initObservability(ctx context.Context, metrics, traces bool) (shutdownFns [ return shutdownFns, httpClient, nil } -func initOtelLogging(ctx context.Context, resource *resource.Resource) error { +func initOtelLogging(ctx context.Context, resource *resource.Resource) (shutdownFn servicerunner.Service, err error) { // If the env var OTEL_LOGS_EXPORTER is empty, we set it to "none", for autoexport to work if os.Getenv("OTEL_LOGS_EXPORTER") == "" { os.Setenv("OTEL_LOGS_EXPORTER", "none") } exp, err := autoexport.NewLogExporter(ctx) if err != nil { - return fmt.Errorf("failed to initialize OpenTelemetry log exporter: %w", err) + return nil, fmt.Errorf("failed to initialize OpenTelemetry log exporter: %w", err) } level, _ := sloggin.ParseLevel(common.EnvConfig.LogLevel) - // Create the handler + // Create the console handler var handler slog.Handler if common.EnvConfig.LogJSON { handler = slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ @@ -106,21 +106,35 @@ func initOtelLogging(ctx context.Context, resource *resource.Resource) error { }) } - // Create the logger provider - provider := sdklog.NewLoggerProvider( - sdklog.WithProcessor( - sdklog.NewBatchProcessor(exp), - ), - sdklog.WithResource(resource), - ) + // When log export is enabled, also send logs to OpenTelemetry through a batch processor. + // When it's disabled (the exporter is a no-op), we skip the OTel handler entirely so we don't convert and buffer every log record just to drop it. + if !autoexport.IsNoneLogExporter(exp) { + // Create the logger provider + provider := sdklog.NewLoggerProvider( + sdklog.WithProcessor( + sdklog.NewBatchProcessor(exp), + ), + sdklog.WithResource(resource), + ) - // Set the logger provider globally - globallog.SetLoggerProvider(provider) + // Set the logger provider globally + globallog.SetLoggerProvider(provider) - handler = slog.NewMultiHandler( - handler, - otelslog.NewHandler(common.Name, otelslog.WithLoggerProvider(provider)), - ) + handler = slog.NewMultiHandler( + handler, + otelslog.NewHandler(common.Name, otelslog.WithLoggerProvider(provider)), + ) + + shutdownFn = func(shutdownCtx context.Context) error { //nolint:contextcheck + lpCtx, lpCancel := context.WithTimeout(shutdownCtx, 10*time.Second) + defer lpCancel() + shutdownErr := provider.Shutdown(lpCtx) + if shutdownErr != nil { + return fmt.Errorf("failed to gracefully shut down logs exporter: %w", shutdownErr) + } + return nil + } + } // Set the default slog to send logs to OTel and add the app name log := slog.New(handler). @@ -128,13 +142,13 @@ func initOtelLogging(ctx context.Context, resource *resource.Resource) error { With(slog.String("version", common.Version)) slog.SetDefault(log) - return nil + return shutdownFn, nil } -func initOtelTracing(ctx context.Context, traces bool, resource *resource.Resource, httpClient *http.Client) (shutdownFn servicerunner.Service, err error) { - if !traces { - otel.SetTracerProvider(tracenoop.NewTracerProvider()) - return nil, nil +func initOtelTracing(ctx context.Context, resource *resource.Resource, httpClient *http.Client) (shutdownFn servicerunner.Service, err error) { + // If the env var OTEL_TRACES_EXPORTER is empty, we set it to "none" + if os.Getenv("OTEL_TRACES_EXPORTER") == "" { + os.Setenv("OTEL_TRACES_EXPORTER", "none") } tr, err := autoexport.NewSpanExporter(ctx) @@ -170,10 +184,10 @@ func initOtelTracing(ctx context.Context, traces bool, resource *resource.Resour return shutdownFn, nil } -func initOtelMetrics(ctx context.Context, metrics bool, resource *resource.Resource) (shutdownFn servicerunner.Service, err error) { - if !metrics { - otel.SetMeterProvider(metricnoop.NewMeterProvider()) - return nil, nil +func initOtelMetrics(ctx context.Context, resource *resource.Resource) (shutdownFn servicerunner.Service, err error) { + // If the env var OTEL_METRICS_EXPORTER is empty, we set it to "none" + if os.Getenv("OTEL_METRICS_EXPORTER") == "" { + os.Setenv("OTEL_METRICS_EXPORTER", "none") } mr, err := autoexport.NewMetricReader(ctx) diff --git a/backend/internal/bootstrap/router_bootstrap.go b/backend/internal/bootstrap/router_bootstrap.go index b8b08e81..32b2f9c5 100644 --- a/backend/internal/bootstrap/router_bootstrap.go +++ b/backend/internal/bootstrap/router_bootstrap.go @@ -26,6 +26,7 @@ import ( "github.com/pocket-id/pocket-id/backend/internal/common" "github.com/pocket-id/pocket-id/backend/internal/controller" "github.com/pocket-id/pocket-id/backend/internal/middleware" + "github.com/pocket-id/pocket-id/backend/internal/tracing" "github.com/pocket-id/pocket-id/backend/internal/utils/systemd" ) @@ -94,8 +95,24 @@ func configureEngine(r *gin.Engine) { r.TrustedPlatform = common.EnvConfig.TrustedPlatform } - if common.EnvConfig.TracingEnabled { - r.Use(otelgin.Middleware(common.Name)) + r.Use(otelgin.Middleware( + common.Name, + otelgin.WithFilter(shouldTraceRequest)), + ) +} + +// shouldTraceRequest reports whether an incoming request should be traced. +// It traces only requests handled by real backend routes (the API, the OIDC/OAuth endpoints, and the well-known documents). +// Everything else falls through to the frontend NoRoute handler, which serves the SPA shell and static assets; tracing those would produce noisy, unparented spans named just "GET" with an empty http.route. +func shouldTraceRequest(r *http.Request) bool { + p := r.URL.Path + switch { + case strings.HasPrefix(p, "/api/"), + strings.HasPrefix(p, "/.well-known/"), + p == "/authorize": + return true + default: + return false } } @@ -159,6 +176,10 @@ func registerRoutes(r *gin.Engine, db *gorm.DB, svc *services, rateLimitServices // These are not rate-limited. controller.NewHealthzController(r) + // Receives OTLP trace payloads from the browser SPA (POST /internal/telemetry/traces) and forwards them to the collector, when trace export is enabled. + // Outside /api, so it's unauthenticated and not traced, but it is rate-limited. + tracing.NewTelemetryController(r, rateLimitMiddleware.Add(middleware.RateLimitInternal)) + return nil } diff --git a/backend/internal/common/env_config.go b/backend/internal/common/env_config.go index 06e17b3d..a98738fc 100644 --- a/backend/internal/common/env_config.go +++ b/backend/internal/common/env_config.go @@ -81,10 +81,8 @@ type EnvConfigSchema struct { ActorsPort string `env:"ACTORS_PORT"` ActorsHost string `env:"ACTORS_HOST" options:"toLower"` - LogLevel string `env:"LOG_LEVEL" options:"toLower"` - MetricsEnabled bool `env:"METRICS_ENABLED"` - TracingEnabled bool `env:"TRACING_ENABLED"` - LogJSON bool `env:"LOG_JSON"` + LogLevel string `env:"LOG_LEVEL" options:"toLower"` + LogJSON bool `env:"LOG_JSON"` } var EnvConfig = defaultConfig() diff --git a/backend/internal/common/env_config_test.go b/backend/internal/common/env_config_test.go index 0eec5424..a64ae75d 100644 --- a/backend/internal/common/env_config_test.go +++ b/backend/internal/common/env_config_test.go @@ -123,8 +123,6 @@ func TestParseEnvConfig(t *testing.T) { err := parseAndValidateEnvConfig(t) require.NoError(t, err) assert.True(t, EnvConfig.UiConfigDisabled) - assert.True(t, EnvConfig.MetricsEnabled) - assert.False(t, EnvConfig.TracingEnabled) assert.True(t, EnvConfig.TrustProxy) assert.False(t, EnvConfig.AnalyticsDisabled) }) diff --git a/backend/internal/controller/app_config_controller.go b/backend/internal/controller/app_config_controller.go index 6317fdf7..c52d8b1c 100644 --- a/backend/internal/controller/app_config_controller.go +++ b/backend/internal/controller/app_config_controller.go @@ -9,6 +9,7 @@ import ( "github.com/pocket-id/pocket-id/backend/internal/dto" "github.com/pocket-id/pocket-id/backend/internal/middleware" "github.com/pocket-id/pocket-id/backend/internal/service" + "github.com/pocket-id/pocket-id/backend/internal/tracing" ) // NewAppConfigController creates a new controller for application configuration endpoints @@ -66,6 +67,13 @@ func (acc *AppConfigController) listAppConfigHandler(c *gin.Context) { Type: "boolean", }) + // Manually add tracingEnabled, derived from the OTel environment, so the frontend only exports traces when the backend can forward them to a collector + configVariablesDto = append(configVariablesDto, dto.PublicAppConfigVariableDto{ + Key: "tracingEnabled", + Value: strconv.FormatBool(tracing.FrontendTracingEnabled()), + Type: "boolean", + }) + c.JSON(http.StatusOK, configVariablesDto) } diff --git a/backend/internal/job/scheduler.go b/backend/internal/job/scheduler.go index 2ef2019b..5985bb34 100644 --- a/backend/internal/job/scheduler.go +++ b/backend/internal/job/scheduler.go @@ -10,8 +10,10 @@ import ( backoff "github.com/cenkalti/backoff/v5" "github.com/go-co-op/gocron/v2" "github.com/google/uuid" + "go.opentelemetry.io/otel/trace" "github.com/pocket-id/pocket-id/backend/internal/service" + "github.com/pocket-id/pocket-id/backend/internal/tracing" ) type Scheduler struct { @@ -65,52 +67,17 @@ func (s *Scheduler) Run(ctx context.Context) error { } func (s *Scheduler) RegisterJob(ctx context.Context, name string, def gocron.JobDefinition, jobFn func(ctx context.Context) error, opts service.RegisterJobOpts) error { + // Wrap the job in a handler that adds tracing and logging + jobFn = jobWithObservability(name, jobFn) + // If a BackOff strategy is provided, wrap the job with retry logic if opts.BackOff != nil { - origJob := jobFn - jobFn = func(ctx context.Context) error { - _, err := backoff.Retry( - ctx, - func() (struct{}, error) { - return struct{}{}, origJob(ctx) - }, - backoff.WithBackOff(opts.BackOff), - backoff.WithNotify(func(err error, d time.Duration) { - slog.WarnContext(ctx, "Job failed, retrying", - slog.String("name", name), - slog.Any("error", err), - slog.Duration("retryIn", d), - ) - }), - ) - return err - } + jobFn = jobWithBackOff(jobFn, opts.BackOff) } jobOptions := []gocron.JobOption{ gocron.WithContext(ctx), gocron.WithName(name), - gocron.WithEventListeners( - gocron.BeforeJobRuns(func(jobID uuid.UUID, jobName string) { - slog.Info("Starting job", - slog.String("name", name), - slog.String("id", jobID.String()), - ) - }), - gocron.AfterJobRuns(func(jobID uuid.UUID, jobName string) { - slog.Info("Job run successfully", - slog.String("name", name), - slog.String("id", jobID.String()), - ) - }), - gocron.AfterJobRunsWithError(func(jobID uuid.UUID, jobName string, err error) { - slog.Error("Job failed with error", - slog.String("name", name), - slog.String("id", jobID.String()), - slog.Any("error", err), - ) - }), - ), } if opts.RunImmediately { @@ -120,7 +87,6 @@ func (s *Scheduler) RegisterJob(ctx context.Context, name string, def gocron.Job jobOptions = append(jobOptions, opts.ExtraOptions...) _, err := s.scheduler.NewJob(def, gocron.NewTask(jobFn), jobOptions...) - if err != nil { return fmt.Errorf("failed to register job %q: %w", name, err) } @@ -128,6 +94,76 @@ func (s *Scheduler) RegisterJob(ctx context.Context, name string, def gocron.Job return nil } +type ( + jobNameKey struct{} + jobIDKey struct{} + jobFn = func(ctx context.Context) error +) + +func jobWithObservability(jobName string, job jobFn) jobFn { + return func(ctx context.Context) error { + // Generate a random job ID + jobID := uuid.NewString() + + // Save in the context + ctx = context.WithValue(ctx, jobNameKey{}, jobName) + ctx = context.WithValue(ctx, jobIDKey{}, jobID) + + // Create a new context with the span + var err error + ctx, span := tracing.Start(ctx, "pocketid.job."+jobName, + trace.WithSpanKind(trace.SpanKindInternal), + trace.WithAttributes( + tracing.JobID(jobID), + ), + ) + defer tracing.End(span, err) + + // Log the start + logger := slog.With( + slog.String("name", jobName), + slog.String("jobID", jobID), + ) + start := time.Now() + logger.InfoContext(ctx, "Starting job") + + // Run the job + err = job(ctx) + d := time.Since(start) + if err != nil { + logger.ErrorContext(ctx, "Job failed", slog.Any("error", err), slog.Duration("duration", d)) + return err + } + + logger.InfoContext(ctx, "Job run successfully", slog.Duration("duration", d)) + return nil + } +} + +func jobWithBackOff(job jobFn, bo backoff.BackOff) jobFn { + return func(ctx context.Context) error { + jobName, _ := (ctx.Value(jobNameKey{})).(string) + jobID, _ := (ctx.Value(jobIDKey{})).(string) + + _, err := backoff.Retry( + ctx, + func() (struct{}, error) { + return struct{}{}, job(ctx) + }, + backoff.WithBackOff(bo), + backoff.WithNotify(func(err error, d time.Duration) { + slog.WarnContext(ctx, "Job failed, retrying", + slog.String("name", jobName), + slog.String("jobID", jobID), + slog.Any("error", err), + slog.Duration("retryIn", d), + ) + }), + ) + return err + } +} + func jobDefWithJitter(interval time.Duration) gocron.JobDefinition { const jitter = 5 * time.Minute diff --git a/backend/internal/middleware/rate_limit.go b/backend/internal/middleware/rate_limit.go index 2a9ec391..8b8def1e 100644 --- a/backend/internal/middleware/rate_limit.go +++ b/backend/internal/middleware/rate_limit.go @@ -26,6 +26,7 @@ const ( RateLimitOneTimeAccessEmail = "one-time-access-email" RateLimitSendEmailVerification = "send-email-verification" RateLimitVerifyEmail = "verify-email" + RateLimitInternal = "internal" ) // RateLimitPolicy is the configuration for a single rate-limit actor @@ -45,14 +46,15 @@ type RateLimitPolicy struct { // The slice is built on each call so the policies are not retained at the package level, and the actor host registers one limiter per entry func RateLimitPolicies() []RateLimitPolicy { return []RateLimitPolicy{ - {Name: RateLimitAPI, Rate: 1, Per: time.Second, Burst: 100}, - {Name: RateLimitSignup, Rate: 1, Per: time.Minute, Burst: 10}, - {Name: RateLimitWebauthnLogin, Rate: 1, Per: 10 * time.Second, Burst: 5}, + {Name: RateLimitAPI, Rate: 100, Per: time.Second}, + {Name: RateLimitSignup, Rate: 2, Per: time.Minute, Burst: 10}, + {Name: RateLimitWebauthnLogin, Rate: 1, Per: 5 * time.Second, Burst: 10}, {Name: RateLimitWebauthnReauthenticate, Rate: 1, Per: 10 * time.Second, Burst: 5}, {Name: RateLimitOneTimeAccessToken, Rate: 1, Per: 10 * time.Second, Burst: 5}, - {Name: RateLimitOneTimeAccessEmail, Rate: 1, Per: 10 * time.Minute, Burst: 3}, - {Name: RateLimitSendEmailVerification, Rate: 1, Per: 10 * time.Minute, Burst: 3}, + {Name: RateLimitOneTimeAccessEmail, Rate: 2, Per: 10 * time.Minute, Burst: 5}, + {Name: RateLimitSendEmailVerification, Rate: 2, Per: 10 * time.Minute, Burst: 1}, {Name: RateLimitVerifyEmail, Rate: 1, Per: 10 * time.Second, Burst: 5}, + {Name: RateLimitInternal, Rate: 20, Per: time.Second, Burst: 20}, } } diff --git a/backend/internal/service/app_config_service.go b/backend/internal/service/app_config_service.go index be1865db..a493796d 100644 --- a/backend/internal/service/app_config_service.go +++ b/backend/internal/service/app_config_service.go @@ -17,6 +17,7 @@ import ( "github.com/pocket-id/pocket-id/backend/internal/common" "github.com/pocket-id/pocket-id/backend/internal/dto" "github.com/pocket-id/pocket-id/backend/internal/model" + "github.com/pocket-id/pocket-id/backend/internal/tracing" "github.com/pocket-id/pocket-id/backend/internal/utils" ) @@ -25,12 +26,16 @@ type AppConfigService struct { db *gorm.DB } -func NewAppConfigService(ctx context.Context, db *gorm.DB) (*AppConfigService, error) { - service := &AppConfigService{ +func NewAppConfigService(ctx context.Context, db *gorm.DB) (service *AppConfigService, err error) { + service = &AppConfigService{ db: db, } - err := service.LoadDbConfig(ctx) + ctx, span := tracing.Start(ctx, "pocketid.appconfig.init") + defer tracing.End(span, err) + + // We need to assign to the "err" variable, do not inline this into the "if" + err = service.LoadDbConfig(ctx) if err != nil { return nil, fmt.Errorf("failed to initialize app config service: %w", err) } @@ -168,9 +173,7 @@ func (s *AppConfigService) UpdateAppConfig(ctx context.Context, input dto.AppCon if err != nil { return nil, err } - defer func() { - tx.Rollback() - }() + defer tx.Rollback() // From here onwards, we know we are the only process/goroutine with exclusive access to the config // Re-load the config from the database to be sure we have the correct data @@ -248,9 +251,7 @@ func (s *AppConfigService) UpdateAppConfigValues(ctx context.Context, keysAndVal if err != nil { return err } - defer func() { - tx.Rollback() - }() + defer tx.Rollback() // From here onwards, we know we are the only process/goroutine with exclusive access to the config // Re-load the config from the database to be sure we have the correct data diff --git a/backend/internal/service/app_lock_service.go b/backend/internal/service/app_lock_service.go index f1bd0c2a..c8f62d92 100644 --- a/backend/internal/service/app_lock_service.go +++ b/backend/internal/service/app_lock_service.go @@ -10,9 +10,12 @@ import ( "time" "github.com/google/uuid" - "github.com/pocket-id/pocket-id/backend/internal/model" + "go.opentelemetry.io/otel/trace" "gorm.io/gorm" "gorm.io/gorm/clause" + + "github.com/pocket-id/pocket-id/backend/internal/model" + "github.com/pocket-id/pocket-id/backend/internal/tracing" ) var ( @@ -73,13 +76,24 @@ func (lv *lockValue) Unmarshal(raw string) error { // Acquire obtains the lock. When force is true, the lock is stolen from any existing owner. // If the lock is forcefully acquired, it blocks until the previous lock has expired. func (s *AppLockService) Acquire(ctx context.Context, force bool) (waitUntil time.Time, err error) { - tx := s.db.WithContext(ctx).Begin() - if tx.Error != nil { - return time.Time{}, fmt.Errorf("begin lock transaction: %w", tx.Error) + ctx, span := tracing.Start(ctx, "pocketid.applock.acquire", trace.WithSpanKind(trace.SpanKindInternal)) + waitUntil, err = s.acquire(ctx, force) + if err != nil { + tracing.End(span, err) + return waitUntil, err } - defer func() { - tx.Rollback() - }() + + span.End() + return waitUntil, nil +} + +func (s *AppLockService) acquire(ctx context.Context, force bool) (waitUntil time.Time, err error) { + tx := s.db.WithContext(ctx).Begin() + err = tx.Error + if err != nil { + return time.Time{}, fmt.Errorf("begin lock transaction: %w", err) + } + defer tx.Rollback() var prevLockRaw string err = tx. @@ -150,7 +164,8 @@ func (s *AppLockService) Acquire(ctx context.Context, force bool) (waitUntil tim // If there is a lock that is not expired and force is false, no rows will be affected if res.RowsAffected == 0 { - return time.Time{}, ErrLockUnavailable + err = ErrLockUnavailable + return time.Time{}, err } if force && prevLock.ExpiresAt > nowUnix && prevLock.LockID != s.lockID { @@ -161,10 +176,11 @@ func (s *AppLockService) Acquire(ctx context.Context, force bool) (waitUntil tim slog.Int64("process_id", s.processID), slog.String("host_id", s.hostID), } - if wait := time.Until(waitUntil); wait > 0 { + wait := time.Until(waitUntil) + if wait > 0 { attrs = append(attrs, slog.Duration("wait_before_proceeding", wait)) } - slog.Info("Acquired application lock", attrs...) + slog.InfoContext(ctx, "Acquired application lock", attrs...) return waitUntil, nil } @@ -179,7 +195,9 @@ func (s *AppLockService) RunRenewal(ctx context.Context) error { case <-ctx.Done(): return nil case <-ticker.C: - err := s.renew(ctx) + renewCtx, span := tracing.Start(ctx, "pocketid.applock.renew", trace.WithSpanKind(trace.SpanKindInternal)) + err := s.renew(renewCtx) + tracing.End(span, err) if err != nil { return fmt.Errorf("renew lock: %w", err) } @@ -188,7 +206,19 @@ func (s *AppLockService) RunRenewal(ctx context.Context) error { } // Release releases the lock if it is held by this process. -func (s *AppLockService) Release(ctx context.Context) error { +func (s *AppLockService) Release(ctx context.Context) (err error) { + ctx, span := tracing.Start(ctx, "pocketid.applock.release", trace.WithSpanKind(trace.SpanKindInternal)) + err = s.release(ctx) + if err != nil { + tracing.End(span, err) + return err + } + + span.End() + return nil +} + +func (s *AppLockService) release(ctx context.Context) error { db, err := s.db.DB() if err != nil { return fmt.Errorf("failed to get DB connection: %w", err) @@ -226,13 +256,15 @@ WHERE key = $1 } if count == 0 { - slog.Warn("Application lock not held by this process, cannot release", + // Treat this as a warning, not an error + slog.WarnContext(ctx, "Application lock not held by this process, cannot release", slog.Int64("process_id", s.processID), slog.String("host_id", s.hostID), ) + return nil } - slog.Info("Released application lock", + slog.InfoContext(ctx, "Released application lock", slog.Int64("process_id", s.processID), slog.String("host_id", s.hostID), ) @@ -302,7 +334,7 @@ WHERE key = $2 } // All good - slog.Debug("Renewed application lock", + slog.DebugContext(ctx, "Renewed application lock", slog.Int64("process_id", s.processID), slog.String("host_id", s.hostID), slog.Duration("duration", time.Since(now)), @@ -311,7 +343,7 @@ WHERE key = $2 } // If we're here, we have an error that can be retried - slog.Debug("Application lock renewal attempt failed", + slog.DebugContext(ctx, "Application lock renewal attempt failed", slog.Any("error", err), slog.Duration("duration", time.Since(now)), ) diff --git a/backend/internal/service/audit_log_service.go b/backend/internal/service/audit_log_service.go index 0a2387df..7a2290e4 100644 --- a/backend/internal/service/audit_log_service.go +++ b/backend/internal/service/audit_log_service.go @@ -9,7 +9,6 @@ import ( "github.com/pocket-id/pocket-id/backend/internal/model" "github.com/pocket-id/pocket-id/backend/internal/utils" "github.com/pocket-id/pocket-id/backend/internal/utils/email" - "go.opentelemetry.io/otel/trace" "gorm.io/gorm" ) @@ -92,11 +91,10 @@ func (s *AuditLogService) CreateNewSignInWithEmail(ctx context.Context, ipAddres // If the user hasn't logged in from the same device before and email notifications are enabled, send an email if s.appConfigService.GetDbConfig().EmailLoginNotificationEnabled.IsTrue() && count <= 1 { - // #nosec G118 - We use a background context here as this is running in a goroutine - //nolint:contextcheck go func() { - span := trace.SpanFromContext(ctx) - innerCtx := trace.ContextWithSpan(context.Background(), span) + // This runs in background, so use a context without cancellation (or it would be stopped when the request ends) + // We still want to have a context derived from the request's to carry over tracing info + innerCtx := context.WithoutCancel(ctx) // Note we don't use the transaction here because this is running in background var user model.User diff --git a/backend/internal/service/one_time_access_service.go b/backend/internal/service/one_time_access_service.go index 4093dd9b..0f6d5b27 100644 --- a/backend/internal/service/one_time_access_service.go +++ b/backend/internal/service/one_time_access_service.go @@ -13,7 +13,6 @@ import ( datatype "github.com/pocket-id/pocket-id/backend/internal/model/types" "github.com/pocket-id/pocket-id/backend/internal/utils" "github.com/pocket-id/pocket-id/backend/internal/utils/email" - "go.opentelemetry.io/otel/trace" "gorm.io/gorm" "gorm.io/gorm/clause" ) @@ -97,11 +96,10 @@ func (s *OneTimeAccessService) requestOneTimeAccessEmailInternal(ctx context.Con return nil, err } - // #nosec G118 - We use a background context here as this is running in a goroutine - //nolint:contextcheck go func() { - span := trace.SpanFromContext(ctx) - innerCtx := trace.ContextWithSpan(context.Background(), span) + // This runs in background, so use a context without cancellation (or it would be stopped when the request ends) + // We still want to have a context derived from the request's to carry over tracing info + innerCtx := context.WithoutCancel(ctx) link := common.EnvConfig.AppURL + "/lc" linkWithCode := link + "/" + oneTimeAccessToken diff --git a/backend/internal/tracing/telemetry_controller.go b/backend/internal/tracing/telemetry_controller.go new file mode 100644 index 00000000..aa11be86 --- /dev/null +++ b/backend/internal/tracing/telemetry_controller.go @@ -0,0 +1,82 @@ +package tracing + +import ( + "fmt" + "io" + "net/http" + "os" + "strings" + "time" + + "github.com/gin-gonic/gin" +) + +// maxTelemetryBodySize caps the size of a browser telemetry payload we're willing to forward. +const maxTelemetryBodySize = 1 << 20 // 1 MiB + +type telemetryController struct { + targetURL string + client *http.Client +} + +// NewTelemetryController registers an endpoint that receives OTLP/HTTP JSON trace payloads from the browser SPA and forwards them to the OpenTelemetry collector the backend already exports to. +// The route is only registered when the backend is configured to export traces via OTLP to a resolved collector endpoint, otherwise it's left unregistered and requests return 404. +func NewTelemetryController(router gin.IRouter, rateLimit gin.HandlerFunc) { + // Only register the route when the browser's traces can actually be forwarded to a collector. + if !FrontendTracingEnabled() { + return + } + + tc := &telemetryController{ + targetURL: otlpTracesEndpoint(), + client: &http.Client{Timeout: 10 * time.Second}, + } + router.POST("/internal/telemetry/traces", rateLimit, tc.forwardTraces) +} + +// frontendTracingEnabled reports whether the browser SPA should export traces. +// It's true only when trace export over OTLP is enabled and an OTLP/HTTP collector endpoint is resolvable: browser spans are forwarded to that collector by the telemetry endpoint, so without it there's nowhere to send them. +// The frontend reads this flag (via the public app config) to decide whether to start tracing at all, instead of POSTing spans to an endpoint that isn't registered. +func FrontendTracingEnabled() bool { + return os.Getenv("OTEL_TRACES_EXPORTER") == "otlp" && otlpTracesEndpoint() != "" +} + +func (tc *telemetryController) forwardTraces(c *gin.Context) { + body := http.MaxBytesReader(c.Writer, c.Request.Body, maxTelemetryBodySize) + + req, err := http.NewRequestWithContext(c.Request.Context(), http.MethodPost, tc.targetURL, body) + if err != nil { + _ = c.Error(fmt.Errorf("failed to build telemetry forward request: %w", err)) + c.Status(http.StatusBadGateway) + return + } + req.Header.Set("Content-Type", "application/json") + + resp, err := tc.client.Do(req) + if err != nil { + _ = c.Error(fmt.Errorf("failed to forward telemetry to collector: %w", err)) + c.Status(http.StatusBadGateway) + return + } + defer resp.Body.Close() + _, _ = io.Copy(io.Discard, resp.Body) + + c.Status(resp.StatusCode) +} + +// otlpTracesEndpoint derives the collector's OTLP/HTTP traces URL from the standard OpenTelemetry environment variables. +// It returns an empty string when no OTLP endpoint is configured, in which case the telemetry route is not registered. +// The endpoint must speak OTLP/HTTP (typically port 4318). +func otlpTracesEndpoint() string { + v := os.Getenv("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT") + if v != "" { + return v + } + + base := os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT") + if base == "" { + return "" + } + + return strings.TrimRight(base, "/") + "/v1/traces" +} diff --git a/backend/internal/tracing/tracing.go b/backend/internal/tracing/tracing.go new file mode 100644 index 00000000..f48a5e46 --- /dev/null +++ b/backend/internal/tracing/tracing.go @@ -0,0 +1,62 @@ +// Package tracing wraps the global OpenTelemetry tracer for Pocket ID +// It centralizes the instrumentation name, the span helpers, and the attribute keys so spans are named and tagged consistently across packages +// The tracer is resolved from the global provider, so every span is a no-op until the embedding app or the runtime binary configures OpenTelemetry +package tracing + +import ( + "context" + "errors" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + "go.opentelemetry.io/otel/trace" + + "github.com/pocket-id/pocket-id/backend/internal/common" +) + +// tracer delegates to the global provider, picking up the real provider once one is installed +var tracer = otel.Tracer(common.TracerName) + +// Start begins a span as a child of the span in ctx, returning the derived context and the span +// The caller must end the span, normally with End so the status is set from the operation error +// +//nolint:spancheck // the span is intentionally returned for the caller to end +func Start(ctx context.Context, name string, opts ...trace.SpanStartOption) (context.Context, trace.Span) { + return tracer.Start(ctx, name, opts...) +} + +// End finishes the span, recording err and marking the span failed when err is non-nil +func End(span trace.Span, err error) { + if err != nil { + span.RecordError(err) + span.SetStatus(codes.Error, err.Error()) + } + + span.End() +} + +// EndExpected finishes the span like End, but treats any of the benign errors as a normal outcome rather than a failure +// It is used, for example, for operations whose "not found" result is expected +func EndExpected(span trace.Span, err error, benign ...error) { + for _, b := range benign { + if errors.Is(err, b) { + span.End() + return + } + } + + End(span, err) +} + +// Fail records err on the span in ctx and marks it failed, without ending it +// It is used where the operation reports failure through a non-error value, such as a structured protocol error +func Fail(ctx context.Context, msg string) { + span := trace.SpanFromContext(ctx) + span.SetStatus(codes.Error, msg) +} + +// JobID returns the attribute for a job ID +func JobID(v string) attribute.KeyValue { + return attribute.String("pocketid.job.id", v) +} diff --git a/backend/internal/utils/networked_filesystem_linux.go b/backend/internal/utils/networked_filesystem_linux.go deleted file mode 100644 index 31afd2fa..00000000 --- a/backend/internal/utils/networked_filesystem_linux.go +++ /dev/null @@ -1,35 +0,0 @@ -//go:build linux - -package utils - -import ( - "fmt" - "syscall" -) - -// Filesystem magic values from Linux's include/uapi/linux/magic.h, used by statfs(2). -const ( - nfsSuperMagic = 0x6969 - smbSuperMagic = 0x517b - cifsSuperMagic = 0xff534d42 - fuseSuperMagic = 0x65735546 -) - -// IsNetworkedFileSystem reports whether path is on a filesystem that is known to be unsafe for SQLite, specifically NFS, SMB/CIFS, or FUSE mounts. -func IsNetworkedFileSystem(path string) (bool, error) { - var statfs syscall.Statfs_t - err := syscall.Statfs(path, &statfs) - if err != nil { - return false, fmt.Errorf("error executing statfs syscall: %w", err) - } - - // Statfs_t.Type is arch-dependent (for example, int32 on some systems and int64 on others). - // Normalize through uint32 first so signed values still preserve the Linux bit pattern for magic numbers such as CIFS (0xff534d42), then compare in a wide unsigned form. - //nolint:gosec - switch uint64(uint32(statfs.Type)) { - case nfsSuperMagic, smbSuperMagic, cifsSuperMagic, fuseSuperMagic: - return true, nil - default: - return false, nil - } -} diff --git a/backend/internal/utils/networked_filesystem_nonlinux.go b/backend/internal/utils/networked_filesystem_nonlinux.go deleted file mode 100644 index 386f5539..00000000 --- a/backend/internal/utils/networked_filesystem_nonlinux.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build !linux - -package utils - -// IsNetworkedFileSystem returns false on non-Linux systems because this detection is only used for Linux-specific statfs(2) filesystem magic values. -func IsNetworkedFileSystem(string) (bool, error) { - return false, nil -} diff --git a/frontend/package.json b/frontend/package.json index 7b4a8787..b9f12094 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,6 +14,11 @@ "format": "prettier --write ." }, "dependencies": { + "@opentelemetry/api": "^1.9.1", + "@opentelemetry/exporter-trace-otlp-http": "^0.219.0", + "@opentelemetry/resources": "^2.8.0", + "@opentelemetry/sdk-trace-web": "^2.8.0", + "@opentelemetry/semantic-conventions": "^1.41.1", "@simplewebauthn/browser": "^13.3.0", "@tailwindcss/vite": "^4.3.0", "axios": "^1.16.1", diff --git a/frontend/src/lib/services/api-service.ts b/frontend/src/lib/services/api-service.ts index d85d104a..06ecce18 100644 --- a/frontend/src/lib/services/api-service.ts +++ b/frontend/src/lib/services/api-service.ts @@ -1,4 +1,9 @@ -import axios from 'axios'; +import { endRequestSpan, startRequestSpan } from '$lib/utils/tracing-util'; +import axios, { AxiosError, type InternalAxiosRequestConfig } from 'axios'; +import type { Span } from '@opentelemetry/api'; + +// Tracks the in-flight span for each request config so the response/error interceptor can end it. +const requestSpans = new WeakMap(); abstract class APIService { protected api = axios.create({ baseURL: '/api' }); @@ -7,6 +12,32 @@ abstract class APIService { if (typeof process !== 'undefined' && process?.env?.DEVELOPMENT_BACKEND_URL) { this.api.defaults.baseURL = process.env.DEVELOPMENT_BACKEND_URL; } + + // Wrap each API request in a CLIENT span and inject its W3C trace headers, so backend spans correlate with the SPA + this.api.interceptors.request.use((config) => { + const method = (config.method ?? 'get').toUpperCase(); + const url = `${config.baseURL ?? ''}${config.url ?? ''}`; + // startRequestSpan returns undefined when tracing is disabled, so only track a span when one was created. + const span = startRequestSpan(method, url, config.headers); + if (span) { + requestSpans.set(config, span); + } + return config; + }); + this.api.interceptors.response.use( + (response) => { + endRequestSpan(requestSpans.get(response.config), response.status); + requestSpans.delete(response.config); + return response; + }, + (error: unknown) => { + if (error instanceof AxiosError && error.config) { + endRequestSpan(requestSpans.get(error.config), error.response?.status, error); + requestSpans.delete(error.config); + } + return Promise.reject(error); + } + ); } } diff --git a/frontend/src/lib/types/application-configuration.type.ts b/frontend/src/lib/types/application-configuration.type.ts index 011fe5b7..edb4e6da 100644 --- a/frontend/src/lib/types/application-configuration.type.ts +++ b/frontend/src/lib/types/application-configuration.type.ts @@ -13,6 +13,7 @@ export type AppConfig = { uiConfigDisabled: boolean; accentColor: string; requireUserEmail: boolean; + tracingEnabled: boolean; }; export type AllAppConfig = AppConfig & { diff --git a/frontend/src/lib/utils/tracing-util.ts b/frontend/src/lib/utils/tracing-util.ts new file mode 100644 index 00000000..d5f1de2b --- /dev/null +++ b/frontend/src/lib/utils/tracing-util.ts @@ -0,0 +1,183 @@ +// Frontend tracing built on the OpenTelemetry Web SDK. +// +// A page-level span is started for each page view and a CLIENT span is created for every API request as its child, with the W3C `traceparent` header injected into the outgoing request. +// The backend server spans therefore join the same trace, so a page view and everything it triggers — frontend, backend and database — show up as one trace rooted at the page view. +// +// Spans are exported over OTLP/HTTP to a same-origin backend endpoint (`/internal/telemetry/traces`), which forwards them to the collector the backend already uses. +// Same-origin means no CORS and no browser-facing collector to configure. +// +// The OpenTelemetry libraries are heavy, so they are only pulled in via dynamic `import()` once tracing is +// enabled (see `setTracingEnabled`). When tracing is disabled they are never loaded and stay out of the main app bundle. + +import { browser, version } from '$app/environment'; +// Type-only imports are erased at build time, so they don't pull the OpenTelemetry libraries into the bundle. +import type { Context, Span } from '@opentelemetry/api'; + +const TRACER_NAME = 'pocket-id-frontend'; + +// Same-origin endpoint that forwards the browser's OTLP payloads to the backend's collector. +const EXPORTER_URL = '/internal/telemetry/traces'; + +/** Minimal shape of a header carrier we can inject into (satisfied by axios' AxiosHeaders). */ +export type HeaderCarrier = { set(name: string, value: string): void }; + +// Runtime bindings from `@opentelemetry/api`, loaded lazily via dynamic import once tracing is enabled. +// Undefined until the OpenTelemetry libraries have been imported and the tracer provider registered. +let otel: typeof import('@opentelemetry/api') | undefined; + +// Memoizes the dynamic import + provider setup so the libraries are loaded and the provider registered only once. +let providerPromise: Promise | undefined; + +// Whether the backend is configured to receive traces (OTEL_TRACES_EXPORTER=otlp with a resolvable collector endpoint). +// It stays false until the app configuration is loaded, so nothing is exported to `/internal/telemetry/traces` when tracing is disabled (that route isn't even registered then). +let tracingEnabled = false; + +// The current page-level span and a Context carrying it. +// API request spans are created as its children so a page view groups everything it triggers into one trace. +// `pageContext` is undefined while no page trace is active. +let pageSpan: Span | undefined; +let pageContext: Context | undefined; + +/** + * Enables or disables frontend tracing based on the backend configuration. + * Called from the root layout once the application configuration is loaded. + * When enabled, the OpenTelemetry libraries are dynamically imported and the tracer provider is set up; + * when disabled, they are never loaded, so nothing is sent to the backend telemetry endpoint and the libraries stay out of the main bundle. + */ +export async function setTracingEnabled(enabled: boolean): Promise { + tracingEnabled = enabled; + if (enabled) { + await ensureProvider(); + } +} + +// Dynamically imports the OpenTelemetry libraries (so they're only fetched when tracing is enabled) and installs the tracer provider. +// Safe to call multiple times: the work is memoized and only runs once. +function ensureProvider(): Promise { + if (!browser || !tracingEnabled) { + return Promise.resolve(); + } + providerPromise ??= loadAndRegister(); + return providerPromise; +} + +async function loadAndRegister(): Promise { + try { + const [ + api, + { OTLPTraceExporter }, + { resourceFromAttributes }, + { BatchSpanProcessor, WebTracerProvider }, + { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } + ] = await Promise.all([ + import('@opentelemetry/api'), + import('@opentelemetry/exporter-trace-otlp-http'), + import('@opentelemetry/resources'), + import('@opentelemetry/sdk-trace-web'), + import('@opentelemetry/semantic-conventions') + ]); + + const provider = new WebTracerProvider({ + resource: resourceFromAttributes({ + [ATTR_SERVICE_NAME]: TRACER_NAME, + [ATTR_SERVICE_VERSION]: version + }), + spanProcessors: [new BatchSpanProcessor(new OTLPTraceExporter({ url: EXPORTER_URL }))] + }); + + // register() installs the provider globally together with the default W3C trace context (`traceparent`) propagator. + provider.register(); + + // End (and flush) the current page span when the tab/page goes away, so its trace completes. + addEventListener('pagehide', () => { + endPageSpan(); + void provider.forceFlush(); + }); + + otel = api; + } catch (e) { + // Tracing is best-effort: never let a failure to load the OpenTelemetry libraries break the app. + console.error('Failed to initialize frontend tracing', e); + } +} + +function endPageSpan(): void { + pageSpan?.end(); + pageSpan = undefined; + pageContext = undefined; +} + +/** + * Starts a new page-level trace, ending the previous one, so that a page view and the API calls it triggers form a single trace rooted at the page view. + * Called on each navigation from the root +layout. + */ +export function startPageTrace(path?: string): void { + if (!browser || !tracingEnabled || !otel) { + return; + } + endPageSpan(); + + pageSpan = otel.trace + .getTracer(TRACER_NAME) + .startSpan(path ? `pageview ${path}` : 'pageview', { + root: true, + kind: otel.SpanKind.INTERNAL + }); + pageContext = otel.trace.setSpan(otel.ROOT_CONTEXT, pageSpan); +} + +/** + * Creates a CLIENT span for an outgoing API request (as a child of the current page span) and injects the W3C trace headers into `carrier`. + * The returned span must be ended with {@link endRequestSpan} once the response (or error) is available. + */ +export function startRequestSpan( + method: string, + url: string, + carrier: HeaderCarrier +): Span | undefined { + if (!tracingEnabled || !otel) { + return undefined; + } + + // If no navigation has happened yet (e.g. the initial page load), start the page trace lazily so the initial requests are grouped under it too. + if (!pageContext) { + startPageTrace(typeof location === 'undefined' ? undefined : location.pathname); + } + + const parent = pageContext ?? otel.ROOT_CONTEXT; + const span = otel.trace.getTracer(TRACER_NAME).startSpan( + `${method} ${url}`, + { + kind: otel.SpanKind.CLIENT, + attributes: { + 'http.request.method': method, + 'url.full': url + } + }, + parent + ); + + otel.propagation.inject(otel.trace.setSpan(parent, span), carrier, { + set: (c, key, value) => c.set(key, value) + }); + + return span; +} + +/** Ends a request span, recording the response status (and error, if any). */ +export function endRequestSpan(span: Span | undefined, statusCode?: number, error?: unknown): void { + // A span can only exist when the OpenTelemetry libraries were loaded, so `otel` is defined here. + if (!span || !otel) { + return; + } + if (statusCode !== undefined) { + span.setAttribute('http.response.status_code', statusCode); + } + if (error) { + span.recordException(error instanceof Error ? error : String(error)); + span.setStatus({ code: otel.SpanStatusCode.ERROR }); + } else if (statusCode !== undefined && statusCode >= 400) { + span.setStatus({ code: otel.SpanStatusCode.ERROR }); + } + span.end(); +} diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte index b1145d7f..ccbd968b 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -4,11 +4,16 @@ import Header from '$lib/components/header/header.svelte'; import { Toaster } from '$lib/components/ui/sonner'; import { m } from '$lib/paraglide/messages'; + import { startPageTrace } from '$lib/utils/tracing-util'; + import { beforeNavigate } from '$app/navigation'; import { ModeWatcher } from 'mode-watcher'; import { type Snippet } from 'svelte'; import '../app.css'; import type { LayoutData } from './$types'; + // Start a new page-level trace on each navigation so a page view and the API calls it triggers are correlated as a single trace. + beforeNavigate((nav) => startPageTrace(nav.to?.url.pathname)); + let { data, children diff --git a/frontend/src/routes/+layout.ts b/frontend/src/routes/+layout.ts index d33bb849..65be769f 100644 --- a/frontend/src/routes/+layout.ts +++ b/frontend/src/routes/+layout.ts @@ -4,6 +4,7 @@ import appConfigStore from '$lib/stores/application-configuration-store'; import userStore from '$lib/stores/user-store'; import { setLocaleForLibraries } from '$lib/utils/locale.util'; import { getAuthRedirectPath } from '$lib/utils/redirection-util'; +import { setTracingEnabled } from '$lib/utils/tracing-util'; import { redirect } from '@sveltejs/kit'; import type { LayoutLoad } from './$types'; @@ -37,6 +38,10 @@ export const load: LayoutLoad = async ({ url }) => { appConfigStore.set(appConfig); } + // Only export traces when the backend is configured to forward them; otherwise the SPA would POST spans to an unregistered endpoint. + // When enabled this dynamically imports the OpenTelemetry libraries, so awaiting it ensures the tracer provider is ready before any traced navigation or request. + await setTracingEnabled(appConfig?.tracingEnabled ?? false); + await setLocaleForLibraries(); return { diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 7b9bc9c3..05100098 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -39,6 +39,9 @@ export default defineConfig((mode) => { '/api': { target: process.env.DEVELOPMENT_BACKEND_URL || 'http://localhost:1411' }, + '/internal': { + target: process.env.DEVELOPMENT_BACKEND_URL || 'http://localhost:1411' + }, '/.well-known': { target: process.env.DEVELOPMENT_BACKEND_URL || 'http://localhost:1411' }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a6a39a36..e1c23859 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,7 +46,7 @@ importers: devDependencies: '@react-email/preview-server': specifier: 5.2.10 - version: 5.2.10(@playwright/test@1.61.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + version: 5.2.10(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@types/node': specifier: ^25.9.0 version: 25.9.4 @@ -61,16 +61,31 @@ importers: version: 6.6.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7) tsx: specifier: ^4.22.2 - version: 4.22.4 + version: 4.23.0 frontend: dependencies: + '@opentelemetry/api': + specifier: ^1.9.1 + version: 1.9.1 + '@opentelemetry/exporter-trace-otlp-http': + specifier: ^0.219.0 + version: 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': + specifier: ^2.8.0 + version: 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-web': + specifier: ^2.8.0 + version: 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': + specifier: ^1.41.1 + version: 1.41.1 '@simplewebauthn/browser': specifier: ^13.3.0 version: 13.3.0 '@tailwindcss/vite': specifier: ^4.3.0 - version: 4.3.1(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)) + version: 4.3.2(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) axios: specifier: ^1.16.1 version: 1.18.1 @@ -85,10 +100,10 @@ importers: version: 1.5.4 runed: specifier: ^0.37.1 - version: 0.37.1(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(zod@4.4.3) + version: 0.37.1(@sveltejs/kit@2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(zod@4.4.3) sveltekit-superforms: specifier: ^2.30.1 - version: 2.30.1(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(@types/json-schema@7.0.15)(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3) + version: 2.30.2(@sveltejs/kit@2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(@types/json-schema@7.0.15)(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3) tailwind-merge: specifier: ^3.6.0 version: 3.6.0 @@ -110,16 +125,16 @@ importers: version: 3.12.2 '@lucide/svelte': specifier: ^1.16.0 - version: 1.21.0(svelte@5.56.4(@typescript-eslint/types@8.62.0)) + version: 1.23.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)) '@sveltejs/adapter-static': specifier: ^3.0.10 - version: 3.0.10(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0))) + version: 3.0.10(@sveltejs/kit@2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0))) '@sveltejs/kit': specifier: ^2.60.1 - version: 2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)) + version: 2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) '@sveltejs/vite-plugin-svelte': specifier: ^7.1.2 - version: 7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)) + version: 7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) '@types/node': specifier: ^25.9.0 version: 25.9.4 @@ -128,52 +143,52 @@ importers: version: 1.5.6 bits-ui: specifier: ^2.18.1 - version: 2.18.1(@internationalized/date@3.12.2)(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0)) + version: 2.18.1(@internationalized/date@3.12.2)(@sveltejs/kit@2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1)) eslint: specifier: ^10.4.0 - version: 10.5.0(jiti@2.7.0) + version: 10.6.0(jiti@2.7.0) eslint-config-prettier: specifier: ^10.1.8 - version: 10.1.8(eslint@10.5.0(jiti@2.7.0)) + version: 10.1.8(eslint@10.6.0(jiti@2.7.0)) eslint-plugin-svelte: specifier: ^3.17.1 - version: 3.20.0(eslint@10.5.0(jiti@2.7.0))(svelte@5.56.4(@typescript-eslint/types@8.62.0)) + version: 3.20.0(eslint@10.6.0(jiti@2.7.0))(svelte@5.56.4(@typescript-eslint/types@8.62.1)) formsnap: specifier: ^2.0.1 - version: 2.0.1(svelte@5.56.4(@typescript-eslint/types@8.62.0))(sveltekit-superforms@2.30.1(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(@types/json-schema@7.0.15)(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)) + version: 2.0.1(svelte@5.56.4(@typescript-eslint/types@8.62.1))(sveltekit-superforms@2.30.2(@sveltejs/kit@2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(@types/json-schema@7.0.15)(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)) globals: specifier: ^17.6.0 version: 17.7.0 mode-watcher: specifier: ^1.1.0 - version: 1.1.0(svelte@5.56.4(@typescript-eslint/types@8.62.0)) + version: 1.1.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)) prettier: specifier: ^3.8.3 - version: 3.8.5 + version: 3.9.4 prettier-plugin-svelte: specifier: ^3.5.2 - version: 3.5.2(prettier@3.8.5)(svelte@5.56.4(@typescript-eslint/types@8.62.0)) + version: 3.5.2(prettier@3.9.4)(svelte@5.56.4(@typescript-eslint/types@8.62.1)) prettier-plugin-tailwindcss: specifier: ^0.8.0 - version: 0.8.0(prettier-plugin-svelte@3.5.2(prettier@3.8.5)(svelte@5.56.4(@typescript-eslint/types@8.62.0)))(prettier@3.8.5) + version: 0.8.0(prettier-plugin-svelte@3.5.2(prettier@3.9.4)(svelte@5.56.4(@typescript-eslint/types@8.62.1)))(prettier@3.9.4) shadcn-svelte: specifier: ^1.3.0 - version: 1.3.0(svelte@5.56.4(@typescript-eslint/types@8.62.0)) + version: 1.3.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)) svelte: specifier: ^5.55.8 - version: 5.56.4(@typescript-eslint/types@8.62.0) + version: 5.56.4(@typescript-eslint/types@8.62.1) svelte-check: specifier: ^4.4.8 - version: 4.7.1(picomatch@4.0.5)(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3) + version: 4.7.1(picomatch@4.0.5)(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3) svelte-sonner: specifier: ^1.1.1 - version: 1.1.1(svelte@5.56.4(@typescript-eslint/types@8.62.0)) + version: 1.1.1(svelte@5.56.4(@typescript-eslint/types@8.62.1)) tailwind-variants: specifier: ^3.2.2 - version: 3.2.2(tailwind-merge@3.6.0)(tailwindcss@4.3.1) + version: 3.2.2(tailwind-merge@3.6.0)(tailwindcss@4.3.2) tailwindcss: specifier: ^4.3.0 - version: 4.3.1 + version: 4.3.2 tslib: specifier: ^2.8.1 version: 2.8.1 @@ -185,19 +200,19 @@ importers: version: 6.0.3 typescript-eslint: specifier: ^8.59.4 - version: 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + version: 8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) vite: specifier: ^8.0.16 - version: 8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0) + version: 8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0) vite-plugin-compression: specifier: ^0.5.1 - version: 0.5.1(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)) + version: 0.5.1(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) tests: dependencies: adm-zip: specifier: ^0.5.17 - version: 0.5.17 + version: 0.5.18 devDependencies: '@playwright/test': specifier: ^1.60.0 @@ -216,15 +231,15 @@ importers: version: 6.2.3 prettier: specifier: ^3.8.3 - version: 3.8.5 + version: 3.9.4 packages: - '@ark/schema@0.56.0': - resolution: {integrity: sha512-ECg3hox/6Z/nLajxXqNhgPtNdHWC9zNsDyskwO28WinoFEnWow4IsERNz9AnXRhTZJnYIlAJ4uGn3nlLk65vZA==} + '@ark/schema@0.56.1': + resolution: {integrity: sha512-1Cf2g9nKD8K/3JGRu+gCCfYw5d4qR8YLLjDs5W5kpmaButCYWAPFUJqSXyBATPjglzCd4tIkp398iPYVs8MjRA==} - '@ark/util@0.56.0': - resolution: {integrity: sha512-BghfRC8b9pNs3vBoDJhcta0/c1J1rsoS1+HgVUreMFPdhz/CRAKReAu57YEllNaSy98rWAdY1gE+gFup7OXpgA==} + '@ark/util@0.56.1': + resolution: {integrity: sha512-Tp1rTik3q5Z+jAeeDxr5JZpmVIw0miti1ykSEHyZv5Pw3TIJl2xbN6KTacOxITp0l3s9ytlrWd30Zvqcy5vzoQ==} '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} @@ -252,8 +267,8 @@ packages: engines: {node: '>=6.0.0'} hasBin: true - '@babel/runtime@7.29.2': - resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} '@babel/template@7.29.7': @@ -478,25 +493,11 @@ packages: '@floating-ui/utils@0.2.11': resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} - '@hapi/address@5.1.1': - resolution: {integrity: sha512-A+po2d/dVoY7cYajycYI43ZbYMXukuopIsqCjh5QzsBCipDtdofHntljDlpccMjIfTy6UOkg+5KPriwYch2bXA==} - engines: {node: '>=14.0.0'} + '@hapi/hoek@9.3.0': + resolution: {integrity: sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==} - '@hapi/formula@3.0.2': - resolution: {integrity: sha512-hY5YPNXzw1He7s0iqkRQi+uMGh383CGdyyIGYtB+W5N3KHPXoqychklvHhKCC9M3Xtv0OCs/IHw+r4dcHtBYWw==} - - '@hapi/hoek@11.0.7': - resolution: {integrity: sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ==} - - '@hapi/pinpoint@2.0.1': - resolution: {integrity: sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==} - - '@hapi/tlds@1.1.7': - resolution: {integrity: sha512-MgNjRwy9Ti92yVAixLmDc8dd1bJIKwO9qlWCfFQRwRmUEDPQHYn4G6hwPFvFGUTzAa0FsS+inMjLin7GnyBRhA==} - engines: {node: '>=14.0.0'} - - '@hapi/topo@6.0.2': - resolution: {integrity: sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==} + '@hapi/topo@5.1.0': + resolution: {integrity: sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==} '@humanfs/core@0.19.2': resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} @@ -706,9 +707,6 @@ packages: resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/source-map@0.3.11': - resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} - '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} @@ -722,8 +720,8 @@ packages: '@lix-js/server-protocol-schema@0.1.1': resolution: {integrity: sha512-jBeALB6prAbtr5q4vTuxnRZZv1M2rKe8iNqRQhFJ4Tv7150unEa0vKyz0hs8Gl3fUGsWaNJBh3J8++fpbrpRBQ==} - '@lucide/svelte@1.21.0': - resolution: {integrity: sha512-MEv//A7Jv3kHukZowv/DWp1MAtUzJKYwtJsmnQ7X98lCgtac3z3NbaToDl3Q6jO3gS9sougFpcD+t+YuxOkRMw==} + '@lucide/svelte@1.23.0': + resolution: {integrity: sha512-3LQbKXx9vId6Nx4E2Nu2qwgJfdmr5+CVeVJbxe5cy+HcnCRd9QVVtZXqvgBYAV1OJrPmQAf9/3gJWLCpASC/Ng==} peerDependencies: svelte: ^5 @@ -733,61 +731,151 @@ packages: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 - '@next/env@16.2.6': - resolution: {integrity: sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==} + '@next/env@16.2.10': + resolution: {integrity: sha512-zLPxg9M0MEHmygpj5OuxjQ+vHMiy/K7cSp74G8ecYolmgUWw0RwN02tF56npup/+qaI8JB97hQgS/r2Hb6QwVA==} - '@next/swc-darwin-arm64@16.2.6': - resolution: {integrity: sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==} + '@next/swc-darwin-arm64@16.2.10': + resolution: {integrity: sha512-v9IdJCa0H0mbo+8z5zwUpOk1Vj7RjkcI5uNYf5Ws1y6szf/p3Mzl9hLaST8SCt6L9h8NGnruZcd2+o0NTNwDhA==} engines: {node: '>= 10'} cpu: [arm64] os: [darwin] - '@next/swc-darwin-x64@16.2.6': - resolution: {integrity: sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ==} + '@next/swc-darwin-x64@16.2.10': + resolution: {integrity: sha512-17IS0jJRViROGmA9uGdNR8VPJpfbnaVG7E9qhso5jDLkmyd0lSDORWxbcKINzcFqzZqGwGtMSnrFRxBpuUYjLQ==} engines: {node: '>= 10'} cpu: [x64] os: [darwin] - '@next/swc-linux-arm64-gnu@16.2.6': - resolution: {integrity: sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w==} + '@next/swc-linux-arm64-gnu@16.2.10': + resolution: {integrity: sha512-GRQRsRtuciNJvB54AvvuQTiq0oZtFwa1owQqtZD8wwnGpM2L39MV22kpI72YSXLKIyY40LC66EiLFv4PiicXxg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [glibc] - '@next/swc-linux-arm64-musl@16.2.6': - resolution: {integrity: sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==} + '@next/swc-linux-arm64-musl@16.2.10': + resolution: {integrity: sha512-zkN9MQYS7UQBro+FnISUq1itaQjXI9xqISzuQ+2bc921NcJ1x4yPCqrn77tVN6/dOOXaaWVX3k6/bR07pPwK+A==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] libc: [musl] - '@next/swc-linux-x64-gnu@16.2.6': - resolution: {integrity: sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw==} + '@next/swc-linux-x64-gnu@16.2.10': + resolution: {integrity: sha512-iCVJnwvrPYECvA6WM/7+oo+OiTvedIKLxtCLAZP4xZR3nXa1zmzZyLPbYCmWvpd4CvMYF1EMTafd0ii3DygLvA==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [glibc] - '@next/swc-linux-x64-musl@16.2.6': - resolution: {integrity: sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g==} + '@next/swc-linux-x64-musl@16.2.10': + resolution: {integrity: sha512-ov2g4H0dHY9bPoOU83m91hWT7Iq5qy13bUnyyshLU3HGR1Ownn0X9QpmDPc5iIUaahTp7f7LeGAhV4DSFtackw==} engines: {node: '>= 10'} cpu: [x64] os: [linux] libc: [musl] - '@next/swc-win32-arm64-msvc@16.2.6': - resolution: {integrity: sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==} + '@next/swc-win32-arm64-msvc@16.2.10': + resolution: {integrity: sha512-DwAnhLX76HQiFFQNgWlcK+JzlnD1rZ+UK/WY0ZMI/deXpvgnesjNYrqcfo1JzBuz4Kf7o3brIBL0glI1junatA==} engines: {node: '>= 10'} cpu: [arm64] os: [win32] - '@next/swc-win32-x64-msvc@16.2.6': - resolution: {integrity: sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA==} + '@next/swc-win32-x64-msvc@16.2.10': + resolution: {integrity: sha512-0JXq3b85Jk9Jg4ntLUbXSPvoDw3gpZou7twuKdoFG2jOw635v7+IiXfTaa0TxVMyx78pUjnrVYwLgjKfX4e6/A==} engines: {node: '>= 10'} cpu: [x64] os: [win32] + '@opentelemetry/api-logs@0.219.0': + resolution: {integrity: sha512-FFx7YnaYJlIjqWW/AG/yAZ0L/NEY724PipXXXQLdtZPbLwBGbUMTGL1i/esI56TWfTUXxhLfpgrnWJCG8aUJyg==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/api@1.9.1': + resolution: {integrity: sha512-gLyJlPHPZYdAk1JENA9LeHejZe1Ti77/pTeFm/nMXmQH/HFZlcS/O2XJB+L8fkbrNSqhdtlvjBVjxwUYanNH5Q==} + engines: {node: '>=8.0.0'} + + '@opentelemetry/core@2.8.0': + resolution: {integrity: sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/core@2.9.0': + resolution: {integrity: sha512-m2nckMT80NnmjTYSPjJQObBJ+8dgkoajEOUbznL8AHZ3T3yHRk2P7gI1PhEBc1+lOnrYE9UWrWHqJDsmqjmNbw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/exporter-trace-otlp-http@0.219.0': + resolution: {integrity: sha512-9t6SvBXXBEjOBcIzgozvBbd3jWrv3Gt3ngGhl1fhdZ/zRc7oZDVOFEqbi2zlBpW9BXhgDMKv422J0DL/3iQWfw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-exporter-base@0.219.0': + resolution: {integrity: sha512-zvIxQX/AZUVKDU+hCuYx+7UkiP7GRdnk1ZbFQRYzHvYp47cAWR4j3IhoPhV9KaeXEv2xdGq3IA6PnpzDmLcmSA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/otlp-transformer@0.219.0': + resolution: {integrity: sha512-aaYKAyXhw9VchKZVGOopD3Gw/kPsyrX2c6IQ0AW32mTjqmZOh5Y6Gf5OYqTNqVktAeBjmFinhyFaCwW6GYK9YQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': ^1.3.0 + + '@opentelemetry/resources@2.8.0': + resolution: {integrity: sha512-qmXQ27ilDbUK/vGMqwL8D4/rhn76C+sherM4wTbjlfknR8Nvfc/hCxjRJPhkzZzUsPiNg16SA31NxMabwttRjg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/resources@2.9.0': + resolution: {integrity: sha512-jyA5MBLQ+Dkl3+JsZkUoUvL7yHvU64kLsvpXKarWm6347Sl1t1bXFTFykUePNpT5WH5pm9a2Qtt03iIYQhZ1Fg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-logs@0.219.0': + resolution: {integrity: sha512-s6lTKRakaPClvKoWHRChxnXjDMkM/TQ30ff78jN6EBGf7MI7VzANE5PU3f4z9qDUudWjvZjOLHG0rBnBKYvoXA==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.4.0 <1.10.0' + + '@opentelemetry/sdk-metrics@2.8.0': + resolution: {integrity: sha512-UDBGaj6W0Rgy5rTTaoxs8gVGF/aGkAKyjurJv7se6wjRxJu7FoquTLT/vt54DZfo4crbprYfhX/SOK9+BPw1qg==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.9.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@2.8.0': + resolution: {integrity: sha512-mhU4jp+vW0mGbFRd+GeXHvmfA4aDqWjBjLC3pE5XMpLs0IE2ryYb019Ts2AQrOq67gaTF25D91+fgvEHDZEnuQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace-base@2.9.0': + resolution: {integrity: sha512-cp9zmTl62R8PJrpvFcmc8N2JQU/xfa0S+61q511Nji+QxCfZ8Ifvg7H27G8cANe4crg4RTrWsVvanHiXjSp6ag==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/sdk-trace-web@2.9.0': + resolution: {integrity: sha512-LS4XlzOK3e6YYdt84m15AmRR04121rKipmifi4XFZToH1h75f7F2bzHLbB1NMgIEYJ0jSKYm4VsK5mws/5kfTQ==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.0.0 <1.10.0' + + '@opentelemetry/sdk-trace@2.9.0': + resolution: {integrity: sha512-sGA19HvtrrSKYsseHphluH6j3p6Xa3fqc7c7y8f/7mYWejc1lyDFcpSdD1kYa50HCLUeEo4zA5bW0pniaPszuw==} + engines: {node: ^18.19.0 || >=20.6.0} + peerDependencies: + '@opentelemetry/api': '>=1.3.0 <1.10.0' + + '@opentelemetry/semantic-conventions@1.41.1': + resolution: {integrity: sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==} + engines: {node: '>=14'} + '@oxc-project/types@0.138.0': resolution: {integrity: sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA==} @@ -1093,6 +1181,15 @@ packages: '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} + '@sideway/address@4.1.5': + resolution: {integrity: sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==} + + '@sideway/formula@3.0.1': + resolution: {integrity: sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==} + + '@sideway/pinpoint@2.0.0': + resolution: {integrity: sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==} + '@simplewebauthn/browser@13.3.0': resolution: {integrity: sha512-BE/UWv6FOToAdVk0EokzkqQQDOWtNydYlY6+OrmiZ5SCNmb41VehttboTetUM3T/fr6EAFYVXjz4My2wg230rQ==} @@ -1119,8 +1216,8 @@ packages: peerDependencies: '@sveltejs/kit': ^2.0.0 - '@sveltejs/kit@2.68.0': - resolution: {integrity: sha512-PdKiWsqinAoubVsSiRgVFkg3MHzGhQPnwQ8VxnGQKpZYijpapZ3UHHBje0GeByt2TvfjHPw+kxV+dNK2RIZg9g==} + '@sveltejs/kit@2.69.1': + resolution: {integrity: sha512-+nz8Fx/cElzb2ZPHC+6Ll3y3NEVIe4Na75PeplLlyTmd1dBXAjz2KR14y1ZgNjb2ThfAYzulu+PFy1UE3RCUzA==} engines: {node: '>=18.13'} hasBin: true peerDependencies: @@ -1152,69 +1249,69 @@ packages: '@swc/helpers@0.5.23': resolution: {integrity: sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==} - '@tailwindcss/node@4.3.1': - resolution: {integrity: sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==} + '@tailwindcss/node@4.3.2': + resolution: {integrity: sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==} - '@tailwindcss/oxide-android-arm64@4.3.1': - resolution: {integrity: sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==} + '@tailwindcss/oxide-android-arm64@4.3.2': + resolution: {integrity: sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==} engines: {node: '>= 20'} cpu: [arm64] os: [android] - '@tailwindcss/oxide-darwin-arm64@4.3.1': - resolution: {integrity: sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==} + '@tailwindcss/oxide-darwin-arm64@4.3.2': + resolution: {integrity: sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==} engines: {node: '>= 20'} cpu: [arm64] os: [darwin] - '@tailwindcss/oxide-darwin-x64@4.3.1': - resolution: {integrity: sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==} + '@tailwindcss/oxide-darwin-x64@4.3.2': + resolution: {integrity: sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==} engines: {node: '>= 20'} cpu: [x64] os: [darwin] - '@tailwindcss/oxide-freebsd-x64@4.3.1': - resolution: {integrity: sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==} + '@tailwindcss/oxide-freebsd-x64@4.3.2': + resolution: {integrity: sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==} engines: {node: '>= 20'} cpu: [x64] os: [freebsd] - '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': - resolution: {integrity: sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==} + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2': + resolution: {integrity: sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==} engines: {node: '>= 20'} cpu: [arm] os: [linux] - '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': - resolution: {integrity: sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==} + '@tailwindcss/oxide-linux-arm64-gnu@4.3.2': + resolution: {integrity: sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-arm64-musl@4.3.1': - resolution: {integrity: sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==} + '@tailwindcss/oxide-linux-arm64-musl@4.3.2': + resolution: {integrity: sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==} engines: {node: '>= 20'} cpu: [arm64] os: [linux] libc: [musl] - '@tailwindcss/oxide-linux-x64-gnu@4.3.1': - resolution: {integrity: sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==} + '@tailwindcss/oxide-linux-x64-gnu@4.3.2': + resolution: {integrity: sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==} engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [glibc] - '@tailwindcss/oxide-linux-x64-musl@4.3.1': - resolution: {integrity: sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==} + '@tailwindcss/oxide-linux-x64-musl@4.3.2': + resolution: {integrity: sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==} engines: {node: '>= 20'} cpu: [x64] os: [linux] libc: [musl] - '@tailwindcss/oxide-wasm32-wasi@4.3.1': - resolution: {integrity: sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==} + '@tailwindcss/oxide-wasm32-wasi@4.3.2': + resolution: {integrity: sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==} engines: {node: '>=14.0.0'} cpu: [wasm32] bundledDependencies: @@ -1225,24 +1322,24 @@ packages: - '@emnapi/wasi-threads' - tslib - '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': - resolution: {integrity: sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==} + '@tailwindcss/oxide-win32-arm64-msvc@4.3.2': + resolution: {integrity: sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==} engines: {node: '>= 20'} cpu: [arm64] os: [win32] - '@tailwindcss/oxide-win32-x64-msvc@4.3.1': - resolution: {integrity: sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==} + '@tailwindcss/oxide-win32-x64-msvc@4.3.2': + resolution: {integrity: sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ==} engines: {node: '>= 20'} cpu: [x64] os: [win32] - '@tailwindcss/oxide@4.3.1': - resolution: {integrity: sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==} + '@tailwindcss/oxide@4.3.2': + resolution: {integrity: sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==} engines: {node: '>= 20'} - '@tailwindcss/vite@4.3.1': - resolution: {integrity: sha512-hItDHuIIlEV61R+faXu66s1K36aTurO/Qw0e45Vskz57gXl9pWOT6eg3zmcEui6CZXddbN7zd41bwmvag4JGwQ==} + '@tailwindcss/vite@4.3.2': + resolution: {integrity: sha512-eHpMeX4JXfVNJDEcsouTeCBubJBTcTLigeaw/NTUW6PB5ATKKXdyonnXgTBX2VuRbjz1hjfz6C5XAhr52ImQXA==} peerDependencies: vite: ^5.2.0 || ^6 || ^7 || ^8 @@ -1306,67 +1403,67 @@ packages: '@types/json-schema': optional: true - '@typescript-eslint/eslint-plugin@8.62.0': - resolution: {integrity: sha512-o+mpz7EYiMzXoySXiKmzlabIvTVqUuK5yLrAedRPRDA0IpPFMUV1IXt6OqljIxX/kumN6EjUYp41Hqelh6p/Dw==} + '@typescript-eslint/eslint-plugin@8.62.1': + resolution: {integrity: sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.62.0 + '@typescript-eslint/parser': ^8.62.1 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.62.0': - resolution: {integrity: sha512-dzHeT2gySzZtLDsuqxU9AkYgIsQoHAHtRBpOqM+Ofzx1Bwrd2RcCjQJ+6iQbsHOIR6NS33bF2W1k3blN1zLDrA==} + '@typescript-eslint/parser@8.62.1': + resolution: {integrity: sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.62.0': - resolution: {integrity: sha512-wexnCqiTg7BOGtbLDftYpRWlmLq4xfoMd7BKFR6Y75sZS3QmRKLdN3yWLhmIYgqMmP/OXWpj3H8odkb5nGURCQ==} + '@typescript-eslint/project-service@8.62.1': + resolution: {integrity: sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.62.0': - resolution: {integrity: sha512-1lX38kNxXIRb8mEc3lbq5mdHq1Pf2+U0nFU65KfT18mtPxxl0fvjuEE92mHuXPuCtElJhOrddOpyMlM3Z0umEA==} + '@typescript-eslint/scope-manager@8.62.1': + resolution: {integrity: sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.62.0': - resolution: {integrity: sha512-y2GAdB6ykaXUvuspbYnizQc4oDDz0Tz/Yc7iWrXf9mx8vm/L/0vLHCe0tS2boG96Zy+DivnVDQ9ZUEWoHqqx1g==} + '@typescript-eslint/tsconfig-utils@8.62.1': + resolution: {integrity: sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.62.0': - resolution: {integrity: sha512-+g5O3j0w2ldzC86Pv6fvbO/xhAonbJFIdf/MKQ1d30gndlsVzUOE83ldfSE15Qrl9fhFjK6AovHs5Wpp6vx86w==} + '@typescript-eslint/type-utils@8.62.1': + resolution: {integrity: sha512-aXM5xlqXiTxPibXB93cLAURfT3rlizf7uMXISCXy66Isr/9hISJx3yDsKl0L7lKa51b8JpFuNKby0/O0pEm9jg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.62.0': - resolution: {integrity: sha512-KvAclkktORPvM54TgLgA4z9HIV1M8zOgw9ZVNXl9f/8dLYfXYX1wkMXP7qmabpijQRV5bHJLOmoyGQbLMaUYeg==} + '@typescript-eslint/types@8.62.1': + resolution: {integrity: sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.62.0': - resolution: {integrity: sha512-+hVbNxtW64pIcZWDPGbyaKF7vp2IBTVY5ma1blwwksrjdsbdqqEKvJWMGbBofei4F6Dovx1M0RJgoFeNu2279A==} + '@typescript-eslint/typescript-estree@8.62.1': + resolution: {integrity: sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.62.0': - resolution: {integrity: sha512-82r66fi9zYwZ+mTq3vKgwjbZ1PVk/DJzrXFLpG6RnBbdvH8TEGVHIs9H4d2drhkOzf0syZuD/OZvvlu6GDbP4g==} + '@typescript-eslint/utils@8.62.1': + resolution: {integrity: sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.62.0': - resolution: {integrity: sha512-CY3uyFSRbcQv3nnSv8S0+lDftMVz6P963PoRlxrV7ew/Md564g9ut60PYzdLM5qW4jFn93GBF+Soi90ISAN+GQ==} + '@typescript-eslint/visitor-keys@8.62.1': + resolution: {integrity: sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@valibot/to-json-schema@1.7.0': - resolution: {integrity: sha512-Y3pPVibbIOHzohrlxSINvO7w/bvXkoYS3BQHoImV9ynE+bXKf171bdMucPurV2zp7gdmt0L1HCcNAsbo7cFRQw==} + '@valibot/to-json-schema@1.7.1': + resolution: {integrity: sha512-3qkmU6KXWh8GIThEAW3kuRHPQBMjWkKy+Ppz3WkUucx53DTpOa6siMn4xDGSOhlVyMrDaJTCTMLYPZVAIk1P0A==} peerDependencies: valibot: ^1.4.0 @@ -1392,8 +1489,8 @@ packages: engines: {node: '>=0.4.0'} hasBin: true - adm-zip@0.5.17: - resolution: {integrity: sha512-+Ut8d9LLqwEvHHJl1+PIHqoyDxFgVN847JTVM3Izi3xHDWPE4UtzzXysMZQs64DMcrJfBeS/uoEP4AD3HQHnQQ==} + adm-zip@0.5.18: + resolution: {integrity: sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==} engines: {node: '>=12.0'} agent-base@6.0.2: @@ -1426,11 +1523,11 @@ packages: resolution: {integrity: sha512-Z/ZeOgVl7bcSYZ/u/rh0fOpvEpq//LZmdbkXyc7syVzjPAhfOa9ebsdTSjEBDU4vs5nC98Kfduj1uFo0qyET3g==} engines: {node: '>= 0.4'} - arkregex@0.0.5: - resolution: {integrity: sha512-ncYjBdLlh5/QnVsAA8De16Tc9EqmYM7y/WU9j+236KcyYNUXogpz3sC4ATIZYzzLxwI+0sEOaQLEmLmRleaEXw==} + arkregex@0.0.7: + resolution: {integrity: sha512-O/Ltrn9EUSn3ui0KVzfyrWGDUsHlzKxDVBtpQxL/6JmLRMAZAebfSNf/A/J5Ny5S6QIwrXX+RfXsu888HMs35A==} - arktype@2.2.0: - resolution: {integrity: sha512-t54MZ7ti5BhOEvzEkgKnWvqj+UbDfWig+DHr5I34xatymPusKLS0lQpNJd8M6DzmIto2QGszHfNKoFIT8tMCZQ==} + arktype@2.2.2: + resolution: {integrity: sha512-YYf1xhL2dh5aPZFlsY0RAsxv5HZqfLGLptH2ZP3JidTmsGRW8VOymhPjjMTkerL12vR2YtX0SK4c1mATtae8SA==} array-timsort@1.0.3: resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} @@ -1456,8 +1553,8 @@ packages: resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==} engines: {node: ^4.5.0 || >= 5.9} - baseline-browser-mapping@2.10.31: - resolution: {integrity: sha512-MujYO3eP72uvmSE0i4wltsodRfIpZATP3jvzRNRGGxgzId7aVocVJJV3nf01qnzzKFGxQVC9bpWxl5cjxTr/7Q==} + baseline-browser-mapping@2.10.42: + resolution: {integrity: sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==} engines: {node: '>=6.0.0'} hasBin: true @@ -1472,9 +1569,6 @@ packages: resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} engines: {node: 18 || 20 || >=22} - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -1487,8 +1581,8 @@ packages: resolution: {integrity: sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==} engines: {node: '>=16'} - caniuse-lite@1.0.30001793: - resolution: {integrity: sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==} + caniuse-lite@1.0.30001800: + resolution: {integrity: sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -1537,9 +1631,6 @@ packages: resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} engines: {node: '>=20'} - commander@2.20.3: - resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} - comment-json@4.6.2: resolution: {integrity: sha512-R2rze/hDX30uul4NZoIZ76ImSJLFxn/1/ZxtKC1L77y2X1k+yYu1joKbAtMA2Fg3hZrTOiw0I5mwVMo0cf250w==} engines: {node: '>= 6'} @@ -1579,8 +1670,8 @@ packages: date-fns@4.4.0: resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==} - dayjs@1.11.20: - resolution: {integrity: sha512-YbwwqR/uYpeoP4pu043q+LTDLFBLApUP6VxRihdfNTqu4ubqMlGDLd6ErXhEgsyvY0K6nCs7nggYumAN+9uEuQ==} + dayjs@1.11.21: + resolution: {integrity: sha512-98IT+HOahAisibz/yjKbzuOBwYcjJ7BCLPzARyHiyEBmRz4fatF+KPJszEHXsGYjUG234aH/cOjW1wwTbKUZlA==} debounce-fn@6.0.0: resolution: {integrity: sha512-rBMW+F2TXryBwB54Q0d8drNEI+TfoS9JpNTAoVpukbWEhjXQq4rySFYLaqXMFXwdv61Zb2OHtj5bviSoimqxRQ==} @@ -1664,8 +1755,8 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - effect@3.21.2: - resolution: {integrity: sha512-rXd2FGDM8KdjSIrc+mqEELo7ScW7xTVxEf1iInmPSpIde9/nyGuFM710cjTo7/EreGXiUX2MOonPpprbz2XHCg==} + effect@3.21.4: + resolution: {integrity: sha512-B89v/xSgPbl1J2Ai2u18jxq3odpFauU1rC6/eSs4FeNHi72kwKdJp12VGigvRV2lK+kRnx+OOz41XV8guZd4gQ==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1751,8 +1842,8 @@ packages: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} - eslint@10.5.0: - resolution: {integrity: sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==} + eslint@10.6.0: + resolution: {integrity: sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: @@ -2002,9 +2093,8 @@ packages: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true - joi@18.2.3: - resolution: {integrity: sha512-N5A3KTWQpPWT4ExxxPlUx7WmykGXRzhNidWhV41d6Abu9YfI2NyWCJuxdPnslJCPWtbRpSVOWSnSS6GakLM/Rg==} - engines: {node: '>= 20'} + joi@17.13.4: + resolution: {integrity: sha512-1RuuER6kmt8K8I3nIWvPZKi5RQCb568ZPyY4Pwjlua+yo+63ZTmIwxLZH0heBmiKN4uxjvCiarDrjaeH84xicQ==} jose@6.2.3: resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} @@ -2072,8 +2162,8 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} - libphonenumber-js@1.13.2: - resolution: {integrity: sha512-S3kmBrptp3yRTm83NUcHy9g1vbwiWMzI8WvY22+koBJ6zkRteLnedBL2VX0MIAGwx2yiyxX4J85pceZyQ6ffgg==} + libphonenumber-js@1.13.8: + resolution: {integrity: sha512-80xal1m93rADejw2pMp2MSzFhHCPLEspjHxnH2UtqI+DgAmElsbmLMiqk9niwH9NWAfjsRtaJI+qBrOEmRx9nQ==} lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} @@ -2241,11 +2331,6 @@ packages: ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - nanoid@3.3.12: - resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - nanoid@3.3.15: resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2258,8 +2343,8 @@ packages: resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} engines: {node: '>= 0.6'} - next@16.2.6: - resolution: {integrity: sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==} + next@16.2.10: + resolution: {integrity: sha512-2som5AVXb3kE6Yjine3/mNbBayYF58eguBWIVVUdr1y/L426xyVEgYxgBG+1QC34P2x5E+tcDup6XkuOAX3dCA==} engines: {node: '>=20.9.0'} hasBin: true peerDependencies: @@ -2299,8 +2384,9 @@ packages: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} - obug@2.1.1: - resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + obug@2.1.3: + resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} + engines: {node: '>=12.20.0'} optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} @@ -2400,10 +2486,6 @@ packages: resolution: {integrity: sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg==} engines: {node: '>=4'} - postcss@8.5.15: - resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} - engines: {node: ^10 || ^12 || >=14} - postcss@8.5.16: resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} engines: {node: ^10 || ^12 || >=14} @@ -2473,11 +2555,6 @@ packages: prettier-plugin-svelte: optional: true - prettier@3.8.5: - resolution: {integrity: sha512-zxcTTCedNGJM4R8sj/Cq/F0W/c4iE0afWBcBwMTRtw4WHYP9TWkYjdiH3npPRUYsXQCPR0hTU9yjovOu+E6EQA==} - engines: {node: '>=14'} - hasBin: true - prettier@3.9.4: resolution: {integrity: sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==} engines: {node: '>=14'} @@ -2644,13 +2721,6 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - sqlite-wasm-kysely@0.3.0: resolution: {integrity: sha512-TzjBNv7KwRw6E3pdKdlRyZiTmUIE0UttT/Sl56MVwVARl/u5gp978KepazCJZewFUnlWHz9i3NQd4kOtP/Afdg==} peerDependencies: @@ -2742,14 +2812,14 @@ packages: resolution: {integrity: sha512-/d0QHehmRuJW8gVz395MTkPcPozxzdjBMBE8oEYGz8O3b9KTMzzQ9ZHJQLuFKOHOPQbU6kx/X4iid/EBBzH7iw==} engines: {node: '>=18'} - sveltekit-superforms@2.30.1: - resolution: {integrity: sha512-wBzyqsE0idvEJWuNJ+HCiAtdxa7Z55GZ8jmtlVHJfonrk9bRYC49MoPaloYyFoYuU3QPy6Omna/Qzn1kaIkgew==} + sveltekit-superforms@2.30.2: + resolution: {integrity: sha512-6sR70ZfjFMAfdNure/Bu26o1rY32+WGxebko1L8jUFS+qZw5fxIHBPkUaTpNBkOIlJZ/UJwoNCYCm5xK2Z7Kqw==} peerDependencies: '@sveltejs/kit': 1.x || 2.x svelte: 3.x || 4.x || >=5.0.0-next.51 - tabbable@6.4.0: - resolution: {integrity: sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==} + tabbable@6.5.0: + resolution: {integrity: sha512-wieBHXygIm7OyQOu5hQlkk62/WyCFYGlWg7L6/ZCUZwx0o398Zkn4pVmMyfYhfMG8kGrj/Krt8eIk6UKC6VzwA==} tagged-tag@1.0.0: resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} @@ -2768,9 +2838,6 @@ packages: tailwind-merge: optional: true - tailwindcss@4.3.1: - resolution: {integrity: sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==} - tailwindcss@4.3.2: resolution: {integrity: sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==} @@ -2778,11 +2845,6 @@ packages: resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} - terser@5.44.0: - resolution: {integrity: sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==} - engines: {node: '>=10'} - hasBin: true - tiny-case@1.0.3: resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==} @@ -2821,8 +2883,8 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsx@4.22.4: - resolution: {integrity: sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==} + tsx@4.23.0: + resolution: {integrity: sha512-eUdUIaCr963q2h5u3+QwvYp0+eqPvn+egeqZUm0hwERCqqx1E3kK5ehbGCvqSE5MQAULr67ww0cA3jKc3YkM1w==} engines: {node: '>=18.0.0'} hasBin: true @@ -2841,11 +2903,11 @@ packages: resolution: {integrity: sha512-1URUxUqfHFM1c+zfSPsa3gnkO7Aq21qyH75SIduNYz4SzY964rn1X2vCMQaHSHhktiw+0kPa2iyb6PUpXqB6Vg==} engines: {node: '>=20'} - typebox@1.1.38: - resolution: {integrity: sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==} + typebox@1.3.3: + resolution: {integrity: sha512-URXGUE31PJDQC+PtRMJeLdF4kmmOdFoVPikPCtV2oOIhUpNpppEdIz7W8bH8cFYPYHdDpaRvqwdegMTmHliudg==} - typescript-eslint@8.62.0: - resolution: {integrity: sha512-8QxXi+ZACKX0kaqO4gY8kn0RSD9gFfaHDWwjqtEN48aWCBkX4MJaufWN+c3BzlrXLOxfywDL8CaoqUwcRq4j4Q==} + typescript-eslint@8.62.1: + resolution: {integrity: sha512-vymnnM5g0AKQDSAyfP12nMIBvgwgA42syg74kkuZ4x1VuTzwQKwc5h9rGxeShCjny5o+zWAb6OEoz7XLgrIkIw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -2884,8 +2946,8 @@ packages: resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} hasBin: true - valibot@1.4.0: - resolution: {integrity: sha512-iC/x7fVcSyOwlm/VSt7RlHnzNGLGvR9GnxdifUeWoCJo0q4ZZvrVkIHC6faTlkxG47I2Y4UrFquPuVHCrOnrLg==} + valibot@1.4.2: + resolution: {integrity: sha512-gjdCvJ6d3RyHAneqxMYMW9QMCwYMb3jpOO0IyHZV1bnRHFBHrX3VkIILt5XYR0WhwHiH7Mty8ovuPZ/O3gamrg==} peerDependencies: typescript: '>=5' peerDependenciesMeta: @@ -2905,8 +2967,8 @@ packages: peerDependencies: vite: '>=2.0.0' - vite@8.1.0: - resolution: {integrity: sha512-BuJcQK/56NQTWDGn4ABea3q4SSBdNPWwNZKTkkUpcMPnLoquSYH8llRtSUIgoL1KSCpHt5eghLShn50mH36y7Q==} + vite@8.1.3: + resolution: {integrity: sha512-Ds+gBRbj0lwRO2Y5hwnUBdxSwlAve9LeRyU4sNnAr0ewW0gWF0n5bgXgUzbgZ49MV9BVUAQUFYVcDUcilUExMA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3030,12 +3092,12 @@ packages: snapshots: - '@ark/schema@0.56.0': + '@ark/schema@0.56.1': dependencies: - '@ark/util': 0.56.0 + '@ark/util': 0.56.1 optional: true - '@ark/util@0.56.0': + '@ark/util@0.56.1': optional: true '@babel/code-frame@7.29.7': @@ -3064,7 +3126,7 @@ snapshots: dependencies: '@babel/types': 7.29.7 - '@babel/runtime@7.29.2': + '@babel/runtime@7.29.7': optional: true '@babel/template@7.29.7': @@ -3077,7 +3139,7 @@ snapshots: dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 - '@babel/parser': 7.29.7 + '@babel/parser': 7.27.0 '@babel/template': 7.29.7 '@babel/types': 7.29.7 debug: 4.4.3 @@ -3189,9 +3251,9 @@ snapshots: '@esbuild/win32-x64@0.28.1': optional: true - '@eslint-community/eslint-utils@4.9.1(eslint@10.5.0(jiti@2.7.0))': + '@eslint-community/eslint-utils@4.9.1(eslint@10.6.0(jiti@2.7.0))': dependencies: - eslint: 10.5.0(jiti@2.7.0) + eslint: 10.6.0(jiti@2.7.0) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -3233,26 +3295,12 @@ snapshots: '@floating-ui/utils@0.2.11': {} - '@hapi/address@5.1.1': + '@hapi/hoek@9.3.0': + optional: true + + '@hapi/topo@5.1.0': dependencies: - '@hapi/hoek': 11.0.7 - optional: true - - '@hapi/formula@3.0.2': - optional: true - - '@hapi/hoek@11.0.7': - optional: true - - '@hapi/pinpoint@2.0.1': - optional: true - - '@hapi/tlds@1.1.7': - optional: true - - '@hapi/topo@6.0.2': - dependencies: - '@hapi/hoek': 11.0.7 + '@hapi/hoek': 9.3.0 optional: true '@humanfs/core@0.19.2': @@ -3422,12 +3470,6 @@ snapshots: '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/source-map@0.3.11': - dependencies: - '@jridgewell/gen-mapping': 0.3.13 - '@jridgewell/trace-mapping': 0.3.31 - optional: true - '@jridgewell/sourcemap-codec@1.5.5': {} '@jridgewell/trace-mapping@0.3.31': @@ -3449,9 +3491,9 @@ snapshots: '@lix-js/server-protocol-schema@0.1.1': {} - '@lucide/svelte@1.21.0(svelte@5.56.4(@typescript-eslint/types@8.62.0))': + '@lucide/svelte@1.23.0(svelte@5.56.4(@typescript-eslint/types@8.62.1))': dependencies: - svelte: 5.56.4(@typescript-eslint/types@8.62.0) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: @@ -3460,32 +3502,129 @@ snapshots: '@tybys/wasm-util': 0.10.3 optional: true - '@next/env@16.2.6': {} + '@next/env@16.2.10': {} - '@next/swc-darwin-arm64@16.2.6': + '@next/swc-darwin-arm64@16.2.10': optional: true - '@next/swc-darwin-x64@16.2.6': + '@next/swc-darwin-x64@16.2.10': optional: true - '@next/swc-linux-arm64-gnu@16.2.6': + '@next/swc-linux-arm64-gnu@16.2.10': optional: true - '@next/swc-linux-arm64-musl@16.2.6': + '@next/swc-linux-arm64-musl@16.2.10': optional: true - '@next/swc-linux-x64-gnu@16.2.6': + '@next/swc-linux-x64-gnu@16.2.10': optional: true - '@next/swc-linux-x64-musl@16.2.6': + '@next/swc-linux-x64-musl@16.2.10': optional: true - '@next/swc-win32-arm64-msvc@16.2.6': + '@next/swc-win32-arm64-msvc@16.2.10': optional: true - '@next/swc-win32-x64-msvc@16.2.6': + '@next/swc-win32-x64-msvc@16.2.10': optional: true + '@opentelemetry/api-logs@0.219.0': + dependencies: + '@opentelemetry/api': 1.9.1 + + '@opentelemetry/api@1.9.1': {} + + '@opentelemetry/core@2.8.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/semantic-conventions': 1.41.1 + + '@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/semantic-conventions': 1.41.1 + + '@opentelemetry/exporter-trace-otlp-http@0.219.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-exporter-base': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/otlp-exporter-base@0.219.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/otlp-transformer': 0.219.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/otlp-transformer@0.219.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/api-logs': 0.219.0 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-logs': 0.219.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-metrics': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.8.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/resources@2.8.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.41.1 + + '@opentelemetry/resources@2.9.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.41.1 + + '@opentelemetry/sdk-logs@0.219.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/api-logs': 0.219.0 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.41.1 + + '@opentelemetry/sdk-metrics@2.8.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/sdk-trace-base@2.8.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.8.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.41.1 + + '@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.41.1 + + '@opentelemetry/sdk-trace-web@2.9.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) + + '@opentelemetry/sdk-trace@2.9.0(@opentelemetry/api@1.9.1)': + dependencies: + '@opentelemetry/api': 1.9.1 + '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) + '@opentelemetry/semantic-conventions': 1.41.1 + + '@opentelemetry/semantic-conventions@1.41.1': {} + '@oxc-project/types@0.138.0': {} '@playwright/test@1.61.1': @@ -3581,10 +3720,10 @@ snapshots: marked: 15.0.12 react: 19.2.7 - '@react-email/preview-server@5.2.10(@playwright/test@1.61.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + '@react-email/preview-server@5.2.10(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: esbuild: 0.28.1 - next: 16.2.6(@playwright/test@1.61.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + next: 16.2.10(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) transitivePeerDependencies: - '@babel/core' - '@opentelemetry/api' @@ -3698,6 +3837,17 @@ snapshots: domhandler: 5.0.3 selderee: 0.11.0 + '@sideway/address@4.1.5': + dependencies: + '@hapi/hoek': 9.3.0 + optional: true + + '@sideway/formula@3.0.1': + optional: true + + '@sideway/pinpoint@2.0.0': + optional: true + '@simplewebauthn/browser@13.3.0': {} '@sinclair/typebox@0.31.28': {} @@ -3712,15 +3862,15 @@ snapshots: dependencies: acorn: 8.17.0 - '@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))': + '@sveltejs/adapter-static@3.0.10(@sveltejs/kit@2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))': dependencies: - '@sveltejs/kit': 2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)) + '@sveltejs/kit': 2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) - '@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0))': + '@sveltejs/kit@2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0))': dependencies: '@standard-schema/spec': 1.1.0 '@sveltejs/acorn-typescript': 1.0.10(acorn@8.17.0) - '@sveltejs/vite-plugin-svelte': 7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)) + '@sveltejs/vite-plugin-svelte': 7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) '@types/cookie': 0.6.0 acorn: 8.17.0 cookie: 0.7.2 @@ -3731,21 +3881,22 @@ snapshots: mrmime: 2.0.1 set-cookie-parser: 3.1.1 sirv: 3.0.2 - svelte: 5.56.4(@typescript-eslint/types@8.62.0) - vite: 8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) + vite: 8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0) optionalDependencies: + '@opentelemetry/api': 1.9.1 typescript: 6.0.3 '@sveltejs/load-config@0.2.0': {} - '@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0))': + '@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0))': dependencies: deepmerge: 4.3.1 magic-string: 0.30.21 - obug: 2.1.1 - svelte: 5.56.4(@typescript-eslint/types@8.62.0) - vite: 8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0) - vitefu: 1.1.3(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)) + obug: 2.1.3 + svelte: 5.56.4(@typescript-eslint/types@8.62.1) + vite: 8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) '@swc/helpers@0.5.15': dependencies: @@ -3755,7 +3906,7 @@ snapshots: dependencies: tslib: 2.8.1 - '@tailwindcss/node@4.3.1': + '@tailwindcss/node@4.3.2': dependencies: '@jridgewell/remapping': 2.3.5 enhanced-resolve: 5.21.6 @@ -3763,65 +3914,65 @@ snapshots: lightningcss: 1.32.0 magic-string: 0.30.21 source-map-js: 1.2.1 - tailwindcss: 4.3.1 + tailwindcss: 4.3.2 - '@tailwindcss/oxide-android-arm64@4.3.1': + '@tailwindcss/oxide-android-arm64@4.3.2': optional: true - '@tailwindcss/oxide-darwin-arm64@4.3.1': + '@tailwindcss/oxide-darwin-arm64@4.3.2': optional: true - '@tailwindcss/oxide-darwin-x64@4.3.1': + '@tailwindcss/oxide-darwin-x64@4.3.2': optional: true - '@tailwindcss/oxide-freebsd-x64@4.3.1': + '@tailwindcss/oxide-freebsd-x64@4.3.2': optional: true - '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.1': + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2': optional: true - '@tailwindcss/oxide-linux-arm64-gnu@4.3.1': + '@tailwindcss/oxide-linux-arm64-gnu@4.3.2': optional: true - '@tailwindcss/oxide-linux-arm64-musl@4.3.1': + '@tailwindcss/oxide-linux-arm64-musl@4.3.2': optional: true - '@tailwindcss/oxide-linux-x64-gnu@4.3.1': + '@tailwindcss/oxide-linux-x64-gnu@4.3.2': optional: true - '@tailwindcss/oxide-linux-x64-musl@4.3.1': + '@tailwindcss/oxide-linux-x64-musl@4.3.2': optional: true - '@tailwindcss/oxide-wasm32-wasi@4.3.1': + '@tailwindcss/oxide-wasm32-wasi@4.3.2': optional: true - '@tailwindcss/oxide-win32-arm64-msvc@4.3.1': + '@tailwindcss/oxide-win32-arm64-msvc@4.3.2': optional: true - '@tailwindcss/oxide-win32-x64-msvc@4.3.1': + '@tailwindcss/oxide-win32-x64-msvc@4.3.2': optional: true - '@tailwindcss/oxide@4.3.1': + '@tailwindcss/oxide@4.3.2': optionalDependencies: - '@tailwindcss/oxide-android-arm64': 4.3.1 - '@tailwindcss/oxide-darwin-arm64': 4.3.1 - '@tailwindcss/oxide-darwin-x64': 4.3.1 - '@tailwindcss/oxide-freebsd-x64': 4.3.1 - '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.1 - '@tailwindcss/oxide-linux-arm64-gnu': 4.3.1 - '@tailwindcss/oxide-linux-arm64-musl': 4.3.1 - '@tailwindcss/oxide-linux-x64-gnu': 4.3.1 - '@tailwindcss/oxide-linux-x64-musl': 4.3.1 - '@tailwindcss/oxide-wasm32-wasi': 4.3.1 - '@tailwindcss/oxide-win32-arm64-msvc': 4.3.1 - '@tailwindcss/oxide-win32-x64-msvc': 4.3.1 + '@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 - '@tailwindcss/vite@4.3.1(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0))': + '@tailwindcss/vite@4.3.2(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0))': dependencies: - '@tailwindcss/node': 4.3.1 - '@tailwindcss/oxide': 4.3.1 - tailwindcss: 4.3.1 - vite: 8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0) + '@tailwindcss/node': 4.3.2 + '@tailwindcss/oxide': 4.3.2 + tailwindcss: 4.3.2 + vite: 8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0) '@tybys/wasm-util@0.10.3': dependencies: @@ -3883,15 +4034,15 @@ snapshots: '@types/json-schema': 7.0.15 optional: true - '@typescript-eslint/eslint-plugin@8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/eslint-plugin@8.62.1(@typescript-eslint/parser@8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/scope-manager': 8.62.0 - '@typescript-eslint/type-utils': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/utils': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.62.0 - eslint: 10.5.0(jiti@2.7.0) + '@typescript-eslint/parser': 8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.62.1 + '@typescript-eslint/type-utils': 8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.62.1 + eslint: 10.6.0(jiti@2.7.0) ignore: 7.0.5 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@6.0.3) @@ -3899,56 +4050,56 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/parser@8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@typescript-eslint/scope-manager': 8.62.0 - '@typescript-eslint/types': 8.62.0 - '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) - '@typescript-eslint/visitor-keys': 8.62.0 + '@typescript-eslint/scope-manager': 8.62.1 + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.62.1 debug: 4.4.3 - eslint: 10.5.0(jiti@2.7.0) + eslint: 10.6.0(jiti@2.7.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.62.0(typescript@6.0.3)': + '@typescript-eslint/project-service@8.62.1(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@6.0.3) - '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/tsconfig-utils': 8.62.1(typescript@6.0.3) + '@typescript-eslint/types': 8.62.1 debug: 4.4.3 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.62.0': + '@typescript-eslint/scope-manager@8.62.1': dependencies: - '@typescript-eslint/types': 8.62.0 - '@typescript-eslint/visitor-keys': 8.62.0 + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/visitor-keys': 8.62.1 - '@typescript-eslint/tsconfig-utils@8.62.0(typescript@6.0.3)': + '@typescript-eslint/tsconfig-utils@8.62.1(typescript@6.0.3)': dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@typescript-eslint/types': 8.62.0 - '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) debug: 4.4.3 - eslint: 10.5.0(jiti@2.7.0) + eslint: 10.6.0(jiti@2.7.0) ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.62.0': {} + '@typescript-eslint/types@8.62.1': {} - '@typescript-eslint/typescript-estree@8.62.0(typescript@6.0.3)': + '@typescript-eslint/typescript-estree@8.62.1(typescript@6.0.3)': dependencies: - '@typescript-eslint/project-service': 8.62.0(typescript@6.0.3) - '@typescript-eslint/tsconfig-utils': 8.62.0(typescript@6.0.3) - '@typescript-eslint/types': 8.62.0 - '@typescript-eslint/visitor-keys': 8.62.0 + '@typescript-eslint/project-service': 8.62.1(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.62.1(typescript@6.0.3) + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/visitor-keys': 8.62.1 debug: 4.4.3 minimatch: 10.2.5 semver: 7.8.5 @@ -3958,25 +4109,25 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/utils@8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0)) - '@typescript-eslint/scope-manager': 8.62.0 - '@typescript-eslint/types': 8.62.0 - '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) - eslint: 10.5.0(jiti@2.7.0) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.62.1 + '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3) + eslint: 10.6.0(jiti@2.7.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.62.0': + '@typescript-eslint/visitor-keys@8.62.1': dependencies: - '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/types': 8.62.1 eslint-visitor-keys: 5.0.1 - '@valibot/to-json-schema@1.7.0(valibot@1.4.0(typescript@6.0.3))': + '@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@6.0.3))': dependencies: - valibot: 1.4.0(typescript@6.0.3) + valibot: 1.4.2(typescript@6.0.3) optional: true '@vinejs/compiler@3.0.0': @@ -3988,7 +4139,7 @@ snapshots: '@types/validator': 13.15.10 '@vinejs/compiler': 3.0.0 camelcase: 8.0.0 - dayjs: 1.11.20 + dayjs: 1.11.21 dlv: 1.1.3 normalize-url: 8.1.1 validator: 13.15.35 @@ -4005,7 +4156,7 @@ snapshots: acorn@8.17.0: {} - adm-zip@0.5.17: {} + adm-zip@0.5.18: {} agent-base@6.0.2: dependencies: @@ -4039,16 +4190,16 @@ snapshots: aria-query@5.3.1: {} - arkregex@0.0.5: + arkregex@0.0.7: dependencies: - '@ark/util': 0.56.0 + '@ark/util': 0.56.1 optional: true - arktype@2.2.0: + arktype@2.2.2: dependencies: - '@ark/schema': 0.56.0 - '@ark/util': 0.56.0 - arkregex: 0.0.5 + '@ark/schema': 0.56.1 + '@ark/util': 0.56.1 + arkregex: 0.0.7 optional: true array-timsort@1.0.3: {} @@ -4076,18 +4227,18 @@ snapshots: base64id@2.0.0: {} - baseline-browser-mapping@2.10.31: {} + baseline-browser-mapping@2.10.42: {} - bits-ui@2.18.1(@internationalized/date@3.12.2)(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0)): + bits-ui@2.18.1(@internationalized/date@3.12.2)(@sveltejs/kit@2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1)): dependencies: '@floating-ui/core': 1.7.5 '@floating-ui/dom': 1.7.6 '@internationalized/date': 3.12.2 esm-env: 1.2.2 - runed: 0.35.1(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0)) - svelte: 5.56.4(@typescript-eslint/types@8.62.0) - svelte-toolbelt: 0.10.6(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0)) - tabbable: 6.4.0 + runed: 0.35.1(@sveltejs/kit@2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1)) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) + svelte-toolbelt: 0.10.6(@sveltejs/kit@2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1)) + tabbable: 6.5.0 transitivePeerDependencies: - '@sveltejs/kit' @@ -4095,9 +4246,6 @@ snapshots: dependencies: balanced-match: 4.0.4 - buffer-from@1.1.2: - optional: true - call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -4108,7 +4256,7 @@ snapshots: camelcase@8.0.0: optional: true - caniuse-lite@1.0.30001793: {} + caniuse-lite@1.0.30001800: {} chalk@4.1.2: dependencies: @@ -4124,7 +4272,7 @@ snapshots: class-validator@0.14.4: dependencies: '@types/validator': 13.15.10 - libphonenumber-js: 1.13.2 + libphonenumber-js: 1.13.8 validator: 13.15.35 optional: true @@ -4154,9 +4302,6 @@ snapshots: commander@14.0.3: {} - commander@2.20.3: - optional: true - comment-json@4.6.2: dependencies: array-timsort: 1.0.3 @@ -4200,7 +4345,7 @@ snapshots: date-fns@4.4.0: {} - dayjs@1.11.20: + dayjs@1.11.21: optional: true debounce-fn@6.0.0: @@ -4264,7 +4409,7 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - effect@3.21.2: + effect@3.21.4: dependencies: '@standard-schema/spec': 1.1.0 fast-check: 3.23.2 @@ -4346,15 +4491,15 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-prettier@10.1.8(eslint@10.5.0(jiti@2.7.0)): + eslint-config-prettier@10.1.8(eslint@10.6.0(jiti@2.7.0)): dependencies: - eslint: 10.5.0(jiti@2.7.0) + eslint: 10.6.0(jiti@2.7.0) - eslint-plugin-svelte@3.20.0(eslint@10.5.0(jiti@2.7.0))(svelte@5.56.4(@typescript-eslint/types@8.62.0)): + eslint-plugin-svelte@3.20.0(eslint@10.6.0(jiti@2.7.0))(svelte@5.56.4(@typescript-eslint/types@8.62.1)): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.7.0)) '@jridgewell/sourcemap-codec': 1.5.5 - eslint: 10.5.0(jiti@2.7.0) + eslint: 10.6.0(jiti@2.7.0) esutils: 2.0.3 globals: 16.5.0 known-css-properties: 0.37.0 @@ -4362,9 +4507,9 @@ snapshots: postcss-load-config: 3.1.4(postcss@8.5.16) postcss-safe-parser: 7.0.1(postcss@8.5.16) semver: 7.8.5 - svelte-eslint-parser: 1.8.0(svelte@5.56.4(@typescript-eslint/types@8.62.0)) + svelte-eslint-parser: 1.8.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)) optionalDependencies: - svelte: 5.56.4(@typescript-eslint/types@8.62.0) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) transitivePeerDependencies: - ts-node @@ -4386,9 +4531,9 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@10.5.0(jiti@2.7.0): + eslint@10.6.0(jiti@2.7.0): dependencies: - '@eslint-community/eslint-utils': 4.9.1(eslint@10.5.0(jiti@2.7.0)) + '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.7.0)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.23.5 '@eslint/config-helpers': 0.6.0 @@ -4443,11 +4588,11 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@2.2.13(@typescript-eslint/types@8.62.0): + esrap@2.2.13(@typescript-eslint/types@8.62.1): dependencies: '@jridgewell/sourcemap-codec': 1.5.5 optionalDependencies: - '@typescript-eslint/types': 8.62.0 + '@typescript-eslint/types': 8.62.1 esrecurse@4.3.0: dependencies: @@ -4507,11 +4652,11 @@ snapshots: hasown: 2.0.4 mime-types: 2.1.35 - formsnap@2.0.1(svelte@5.56.4(@typescript-eslint/types@8.62.0))(sveltekit-superforms@2.30.1(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(@types/json-schema@7.0.15)(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)): + formsnap@2.0.1(svelte@5.56.4(@typescript-eslint/types@8.62.1))(sveltekit-superforms@2.30.2(@sveltejs/kit@2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(@types/json-schema@7.0.15)(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)): dependencies: - svelte: 5.56.4(@typescript-eslint/types@8.62.0) - svelte-toolbelt: 0.5.0(svelte@5.56.4(@typescript-eslint/types@8.62.0)) - sveltekit-superforms: 2.30.1(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(@types/json-schema@7.0.15)(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) + svelte-toolbelt: 0.5.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)) + sveltekit-superforms: 2.30.2(@sveltejs/kit@2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(@types/json-schema@7.0.15)(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3) fs-extra@10.1.0: dependencies: @@ -4631,15 +4776,13 @@ snapshots: jiti@2.7.0: {} - joi@18.2.3: + joi@17.13.4: dependencies: - '@hapi/address': 5.1.1 - '@hapi/formula': 3.0.2 - '@hapi/hoek': 11.0.7 - '@hapi/pinpoint': 2.0.1 - '@hapi/tlds': 1.1.7 - '@hapi/topo': 6.0.2 - '@standard-schema/spec': 1.1.0 + '@hapi/hoek': 9.3.0 + '@hapi/topo': 5.1.0 + '@sideway/address': 4.1.5 + '@sideway/formula': 3.0.1 + '@sideway/pinpoint': 2.0.0 optional: true jose@6.2.3: {} @@ -4654,7 +4797,7 @@ snapshots: json-schema-to-ts@3.1.1: dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 ts-algebra: 2.0.0 optional: true @@ -4693,7 +4836,7 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 - libphonenumber-js@1.13.2: + libphonenumber-js@1.13.8: optional: true lightningcss-android-arm64@1.32.0: @@ -4800,11 +4943,11 @@ snapshots: minipass@7.1.3: {} - mode-watcher@1.1.0(svelte@5.56.4(@typescript-eslint/types@8.62.0)): + mode-watcher@1.1.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)): dependencies: - runed: 0.25.0(svelte@5.56.4(@typescript-eslint/types@8.62.0)) - svelte: 5.56.4(@typescript-eslint/types@8.62.0) - svelte-toolbelt: 0.7.1(svelte@5.56.4(@typescript-eslint/types@8.62.0)) + runed: 0.25.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) + svelte-toolbelt: 0.7.1(svelte@5.56.4(@typescript-eslint/types@8.62.1)) mri@1.2.0: {} @@ -4812,33 +4955,32 @@ snapshots: ms@2.1.3: {} - nanoid@3.3.12: {} - nanoid@3.3.15: {} natural-compare@1.4.0: {} negotiator@0.6.3: {} - next@16.2.6(@playwright/test@1.61.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + next@16.2.10(@opentelemetry/api@1.9.1)(@playwright/test@1.61.1)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - '@next/env': 16.2.6 + '@next/env': 16.2.10 '@swc/helpers': 0.5.15 - baseline-browser-mapping: 2.10.31 - caniuse-lite: 1.0.30001793 + baseline-browser-mapping: 2.10.42 + caniuse-lite: 1.0.30001800 postcss: 8.5.16 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) styled-jsx: 5.1.6(react@19.2.7) optionalDependencies: - '@next/swc-darwin-arm64': 16.2.6 - '@next/swc-darwin-x64': 16.2.6 - '@next/swc-linux-arm64-gnu': 16.2.6 - '@next/swc-linux-arm64-musl': 16.2.6 - '@next/swc-linux-x64-gnu': 16.2.6 - '@next/swc-linux-x64-musl': 16.2.6 - '@next/swc-win32-arm64-msvc': 16.2.6 - '@next/swc-win32-x64-msvc': 16.2.6 + '@next/swc-darwin-arm64': 16.2.10 + '@next/swc-darwin-x64': 16.2.10 + '@next/swc-linux-arm64-gnu': 16.2.10 + '@next/swc-linux-arm64-musl': 16.2.10 + '@next/swc-linux-x64-gnu': 16.2.10 + '@next/swc-linux-x64-musl': 16.2.10 + '@next/swc-win32-arm64-msvc': 16.2.10 + '@next/swc-win32-x64-msvc': 16.2.10 + '@opentelemetry/api': 1.9.1 '@playwright/test': 1.61.1 sharp: 0.34.5 transitivePeerDependencies: @@ -4860,7 +5002,7 @@ snapshots: object-assign@4.1.1: {} - obug@2.1.1: {} + obug@2.1.3: {} optionator@0.9.4: dependencies: @@ -4943,12 +5085,6 @@ snapshots: cssesc: 3.0.0 util-deprecate: 1.0.2 - postcss@8.5.15: - dependencies: - nanoid: 3.3.12 - picocolors: 1.1.1 - source-map-js: 1.2.1 - postcss@8.5.16: dependencies: nanoid: 3.3.15 @@ -4957,18 +5093,16 @@ snapshots: prelude-ls@1.2.1: {} - prettier-plugin-svelte@3.5.2(prettier@3.8.5)(svelte@5.56.4(@typescript-eslint/types@8.62.0)): + prettier-plugin-svelte@3.5.2(prettier@3.9.4)(svelte@5.56.4(@typescript-eslint/types@8.62.1)): dependencies: - prettier: 3.8.5 - svelte: 5.56.4(@typescript-eslint/types@8.62.0) + prettier: 3.9.4 + svelte: 5.56.4(@typescript-eslint/types@8.62.1) - prettier-plugin-tailwindcss@0.8.0(prettier-plugin-svelte@3.5.2(prettier@3.8.5)(svelte@5.56.4(@typescript-eslint/types@8.62.0)))(prettier@3.8.5): + prettier-plugin-tailwindcss@0.8.0(prettier-plugin-svelte@3.5.2(prettier@3.9.4)(svelte@5.56.4(@typescript-eslint/types@8.62.1)))(prettier@3.9.4): dependencies: - prettier: 3.8.5 + prettier: 3.9.4 optionalDependencies: - prettier-plugin-svelte: 3.5.2(prettier@3.8.5)(svelte@5.56.4(@typescript-eslint/types@8.62.0)) - - prettier@3.8.5: {} + prettier-plugin-svelte: 3.5.2(prettier@3.9.4)(svelte@5.56.4(@typescript-eslint/types@8.62.1)) prettier@3.9.4: {} @@ -5062,38 +5196,38 @@ snapshots: '@rolldown/binding-win32-arm64-msvc': 1.1.4 '@rolldown/binding-win32-x64-msvc': 1.1.4 - runed@0.23.4(svelte@5.56.4(@typescript-eslint/types@8.62.0)): + runed@0.23.4(svelte@5.56.4(@typescript-eslint/types@8.62.1)): dependencies: esm-env: 1.2.2 - svelte: 5.56.4(@typescript-eslint/types@8.62.0) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) - runed@0.25.0(svelte@5.56.4(@typescript-eslint/types@8.62.0)): + runed@0.25.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)): dependencies: esm-env: 1.2.2 - svelte: 5.56.4(@typescript-eslint/types@8.62.0) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) - runed@0.28.0(svelte@5.56.4(@typescript-eslint/types@8.62.0)): + runed@0.28.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)): dependencies: esm-env: 1.2.2 - svelte: 5.56.4(@typescript-eslint/types@8.62.0) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) - runed@0.35.1(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0)): + runed@0.35.1(@sveltejs/kit@2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1)): dependencies: dequal: 2.0.3 esm-env: 1.2.2 lz-string: 1.5.0 - svelte: 5.56.4(@typescript-eslint/types@8.62.0) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) optionalDependencies: - '@sveltejs/kit': 2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)) + '@sveltejs/kit': 2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) - runed@0.37.1(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(zod@4.4.3): + runed@0.37.1(@sveltejs/kit@2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(zod@4.4.3): dependencies: dequal: 2.0.3 esm-env: 1.2.2 lz-string: 1.5.0 - svelte: 5.56.4(@typescript-eslint/types@8.62.0) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) optionalDependencies: - '@sveltejs/kit': 2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)) + '@sveltejs/kit': 2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) zod: 4.4.3 sade@1.8.1: @@ -5112,12 +5246,12 @@ snapshots: set-cookie-parser@3.1.1: {} - shadcn-svelte@1.3.0(svelte@5.56.4(@typescript-eslint/types@8.62.0)): + shadcn-svelte@1.3.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)): dependencies: commander: 14.0.3 node-fetch-native: 1.6.7 - postcss: 8.5.15 - svelte: 5.56.4(@typescript-eslint/types@8.62.0) + postcss: 8.5.16 + svelte: 5.56.4(@typescript-eslint/types@8.62.1) tailwind-merge: 3.6.0 sharp@0.34.5: @@ -5198,15 +5332,6 @@ snapshots: source-map-js@1.2.1: {} - source-map-support@0.5.21: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - optional: true - - source-map@0.6.1: - optional: true - sqlite-wasm-kysely@0.3.0(kysely@0.29.2): dependencies: '@sqlite.org/sqlite-wasm': 3.48.0-build4 @@ -5246,7 +5371,7 @@ snapshots: dependencies: has-flag: 4.0.0 - svelte-check@4.7.1(picomatch@4.0.5)(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3): + svelte-check@4.7.1(picomatch@4.0.5)(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3): dependencies: '@jridgewell/trace-mapping': 0.3.31 '@sveltejs/load-config': 0.2.0 @@ -5254,12 +5379,12 @@ snapshots: fdir: 6.5.0(picomatch@4.0.5) picocolors: 1.1.1 sade: 1.8.1 - svelte: 5.56.4(@typescript-eslint/types@8.62.0) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) typescript: 6.0.3 transitivePeerDependencies: - picomatch - svelte-eslint-parser@1.8.0(svelte@5.56.4(@typescript-eslint/types@8.62.0)): + svelte-eslint-parser@1.8.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)): dependencies: eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -5269,36 +5394,36 @@ snapshots: postcss-selector-parser: 7.1.4 semver: 7.8.5 optionalDependencies: - svelte: 5.56.4(@typescript-eslint/types@8.62.0) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) - svelte-sonner@1.1.1(svelte@5.56.4(@typescript-eslint/types@8.62.0)): + svelte-sonner@1.1.1(svelte@5.56.4(@typescript-eslint/types@8.62.1)): dependencies: - runed: 0.28.0(svelte@5.56.4(@typescript-eslint/types@8.62.0)) - svelte: 5.56.4(@typescript-eslint/types@8.62.0) + runed: 0.28.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) - svelte-toolbelt@0.10.6(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0)): + svelte-toolbelt@0.10.6(@sveltejs/kit@2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1)): dependencies: clsx: 2.1.1 - runed: 0.35.1(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0)) + runed: 0.35.1(@sveltejs/kit@2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1)) style-to-object: 1.0.14 - svelte: 5.56.4(@typescript-eslint/types@8.62.0) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) transitivePeerDependencies: - '@sveltejs/kit' - svelte-toolbelt@0.5.0(svelte@5.56.4(@typescript-eslint/types@8.62.0)): + svelte-toolbelt@0.5.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)): dependencies: clsx: 2.1.1 style-to-object: 1.0.14 - svelte: 5.56.4(@typescript-eslint/types@8.62.0) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) - svelte-toolbelt@0.7.1(svelte@5.56.4(@typescript-eslint/types@8.62.0)): + svelte-toolbelt@0.7.1(svelte@5.56.4(@typescript-eslint/types@8.62.1)): dependencies: clsx: 2.1.1 - runed: 0.23.4(svelte@5.56.4(@typescript-eslint/types@8.62.0)) + runed: 0.23.4(svelte@5.56.4(@typescript-eslint/types@8.62.1)) style-to-object: 1.0.14 - svelte: 5.56.4(@typescript-eslint/types@8.62.0) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) - svelte@5.56.4(@typescript-eslint/types@8.62.0): + svelte@5.56.4(@typescript-eslint/types@8.62.1): dependencies: '@jridgewell/remapping': 2.3.5 '@jridgewell/sourcemap-codec': 1.5.5 @@ -5311,7 +5436,7 @@ snapshots: clsx: 2.1.1 devalue: 5.8.1 esm-env: 1.2.2 - esrap: 2.2.13(@typescript-eslint/types@8.62.0) + esrap: 2.2.13(@typescript-eslint/types@8.62.1) is-reference: 3.0.3 locate-character: 3.0.0 magic-string: 0.30.21 @@ -5319,27 +5444,27 @@ snapshots: transitivePeerDependencies: - '@typescript-eslint/types' - sveltekit-superforms@2.30.1(@sveltejs/kit@2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(@types/json-schema@7.0.15)(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3): + sveltekit-superforms@2.30.2(@sveltejs/kit@2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(@types/json-schema@7.0.15)(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3): dependencies: - '@sveltejs/kit': 2.68.0(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.0))(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.0))(typescript@6.0.3)(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)) + '@sveltejs/kit': 2.69.1(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.1.2(svelte@5.56.4(@typescript-eslint/types@8.62.1))(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3)(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) devalue: 5.8.1 memoize-weak: 1.0.2 - svelte: 5.56.4(@typescript-eslint/types@8.62.0) + svelte: 5.56.4(@typescript-eslint/types@8.62.1) ts-deepmerge: 8.0.0 optionalDependencies: '@exodus/schemasafe': 1.3.0 '@standard-schema/spec': 1.1.0 '@typeschema/class-validator': 0.3.0(@types/json-schema@7.0.15)(class-validator@0.14.4) - '@valibot/to-json-schema': 1.7.0(valibot@1.4.0(typescript@6.0.3)) + '@valibot/to-json-schema': 1.7.1(valibot@1.4.2(typescript@6.0.3)) '@vinejs/vine': 3.0.1 - arktype: 2.2.0 + arktype: 2.2.2 class-validator: 0.14.4 - effect: 3.21.2 - joi: 18.2.3 + effect: 3.21.4 + joi: 17.13.4 json-schema-to-ts: 3.1.1 superstruct: 2.0.2 - typebox: 1.1.38 - valibot: 1.4.0(typescript@6.0.3) + typebox: 1.3.3 + valibot: 1.4.2(typescript@6.0.3) yup: 1.7.1 zod: 4.4.3 zod-v3-to-json-schema: 4.0.0(zod@4.4.3) @@ -5347,32 +5472,22 @@ snapshots: - '@types/json-schema' - typescript - tabbable@6.4.0: {} + tabbable@6.5.0: {} tagged-tag@1.0.0: {} tailwind-merge@3.6.0: {} - tailwind-variants@3.2.2(tailwind-merge@3.6.0)(tailwindcss@4.3.1): + tailwind-variants@3.2.2(tailwind-merge@3.6.0)(tailwindcss@4.3.2): dependencies: - tailwindcss: 4.3.1 + tailwindcss: 4.3.2 optionalDependencies: tailwind-merge: 3.6.0 - tailwindcss@4.3.1: {} - tailwindcss@4.3.2: {} tapable@2.3.3: {} - terser@5.44.0: - dependencies: - '@jridgewell/source-map': 0.3.11 - acorn: 8.17.0 - commander: 2.20.3 - source-map-support: 0.5.21 - optional: true - tiny-case@1.0.3: optional: true @@ -5405,7 +5520,7 @@ snapshots: tslib@2.8.1: {} - tsx@4.22.4: + tsx@4.23.0: dependencies: esbuild: 0.28.1 optionalDependencies: @@ -5424,16 +5539,16 @@ snapshots: dependencies: tagged-tag: 1.0.0 - typebox@1.1.38: + typebox@1.3.3: optional: true - typescript-eslint@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3): + typescript-eslint@8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.62.0(@typescript-eslint/parser@8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/parser': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) - '@typescript-eslint/typescript-estree': 8.62.0(typescript@6.0.3) - '@typescript-eslint/utils': 8.62.0(eslint@10.5.0(jiti@2.7.0))(typescript@6.0.3) - eslint: 10.5.0(jiti@2.7.0) + '@typescript-eslint/eslint-plugin': 8.62.1(@typescript-eslint/parser@8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3))(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/parser': 8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.62.1(typescript@6.0.3) + '@typescript-eslint/utils': 8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) + eslint: 10.6.0(jiti@2.7.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color @@ -5463,7 +5578,7 @@ snapshots: uuid@14.0.1: {} - valibot@1.4.0(typescript@6.0.3): + valibot@1.4.2(typescript@6.0.3): optionalDependencies: typescript: 6.0.3 optional: true @@ -5473,16 +5588,16 @@ snapshots: vary@1.1.2: {} - vite-plugin-compression@0.5.1(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)): + vite-plugin-compression@0.5.1(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)): dependencies: chalk: 4.1.2 debug: 4.4.3 fs-extra: 10.1.0 - vite: 8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0) + vite: 8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color - vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0): + vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.5 @@ -5494,13 +5609,12 @@ snapshots: esbuild: 0.28.1 fsevents: 2.3.3 jiti: 2.7.0 - terser: 5.44.0 - tsx: 4.22.4 + tsx: 4.23.0 yaml: 2.9.0 - vitefu@1.1.3(vite@8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0)): + vitefu@1.1.3(vite@8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)): optionalDependencies: - vite: 8.1.0(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.44.0)(tsx@4.22.4)(yaml@2.9.0) + vite: 8.1.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0) webpack-virtual-modules@0.6.2: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index debcb46d..f3f2b431 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -7,6 +7,8 @@ onlyBuiltDependencies: - esbuild - sharp +minimumReleaseAge: 1440 + overrides: '@isaacs/brace-expansion': '>=5.0.1' cookie@<0.7.0: 0.7.2