diff --git a/.github/workflows/build-next.yml b/.github/workflows/build-next.yml index dbc30af8..e9316a5b 100644 --- a/.github/workflows/build-next.yml +++ b/.github/workflows/build-next.yml @@ -33,7 +33,7 @@ jobs: run_install: false - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v6.4.0 with: node-version: 24 cache: pnpm @@ -82,11 +82,11 @@ jobs: MACOS_NOTARY_ISSUER_ID: ${{ secrets.MACOS_NOTARY_ISSUER_ID }} - name: Binary attestation - uses: actions/attest@v4 + uses: actions/attest@v4.1.1 with: subject-checksums: ./dist/checksums.txt - name: Container image attestation - uses: actions/attest@v4 + uses: actions/attest@v4.1.1 with: subject-checksums: ./dist/digests.txt diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 7cde9ebd..37b6684b 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -45,7 +45,7 @@ jobs: uses: pnpm/action-setup@v5 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v6.4.0 with: node-version: 24 cache: "pnpm" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3294e9ed..27f12087 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,7 +28,7 @@ jobs: run_install: false - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v6.4.0 with: node-version: 24 cache: pnpm @@ -76,12 +76,12 @@ jobs: DISCORD_WEBHOOK_TOKEN: ${{ secrets.DISCORD_WEBHOOK_TOKEN }} - name: Binary attestation - uses: actions/attest@v4 + uses: actions/attest@v4.1.1 with: subject-checksums: ./dist/checksums.txt - name: Container image attestation - uses: actions/attest@v4 + uses: actions/attest@v4.1.1 with: subject-checksums: ./dist/digests.txt diff --git a/.github/workflows/svelte-check.yml b/.github/workflows/svelte-check.yml index 75591ce0..015be719 100644 --- a/.github/workflows/svelte-check.yml +++ b/.github/workflows/svelte-check.yml @@ -42,7 +42,7 @@ jobs: uses: pnpm/action-setup@v5 - name: Setup Node.js - uses: actions/setup-node@v6 + uses: actions/setup-node@v6.4.0 with: node-version: 24 cache: "pnpm" diff --git a/backend/go.mod b/backend/go.mod index eb36f49b..522c38a3 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -4,9 +4,9 @@ go 1.26.5 require ( github.com/aws/aws-sdk-go-v2 v1.42.1 - github.com/aws/aws-sdk-go-v2/config v1.32.27 - github.com/aws/aws-sdk-go-v2/credentials v1.19.26 - github.com/aws/aws-sdk-go-v2/service/s3 v1.104.2 + github.com/aws/aws-sdk-go-v2/config v1.32.29 + github.com/aws/aws-sdk-go-v2/credentials v1.19.28 + github.com/aws/aws-sdk-go-v2/service/s3 v1.105.0 github.com/aws/smithy-go v1.27.3 github.com/caarlos0/env/v11 v11.4.1 github.com/cenkalti/backoff/v5 v5.0.3 @@ -17,7 +17,7 @@ require ( github.com/fsnotify/fsnotify v1.10.1 github.com/gin-contrib/slog v1.2.1 github.com/gin-gonic/gin v1.12.0 - github.com/go-co-op/gocron/v2 v2.21.2 + github.com/go-co-op/gocron/v2 v2.22.0 github.com/go-jose/go-jose/v4 v4.1.4 github.com/go-ldap/ldap/v3 v3.4.13 github.com/go-playground/validator/v10 v10.30.3 @@ -32,13 +32,14 @@ require ( github.com/joho/godotenv v1.5.1 github.com/lestrrat-go/httprc/v3 v3.0.6 github.com/lestrrat-go/jwx/v3 v3.1.1 - github.com/libtnb/sqlite v1.1.2 + github.com/libtnb/sqlite v1.2.0 github.com/lmittmann/tint v1.1.3 github.com/mattn/go-isatty v0.0.22 github.com/mileusna/useragent v1.3.5 github.com/orandin/slog-gorm v1.4.0 github.com/ory/fosite v0.49.1-0.20250703093431-a5f0b09bf31c github.com/oschwald/maxminddb-golang/v2 v2.4.1 + github.com/pires/go-proxyproto v0.15.0 github.com/spf13/cobra v1.10.2 github.com/stretchr/testify v1.11.1 github.com/zitadel/exifremove v0.1.0 @@ -52,10 +53,10 @@ require ( go.opentelemetry.io/otel/sdk/log v0.20.0 go.opentelemetry.io/otel/sdk/metric v1.44.0 go.opentelemetry.io/otel/trace v1.44.0 - golang.org/x/crypto v0.53.0 - golang.org/x/image v0.43.0 + golang.org/x/crypto v0.54.0 + golang.org/x/image v0.44.0 golang.org/x/sync v0.22.0 - golang.org/x/text v0.39.0 + golang.org/x/text v0.40.0 gorm.io/driver/postgres v1.6.0 gorm.io/gorm v1.31.2 gorm.io/plugin/opentelemetry v0.1.16 @@ -230,7 +231,7 @@ require ( golang.org/x/mod v0.37.0 // indirect golang.org/x/net v0.56.0 // indirect golang.org/x/oauth2 v0.36.0 // indirect - golang.org/x/sys v0.46.0 // indirect + golang.org/x/sys v0.47.0 // indirect golang.org/x/time v0.15.0 // indirect golang.org/x/tools v0.47.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260630182238-925bb5da69e7 // indirect diff --git a/backend/go.sum b/backend/go.sum index 56031885..1c7844e2 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -22,10 +22,10 @@ github.com/aws/aws-sdk-go-v2 v1.42.1 h1:9eOTgu1z/dVtYpNZ3/8/XbbaX0x/BqE3HUzAzs6K github.com/aws/aws-sdk-go-v2 v1.42.1/go.mod h1:5pKeft2eJj+gElQ38Jqg4ibCqh+/AK33/0X3hip7IjM= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.14 h1:3IZY0XAJquT3aHzbkHfPzy4ACPcEjVG0x87KOwtpqGY= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.14/go.mod h1:zwM6veDkhGgQFqkBy+uT28AAYpLu+uFMlPl+rCg/73E= -github.com/aws/aws-sdk-go-v2/config v1.32.27 h1:SJwJ9Q4kM7v5QVSYYyXj3znRr6lNyZEhSgAXmXXcVbI= -github.com/aws/aws-sdk-go-v2/config v1.32.27/go.mod h1:uBfrzTRedDmB2u+b6+UlaKJy2O6VSH5un2jP24t/KvQ= -github.com/aws/aws-sdk-go-v2/credentials v1.19.26 h1:Si8kk1kyJnuJWCEgiwpBtTdtgSdR7i611596NnC0YIQ= -github.com/aws/aws-sdk-go-v2/credentials v1.19.26/go.mod h1:lBckz+W9SAdNtSDw3pYgQUJDJFcBBWry0GSzw+bK0TY= +github.com/aws/aws-sdk-go-v2/config v1.32.29 h1:BcMHHnpiWKogf+gGfpj3K1w+Sktz29XDo/cPSAPO3FU= +github.com/aws/aws-sdk-go-v2/config v1.32.29/go.mod h1:+Kbhn8Es4kPUph3F/0W7avykytc+Jh2Ld9/msv9ljV4= +github.com/aws/aws-sdk-go-v2/credentials v1.19.28 h1:zTXJSsNcoO91/mTXsZoYf0AK8dvNPiA58/VtyGXR+wM= +github.com/aws/aws-sdk-go-v2/credentials v1.19.28/go.mod h1:Kd9E0JzDBW/q1xbsHFrev/GnbAf5J0Ng8xoyc7HZ91Q= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.30 h1:/hi1JADLEW9YYryEz1w4GQu0EtP23pP553Cf9KgsDV4= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.30/go.mod h1:/3AOgy4K17Dm4ucMZVC/MJkzy5kmfKUcINRHZyo0koQ= github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.30 h1:xM/Is9cKMHa8Jj8zkvWhvrFkZsXJV9E+BB4g0HW0duQ= @@ -42,8 +42,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.30 h1:/Z5jmNrK github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.30/go.mod h1:lEzEZnOosE7zi8Z6royW1cFJTD9fpab4Ul1SBrllewk= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.31 h1:uao4A3QZ5UmB326V6KF+qRpv9Tjz7IlnlnTbbANntlU= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.31/go.mod h1:I/1+z0VwL1GhQyLgkoHDlygpUZ+iTAwOQ/NsftiUL2I= -github.com/aws/aws-sdk-go-v2/service/s3 v1.104.2 h1:bAY6O/TDv1HQnvylh9E247IyIKsUWUt2G965S7qX110= -github.com/aws/aws-sdk-go-v2/service/s3 v1.104.2/go.mod h1:zdmCoFO/dSI7GlrwsPqFJI+WlFnSU4Tc8TJnlXrM1Do= +github.com/aws/aws-sdk-go-v2/service/s3 v1.105.0 h1:XptwLL+UHXgafYMIHTy59IRovLbhz3znkxY2uS/pbXU= +github.com/aws/aws-sdk-go-v2/service/s3 v1.105.0/go.mod h1:zdmCoFO/dSI7GlrwsPqFJI+WlFnSU4Tc8TJnlXrM1Do= github.com/aws/aws-sdk-go-v2/service/signin v1.4.0 h1:sLzmJGCMv+C8KqiJgEqDLB6vxaJGmobRh4rr//ZpA3w= github.com/aws/aws-sdk-go-v2/service/signin v1.4.0/go.mod h1:mxC0nT/C8wMMS97DemZPzvUZxvIt+2Iq+eS3JdFZGgg= github.com/aws/aws-sdk-go-v2/service/sso v1.32.0 h1:qjMmry/cBDee1E/2gyvel0uRYCi3mwRZ2hf6N+GAodo= @@ -161,8 +161,8 @@ github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8= github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 h1:BP4M0CvQ4S3TGls2FvczZtj5Re/2ZzkV9VwqPHH/3Bo= github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0= -github.com/go-co-op/gocron/v2 v2.21.2 h1:bD8/YwkojYHgXFr3iEulL148KBdTbKVxUZzFKpXcdbY= -github.com/go-co-op/gocron/v2 v2.21.2/go.mod h1:5lEiCKk1oVJV39Zg7/YG10OnaVrDAV5GGR6O0663k6U= +github.com/go-co-op/gocron/v2 v2.22.0 h1:uEuH2F7k7VoESb1BYSaffuuV+T0kkpzsC0aXk7/z79I= +github.com/go-co-op/gocron/v2 v2.22.0/go.mod h1:hiH/U9RMhTi1BBZJmef9s3KC9QwhpBF6PFrvUKaXY9M= github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= github.com/go-errors/errors v1.0.2/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= github.com/go-errors/errors v1.1.1/go.mod h1:psDX2osz5VnTOnFWbDeWwS7yejl+uV3FEWEp4lssFEs= @@ -348,8 +348,8 @@ github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLO github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg= github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ= github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= -github.com/libtnb/sqlite v1.1.2 h1:3g8nez8MwdEhn+oqt3bfKWnYJxk0ZGTO0kVA1WT+hjQ= -github.com/libtnb/sqlite v1.1.2/go.mod h1:2PGCWOR6HqqXuoIVMmwfJ655epvRO8sw0b9kHWuKNsQ= +github.com/libtnb/sqlite v1.2.0 h1:XsA7jsXHH2qmFkTWoy5YCKJybzHzWQ0flqDFP5Y9Yto= +github.com/libtnb/sqlite v1.2.0/go.mod h1:O6vURH5fa5IgSmXd/qLAL2zSnYFUd7xSIumsmX3BrSI= github.com/lmittmann/tint v1.1.3 h1:Hv4EaHWXQr+GTFnOU4VKf8UvAtZgn0VuKT+G0wFlO3I= github.com/lmittmann/tint v1.1.3/go.mod h1:HIS3gSy7qNwGCj+5oRjAutErFBl4BzdQP6cJZ0NfMwE= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= @@ -421,6 +421,8 @@ 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/pires/go-proxyproto v0.15.0 h1:dTshmNbFm/D+0+sbrxUuddPOZ5Y0B7c5NhtsBkm6LqI= +github.com/pires/go-proxyproto v0.15.0/go.mod h1:OXsCrKwrK2tXS9YrI5tkHx5xaQlO8FH3lFW76orFh24= 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= @@ -616,13 +618,13 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY 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= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto= -golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio= +golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw= +golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk= golang.org/x/exp v0.0.0-20260611194520-c48552f49976 h1:X8Hz2ImujgbmetVuW+w2YkyZChE3cBpZi2P158rTG9M= golang.org/x/exp v0.0.0-20260611194520-c48552f49976/go.mod h1:vnf4pv9iKZXY58sQE1L86zmNWJ4159e1RkcWiLCkeEY= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/image v0.43.0 h1:FLxcP4ec2350nTfOC8ysKtqYSIFbk/QGjw1ZHNP4tsY= -golang.org/x/image v0.43.0/go.mod h1:rrpelvGFt+kLPAjPM4HeWPgrl0FtafueU//e5N0qk/Q= +golang.org/x/image v0.44.0 h1:+tDekMZED9+LrtB3G5xzRggpVh9CARjZqROla3R3R+I= +golang.org/x/image v0.44.0/go.mod h1:V8K3KE9KKKE+pLpQDOeN18w9oacNSvy1tDOirTu4xtY= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -686,8 +688,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw= -golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs= +golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -709,8 +711,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus= -golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM= +golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs= +golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY= golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/backend/internal/bootstrap/proxy_protocol.go b/backend/internal/bootstrap/proxy_protocol.go new file mode 100644 index 00000000..6f73ed94 --- /dev/null +++ b/backend/internal/bootstrap/proxy_protocol.go @@ -0,0 +1,55 @@ +package bootstrap + +import ( + "fmt" + "log/slog" + "net" + "time" + + "github.com/pires/go-proxyproto" +) + +const proxyProtocolReadHeaderTimeout = 5 * time.Second + +func newProxyProtocolListener(listener net.Listener, trustedProxies []string) (net.Listener, error) { + if len(trustedProxies) == 0 { + return listener, nil + } + + // PROXY protocol transports client metadata on TCP connections before any TLS handshake + network := listener.Addr().Network() + if network != "tcp" && network != "tcp4" && network != "tcp6" { + return nil, fmt.Errorf("PROXY protocol requires a TCP listener, got %q", network) + } + + // Require headers from configured network proxies and reject every other network peer + trustedProxyPolicy, err := proxyproto.TrustProxyHeaderFromRanges(trustedProxies) + if err != nil { + return nil, fmt.Errorf("failed to configure PROXY protocol trusted proxies: %w", err) + } + + // We need to add another policy for loopback connections because healthcheck requests are made from the same host + // loopbackPolicy handles requests from trusted loopback like normal, but it also allows untrusted loopback connections to be accepted + // untrusted loopback connections aren't allowed to send PROXY headers though + loopbackPolicy, err := proxyproto.PolicyFromRanges(trustedProxies, proxyproto.USE, proxyproto.SKIP) + if err != nil { + return nil, fmt.Errorf("failed to configure PROXY protocol loopback proxies: %w", err) + } + + // Create a policy which decides which of the two policies to use based on the upstream address + policy := func(options proxyproto.ConnPolicyOptions) (proxyproto.Policy, error) { + upstream, ok := options.Upstream.(*net.TCPAddr) + if ok && upstream.IP.IsLoopback() { + return loopbackPolicy(options) + } + + return trustedProxyPolicy(options) + } + + slog.Info("PROXY protocol enabled") + return &proxyproto.Listener{ + Listener: listener, + ConnPolicy: policy, + ReadHeaderTimeout: proxyProtocolReadHeaderTimeout, + }, nil +} diff --git a/backend/internal/bootstrap/proxy_protocol_test.go b/backend/internal/bootstrap/proxy_protocol_test.go new file mode 100644 index 00000000..0dea1ebd --- /dev/null +++ b/backend/internal/bootstrap/proxy_protocol_test.go @@ -0,0 +1,486 @@ +package bootstrap + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "io" + "math/big" + "net" + "net/http" + "sync" + "testing" + "time" + + "github.com/gin-gonic/gin" + "github.com/pires/go-proxyproto" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestNewProxyProtocolListenerDisabled(t *testing.T) { + listener, _ := newProxyProtocolTestConnection(t, "192.0.2.10") + + wrapped, err := newProxyProtocolListener(listener, nil) + + require.NoError(t, err) + assert.Same(t, listener, wrapped) +} + +func TestNewProxyProtocolListenerRequiresTCP(t *testing.T) { + serverConn, clientConn := net.Pipe() + t.Cleanup(func() { + _ = serverConn.Close() + _ = clientConn.Close() + }) + listener := &singleConnListener{ + conn: serverConn, + addr: &net.UnixAddr{Name: "/tmp/pocket-id.sock", Net: "unix"}, + closed: make(chan struct{}), + } + + _, err := newProxyProtocolListener(listener, []string{"127.0.0.1"}) + + require.Error(t, err) + assert.ErrorContains(t, err, "PROXY protocol requires a TCP listener") +} + +func TestNewProxyProtocolListenerUsesStrictTrustedProxyPolicy(t *testing.T) { + listener, _ := newProxyProtocolTestConnection(t, "192.0.2.10") + + wrapped, err := newProxyProtocolListener(listener, []string{"192.0.2.0/24"}) + require.NoError(t, err) + proxyListener := requireProxyProtocolListener(t, wrapped) + assert.Equal(t, proxyProtocolReadHeaderTimeout, proxyListener.ReadHeaderTimeout) + + policy, err := proxyListener.ConnPolicy(proxyproto.ConnPolicyOptions{ + Upstream: &net.TCPAddr{IP: net.ParseIP("192.0.2.10"), Port: 12345}, + }) + require.NoError(t, err) + assert.Equal(t, proxyproto.REQUIRE, policy) + + _, err = proxyListener.ConnPolicy(proxyproto.ConnPolicyOptions{ + Upstream: &net.TCPAddr{IP: net.ParseIP("198.51.100.10"), Port: 12345}, + }) + require.ErrorIs(t, err, proxyproto.ErrInvalidUpstream) + + policy, err = proxyListener.ConnPolicy(proxyproto.ConnPolicyOptions{ + Upstream: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 12345}, + }) + require.NoError(t, err) + assert.Equal(t, proxyproto.SKIP, policy) +} + +func TestProxyProtocolListenerAllowsLoopbackWithoutHeader(t *testing.T) { + listener, clientConn := newProxyProtocolTestConnection(t, "127.0.0.1") + wrapped, err := newProxyProtocolListener(listener, []string{"192.0.2.0/24"}) + require.NoError(t, err) + + serverConn, err := wrapped.Accept() + require.NoError(t, err) + + writeErr := make(chan error, 1) + go func() { + _, err := clientConn.Write([]byte("healthcheck")) + writeErr <- err + }() + + payload := make([]byte, len("healthcheck")) + _, err = io.ReadFull(serverConn, payload) + require.NoError(t, err) + require.NoError(t, <-writeErr) + assert.Equal(t, "healthcheck", string(payload)) + assert.Equal(t, "127.0.0.1:12345", serverConn.RemoteAddr().String()) +} + +func TestProxyProtocolListenerAcceptsHeaderFromTrustedLoopback(t *testing.T) { + listener, clientConn := newProxyProtocolTestConnection(t, "::1") + wrapped, err := newProxyProtocolListener(listener, []string{"::1"}) + require.NoError(t, err) + + serverConn, err := wrapped.Accept() + require.NoError(t, err) + + source := &net.TCPAddr{IP: net.ParseIP("2001:db8::15"), Port: 42300} + dest := &net.TCPAddr{IP: net.ParseIP("::1"), Port: 1411} + writeErr := make(chan error, 1) + go func() { + header := proxyproto.HeaderProxyFromAddrs(2, source, dest) + if _, err := header.WriteTo(clientConn); err != nil { + writeErr <- err + return + } + _, err := clientConn.Write([]byte("payload")) + writeErr <- err + }() + + payload := make([]byte, len("payload")) + _, err = io.ReadFull(serverConn, payload) + require.NoError(t, err) + require.NoError(t, <-writeErr) + assert.Equal(t, "payload", string(payload)) + assert.Equal(t, source.String(), serverConn.RemoteAddr().String()) +} + +func TestProxyProtocolListenerAllowsTrustedLoopbackWithoutHeader(t *testing.T) { + listener, clientConn := newProxyProtocolTestConnection(t, "::1") + wrapped, err := newProxyProtocolListener(listener, []string{"::1"}) + require.NoError(t, err) + + serverConn, err := wrapped.Accept() + require.NoError(t, err) + + writeErr := make(chan error, 1) + go func() { + _, err := clientConn.Write([]byte("healthcheck")) + writeErr <- err + }() + + payload := make([]byte, len("healthcheck")) + _, err = io.ReadFull(serverConn, payload) + require.NoError(t, err) + require.NoError(t, <-writeErr) + assert.Equal(t, "healthcheck", string(payload)) + assert.Equal(t, "[::1]:12345", serverConn.RemoteAddr().String()) +} + +func TestProxyProtocolListenerDropsUntrustedPeerAndContinues(t *testing.T) { + untrustedServerConn, untrustedClientConn := newAddressedPipe("198.51.100.10") + trustedServerConn, trustedClientConn := newAddressedPipe("192.0.2.10") + listener := &queuedConnListener{ + conns: []net.Conn{untrustedServerConn, trustedServerConn}, + addr: trustedServerConn.LocalAddr(), + } + t.Cleanup(func() { + _ = listener.Close() + _ = untrustedServerConn.Close() + _ = trustedServerConn.Close() + _ = untrustedClientConn.Close() + _ = trustedClientConn.Close() + }) + + wrapped, err := newProxyProtocolListener(listener, []string{"192.0.2.0/24"}) + require.NoError(t, err) + serverConn, err := wrapped.Accept() + require.NoError(t, err) + + source := &net.TCPAddr{IP: net.ParseIP("203.0.113.15"), Port: 42300} + dest := &net.TCPAddr{IP: net.ParseIP("192.0.2.20"), Port: 443} + writeErr := make(chan error, 1) + go func() { + header := proxyproto.HeaderProxyFromAddrs(1, source, dest) + if _, err := header.WriteTo(trustedClientConn); err != nil { + writeErr <- err + return + } + _, err := trustedClientConn.Write([]byte("payload")) + writeErr <- err + }() + + payload := make([]byte, len("payload")) + _, err = io.ReadFull(serverConn, payload) + require.NoError(t, err) + require.NoError(t, <-writeErr) + assert.Equal(t, source.String(), serverConn.RemoteAddr().String()) + + _, err = untrustedClientConn.Write([]byte("payload")) + require.Error(t, err) +} + +func TestProxyProtocolListenerExtractsClientAddress(t *testing.T) { + testCases := []struct { + name string + version byte + source *net.TCPAddr + dest *net.TCPAddr + }{ + { + name: "version 1 IPv4", + version: 1, + source: &net.TCPAddr{IP: net.ParseIP("203.0.113.15"), Port: 42300}, + dest: &net.TCPAddr{IP: net.ParseIP("192.0.2.20"), Port: 443}, + }, + { + name: "version 2 IPv6", + version: 2, + source: &net.TCPAddr{IP: net.ParseIP("2001:db8::15"), Port: 42300}, + dest: &net.TCPAddr{IP: net.ParseIP("2001:db8::20"), Port: 443}, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.name, func(t *testing.T) { + listener, clientConn := newProxyProtocolTestConnection(t, "192.0.2.10") + wrapped, err := newProxyProtocolListener(listener, []string{"192.0.2.0/24"}) + require.NoError(t, err) + + serverConn, err := wrapped.Accept() + require.NoError(t, err) + + writeErr := make(chan error, 1) + go func() { + header := proxyproto.HeaderProxyFromAddrs(testCase.version, testCase.source, testCase.dest) + if _, err := header.WriteTo(clientConn); err != nil { + writeErr <- err + return + } + _, err := clientConn.Write([]byte("payload")) + writeErr <- err + }() + + payload := make([]byte, len("payload")) + _, err = io.ReadFull(serverConn, payload) + require.NoError(t, err) + require.NoError(t, <-writeErr) + assert.Equal(t, "payload", string(payload)) + assert.Equal(t, testCase.source.String(), serverConn.RemoteAddr().String()) + assert.Equal(t, testCase.dest.String(), serverConn.LocalAddr().String()) + }) + } +} + +func TestProxyProtocolListenerRejectsMissingHeader(t *testing.T) { + listener, clientConn := newProxyProtocolTestConnection(t, "192.0.2.10") + wrapped, err := newProxyProtocolListener(listener, []string{"192.0.2.0/24"}) + require.NoError(t, err) + + serverConn, err := wrapped.Accept() + require.NoError(t, err) + + go func() { + _, _ = clientConn.Write([]byte("GET /healthz HTTP/1.1\r\n")) + }() + + _, err = serverConn.Read(make([]byte, 1)) + require.ErrorIs(t, err, proxyproto.ErrNoProxyProtocol) +} + +func TestProxyProtocolListenerSetsGinClientIP(t *testing.T) { + listener, clientConn := newProxyProtocolTestConnection(t, "192.0.2.10") + wrapped, err := newProxyProtocolListener(listener, []string{"192.0.2.0/24"}) + require.NoError(t, err) + + engine := gin.New() + require.NoError(t, engine.SetTrustedProxies(nil)) + clientIP := make(chan string, 1) + engine.GET("/client-ip", func(c *gin.Context) { + clientIP <- c.ClientIP() + c.Status(http.StatusNoContent) + }) + server := &http.Server{ + Handler: engine, + ReadHeaderTimeout: time.Second, + } + t.Cleanup(func() { + _ = server.Close() + }) + go func() { + _ = server.Serve(wrapped) + }() + + source := &net.TCPAddr{IP: net.ParseIP("203.0.113.15"), Port: 42300} + dest := &net.TCPAddr{IP: net.ParseIP("192.0.2.20"), Port: 443} + writeErr := make(chan error, 1) + go func() { + header := proxyproto.HeaderProxyFromAddrs(1, source, dest) + if _, err := header.WriteTo(clientConn); err != nil { + writeErr <- err + return + } + _, err := clientConn.Write([]byte("GET /client-ip HTTP/1.1\r\nHost: pocket-id.example\r\nConnection: close\r\n\r\n")) + writeErr <- err + }() + + select { + case actualClientIP := <-clientIP: + assert.Equal(t, "203.0.113.15", actualClientIP) + case <-time.After(time.Second): + t.Fatal("timed out waiting for Gin to handle the proxied request") + } + require.NoError(t, <-writeErr) +} + +func TestProxyProtocolHeaderIsReadBeforeTLS(t *testing.T) { + listener, clientConn := newProxyProtocolTestConnection(t, "192.0.2.10") + proxyListener, err := newProxyProtocolListener(listener, []string{"192.0.2.0/24"}) + require.NoError(t, err) + + serverTLSConfig, clientTLSConfig := newProxyProtocolTestTLSConfigs(t) + tlsListener := tls.NewListener(proxyListener, serverTLSConfig) + serverConn, err := tlsListener.Accept() + require.NoError(t, err) + + source := &net.TCPAddr{IP: net.ParseIP("203.0.113.15"), Port: 42300} + dest := &net.TCPAddr{IP: net.ParseIP("192.0.2.20"), Port: 443} + ctx := t.Context() + clientErr := make(chan error, 1) + go func() { + header := proxyproto.HeaderProxyFromAddrs(2, source, dest) + if _, err := header.WriteTo(clientConn); err != nil { + clientErr <- err + return + } + + tlsClient := tls.Client(clientConn, clientTLSConfig) + if err := tlsClient.HandshakeContext(ctx); err != nil { + clientErr <- err + return + } + _, err := tlsClient.Write([]byte("payload")) + clientErr <- err + }() + + tlsServerConn, ok := serverConn.(*tls.Conn) + require.True(t, ok) + require.NoError(t, tlsServerConn.HandshakeContext(ctx)) + payload := make([]byte, len("payload")) + _, err = io.ReadFull(serverConn, payload) + require.NoError(t, err) + require.NoError(t, <-clientErr) + assert.Equal(t, "payload", string(payload)) + assert.Equal(t, source.String(), serverConn.RemoteAddr().String()) +} + +func requireProxyProtocolListener(t *testing.T, listener net.Listener) *proxyproto.Listener { + t.Helper() + + proxyListener, ok := listener.(*proxyproto.Listener) + require.True(t, ok) + return proxyListener +} + +func newProxyProtocolTestConnection(t *testing.T, upstreamIP string) (*singleConnListener, net.Conn) { + t.Helper() + + serverConn, clientConn := newAddressedPipe(upstreamIP) + listener := &singleConnListener{ + conn: serverConn, + addr: serverConn.LocalAddr(), + closed: make(chan struct{}), + } + + t.Cleanup(func() { + _ = listener.Close() + _ = clientConn.Close() + }) + return listener, clientConn +} + +func newAddressedPipe(upstreamIP string) (net.Conn, net.Conn) { + serverConn, clientConn := net.Pipe() + return &addressedConn{ + Conn: serverConn, + localAddr: &net.TCPAddr{IP: net.ParseIP("192.0.2.20"), Port: 1411}, + remoteAddr: &net.TCPAddr{IP: net.ParseIP(upstreamIP), Port: 12345}, + }, clientConn +} + +func newProxyProtocolTestTLSConfigs(t *testing.T) (*tls.Config, *tls.Config) { + t.Helper() + + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + + now := time.Now() + template := &x509.Certificate{ + SerialNumber: big.NewInt(1), + DNSNames: []string{"localhost"}, + NotBefore: now.Add(-time.Minute), + NotAfter: now.Add(time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + } + certificateDER, err := x509.CreateCertificate(rand.Reader, template, template, &privateKey.PublicKey, privateKey) + require.NoError(t, err) + certificate, err := x509.ParseCertificate(certificateDER) + require.NoError(t, err) + + roots := x509.NewCertPool() + roots.AddCert(certificate) + + serverConfig := &tls.Config{ + Certificates: []tls.Certificate{{ + Certificate: [][]byte{certificateDER}, + PrivateKey: privateKey, + }}, + MinVersion: tls.VersionTLS13, + } + clientConfig := &tls.Config{ + RootCAs: roots, + ServerName: "localhost", + MinVersion: tls.VersionTLS13, + } + return serverConfig, clientConfig +} + +type singleConnListener struct { + conn net.Conn + addr net.Addr + accepted bool + closed chan struct{} + closeOnce sync.Once +} + +func (l *singleConnListener) Accept() (net.Conn, error) { + if l.accepted { + <-l.closed + return nil, net.ErrClosed + } + l.accepted = true + return l.conn, nil +} + +func (l *singleConnListener) Close() error { + var closeErr error + l.closeOnce.Do(func() { + close(l.closed) + closeErr = l.conn.Close() + }) + return closeErr +} + +func (l *singleConnListener) Addr() net.Addr { + return l.addr +} + +type addressedConn struct { + net.Conn + localAddr net.Addr + remoteAddr net.Addr +} + +func (c *addressedConn) LocalAddr() net.Addr { + return c.localAddr +} + +func (c *addressedConn) RemoteAddr() net.Addr { + return c.remoteAddr +} + +type queuedConnListener struct { + conns []net.Conn + addr net.Addr +} + +func (l *queuedConnListener) Accept() (net.Conn, error) { + if len(l.conns) == 0 { + return nil, net.ErrClosed + } + + conn := l.conns[0] + l.conns = l.conns[1:] + return conn, nil +} + +func (l *queuedConnListener) Close() error { + for _, conn := range l.conns { + _ = conn.Close() + } + return nil +} + +func (l *queuedConnListener) Addr() net.Addr { + return l.addr +} diff --git a/backend/internal/bootstrap/router_bootstrap.go b/backend/internal/bootstrap/router_bootstrap.go index 2ea0e162..9969045f 100644 --- a/backend/internal/bootstrap/router_bootstrap.go +++ b/backend/internal/bootstrap/router_bootstrap.go @@ -222,7 +222,18 @@ func initServer(r *gin.Engine) (*serverConfig, error) { } addr := socket.addr + listener := socket.listener + + // Wrap the listener with a proxy protocol listener if configured and not using a Unix socket + if len(common.EnvConfig.ProxyProtocol) > 0 && common.EnvConfig.UnixSocket == "" { + listener, err = newProxyProtocolListener(socket.listener, common.EnvConfig.ProxyProtocol) + if err != nil { + _ = socket.listener.Close() + return nil, err + } + } + server := newHTTPServer(r, protocols) return &serverConfig{addr, certProvider, listener, server, tlsConfig}, nil diff --git a/backend/internal/cmds/healthcheck.go b/backend/internal/cmds/healthcheck.go index bbea825c..5b9f4665 100644 --- a/backend/internal/cmds/healthcheck.go +++ b/backend/internal/cmds/healthcheck.go @@ -2,6 +2,7 @@ package cmds import ( "context" + "crypto/tls" "fmt" "log/slog" "net" @@ -64,13 +65,27 @@ func init() { }, } - healthcheckCmd.Flags().StringVarP(&flags.Endpoint, "endpoint", "e", "http://localhost:"+common.EnvConfig.Port, "Endpoint for Pocket ID") + healthcheckCmd.Flags().StringVarP(&flags.Endpoint, "endpoint", "e", defaultEndpoint(), "Endpoint for Pocket ID") healthcheckCmd.Flags().StringVar(&flags.UnixSocket, "unix-socket", "", "UNIX socket path for Pocket ID") healthcheckCmd.Flags().BoolVarP(&flags.Verbose, "verbose", "v", false, "Enable verbose mode") rootCmd.AddCommand(healthcheckCmd) } +// The server only serves TLS when both a certificate and a key file are configured +func tlsEnabled() bool { + return common.EnvConfig.TLSCertFile != "" && common.EnvConfig.TLSKeyFile != "" +} + +func defaultEndpoint() string { + scheme := "http" + if tlsEnabled() { + scheme = "https" + } + + return scheme + "://localhost:" + common.EnvConfig.Port +} + func healthcheck(ctx context.Context, flags healthcheckFlags) (*healthcheckResult, error) { url := strings.TrimRight(flags.Endpoint, "/") + "/healthz" req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) @@ -78,17 +93,25 @@ func healthcheck(ctx context.Context, flags healthcheckFlags) (*healthcheckResul return nil, fmt.Errorf("failed to create request object for %q: %w", url, err) } - client := http.DefaultClient + transport := http.DefaultTransport.(*http.Transport).Clone() + if flags.UnixSocket != "" { - transport := http.DefaultTransport.(*http.Transport).Clone() transport.Proxy = nil transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) { dialer := net.Dialer{} return dialer.DialContext(ctx, "unix", flags.UnixSocket) } - client = &http.Client{Transport: transport} } + // If TLS cert and key files are provided, there is a high chance that the server is using a self-signed certificate + if tlsEnabled() { + transport.TLSClientConfig = &tls.Config{ + InsecureSkipVerify: true, //nolint:gosec // no sensitive data is transmitted in healthcheck + } + } + + client := &http.Client{Transport: transport} + res, err := client.Do(req) if err != nil { return nil, fmt.Errorf("failed to perform request to %q: %w", url, err) diff --git a/backend/internal/cmds/healthcheck_test.go b/backend/internal/cmds/healthcheck_test.go index 3df83cd8..0da483a6 100644 --- a/backend/internal/cmds/healthcheck_test.go +++ b/backend/internal/cmds/healthcheck_test.go @@ -2,15 +2,24 @@ package cmds import ( "context" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/tls" + "crypto/x509" + "math/big" "net" "net/http" "net/http/httptest" + "os" "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "github.com/pocket-id/pocket-id/backend/internal/common" ) func TestHealthcheckTCPSuccess(t *testing.T) { @@ -77,3 +86,123 @@ func TestHealthcheckUnixSocket(t *testing.T) { require.Equal(t, http.StatusNoContent, result.StatusCode) require.Equal(t, "http://localhost:1411/healthz", result.URL) } + +// The server also serves TLS over a UNIX socket, so the healthcheck must keep dialing +// the socket instead of falling back to the host in the endpoint +func TestHealthcheckUnixSocketWithTLS(t *testing.T) { + setTLSFiles(t) + + socketPath := filepath.Join(shortTempDir(t), "s.sock") + listener, err := (&net.ListenConfig{}).Listen(t.Context(), "unix", socketPath) + require.NoError(t, err) + + server := &http.Server{ + Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "/healthz", r.URL.Path) + w.WriteHeader(http.StatusNoContent) + }), + ReadHeaderTimeout: time.Second, + TLSConfig: newSelfSignedTLSConfig(t), + } + + errCh := make(chan error, 1) + go func() { + errCh <- server.ServeTLS(listener, "", "") + }() + + // The connection is upgraded to HTTP/2, which keeps a graceful shutdown waiting for the idle client connection + t.Cleanup(func() { + require.NoError(t, server.Close()) + require.ErrorIs(t, <-errCh, http.ErrServerClosed) + }) + + result, err := healthcheck(t.Context(), healthcheckFlags{ + Endpoint: "https://localhost:1411", + UnixSocket: socketPath, + }) + + require.NoError(t, err) + require.Equal(t, http.StatusNoContent, result.StatusCode) +} + +// The healthcheck must reach a server that serves TLS with a self-signed certificate +func TestHealthcheckTLSSelfSignedCertificate(t *testing.T) { + setTLSFiles(t) + + server := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + assert.Equal(t, "/healthz", r.URL.Path) + w.WriteHeader(http.StatusNoContent) + })) + defer server.Close() + + result, err := healthcheck(t.Context(), healthcheckFlags{ + Endpoint: server.URL, + }) + + require.NoError(t, err) + require.Equal(t, http.StatusNoContent, result.StatusCode) +} + +func TestDefaultEndpoint(t *testing.T) { + t.Run("uses http if no TLS certificate is configured", func(t *testing.T) { + assert.Equal(t, "http://localhost:"+common.EnvConfig.Port, defaultEndpoint()) + }) + + t.Run("uses https if a TLS certificate is configured", func(t *testing.T) { + setTLSFiles(t) + assert.Equal(t, "https://localhost:"+common.EnvConfig.Port, defaultEndpoint()) + }) +} + +// t.TempDir embeds the test name, which can exceed the maximum socket path length +func shortTempDir(t *testing.T) string { + t.Helper() + + dir, err := os.MkdirTemp("", "pid") + require.NoError(t, err) + t.Cleanup(func() { + _ = os.RemoveAll(dir) + }) + + return dir +} + +// Only the presence of the paths matters, the healthcheck never reads the files +func setTLSFiles(t *testing.T) { + t.Helper() + + certFile, keyFile := common.EnvConfig.TLSCertFile, common.EnvConfig.TLSKeyFile + t.Cleanup(func() { + common.EnvConfig.TLSCertFile, common.EnvConfig.TLSKeyFile = certFile, keyFile + }) + + common.EnvConfig.TLSCertFile = "cert.pem" + common.EnvConfig.TLSKeyFile = "key.pem" +} + +func newSelfSignedTLSConfig(t *testing.T) *tls.Config { + t.Helper() + + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + require.NoError(t, err) + + now := time.Now() + template := &x509.Certificate{ + SerialNumber: big.NewInt(1), + DNSNames: []string{"localhost"}, + NotBefore: now.Add(-time.Minute), + NotAfter: now.Add(time.Hour), + KeyUsage: x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + } + certificateDER, err := x509.CreateCertificate(rand.Reader, template, template, &privateKey.PublicKey, privateKey) + require.NoError(t, err) + + return &tls.Config{ + Certificates: []tls.Certificate{{ + Certificate: [][]byte{certificateDER}, + PrivateKey: privateKey, + }}, + MinVersion: tls.VersionTLS13, + } +} diff --git a/backend/internal/common/env_config.go b/backend/internal/common/env_config.go index 601f92da..578b6155 100644 --- a/backend/internal/common/env_config.go +++ b/backend/internal/common/env_config.go @@ -46,6 +46,7 @@ type EnvConfigSchema struct { DbProvider DbProvider DbConnectionString string `env:"DB_CONNECTION_STRING" options:"file"` TrustProxy TrustProxyConfig `env:"TRUST_PROXY"` + ProxyProtocol TrustProxyConfig `env:"PROXY_PROTOCOL"` TrustedPlatform string `env:"TRUSTED_PLATFORM"` AuditLogRetentionDays int `env:"AUDIT_LOG_RETENTION_DAYS"` AnalyticsDisabled bool `env:"ANALYTICS_DISABLED"` @@ -158,6 +159,9 @@ func ValidateEnvConfig(config *EnvConfigSchema) error { if config.SystemdSocket && config.UnixSocket != "" { return errors.New("SYSTEMD_SOCKET and UNIX_SOCKET are mutually exclusive") } + if len(config.ProxyProtocol) > 0 && config.UnixSocket != "" { + return errors.New("PROXY_PROTOCOL and UNIX_SOCKET are mutually exclusive") + } if config.AuditLogRetentionDays <= 0 { return errors.New("AUDIT_LOG_RETENTION_DAYS must be greater than 0") @@ -414,6 +418,11 @@ func (config *TrustProxyConfig) UnmarshalText(text []byte) error { proxies := strings.Split(value, ",") for i, proxy := range proxies { proxy = strings.TrimSpace(proxy) + if net.ParseIP(proxy) == nil { + if _, _, err := net.ParseCIDR(proxy); err != nil { + return fmt.Errorf("invalid proxy IP address or CIDR %q", proxy) + } + } proxies[i] = proxy } diff --git a/backend/internal/common/env_config_test.go b/backend/internal/common/env_config_test.go index 2eabdbcc..c13cc6d5 100644 --- a/backend/internal/common/env_config_test.go +++ b/backend/internal/common/env_config_test.go @@ -118,6 +118,7 @@ func TestParseEnvConfig(t *testing.T) { t.Setenv("METRICS_ENABLED", "true") t.Setenv("TRACING_ENABLED", "false") t.Setenv("TRUST_PROXY", "true") + t.Setenv("PROXY_PROTOCOL", "true") t.Setenv("ANALYTICS_DISABLED", "false") t.Setenv("ALLOW_INSECURE_CALLBACK_URLS", "false") @@ -125,6 +126,7 @@ func TestParseEnvConfig(t *testing.T) { require.NoError(t, err) assert.True(t, EnvConfig.UiConfigDisabled) assert.Equal(t, TrustProxyConfig{"0.0.0.0/0", "::/0"}, EnvConfig.TrustProxy) + assert.Equal(t, TrustProxyConfig{"0.0.0.0/0", "::/0"}, EnvConfig.ProxyProtocol) assert.False(t, EnvConfig.AnalyticsDisabled) assert.False(t, EnvConfig.AllowInsecureCallbackURLs) }) @@ -147,6 +149,34 @@ func TestParseEnvConfig(t *testing.T) { assert.Nil(t, EnvConfig.TrustProxy) }) + t.Run("should parse PROXY protocol trusted proxy IP addresses and CIDR ranges", func(t *testing.T) { + EnvConfig = defaultConfig() + t.Setenv("PROXY_PROTOCOL", "10.0.0.0/8, 192.168.1.10, ::1/128") + + err := parseAndValidateEnvConfig(t) + require.NoError(t, err) + assert.Equal(t, TrustProxyConfig{"10.0.0.0/8", "192.168.1.10", "::1/128"}, EnvConfig.ProxyProtocol) + }) + + t.Run("should reject an invalid PROXY protocol trusted proxy", func(t *testing.T) { + EnvConfig = defaultConfig() + t.Setenv("PROXY_PROTOCOL", "not-an-ip") + + err := parseAndValidateEnvConfig(t) + require.Error(t, err) + assert.ErrorContains(t, err, "invalid proxy IP address or CIDR") + }) + + t.Run("should reject PROXY protocol with a UNIX socket", func(t *testing.T) { + EnvConfig = defaultConfig() + t.Setenv("PROXY_PROTOCOL", "true") + t.Setenv("UNIX_SOCKET", "/tmp/pocket-id.sock") + + err := parseAndValidateEnvConfig(t) + require.Error(t, err) + assert.ErrorContains(t, err, "PROXY_PROTOCOL and UNIX_SOCKET are mutually exclusive") + }) + t.Run("should allow insecure callback URLs by default", func(t *testing.T) { assert.True(t, defaultConfig().AllowInsecureCallbackURLs) }) diff --git a/backend/internal/service/user_service.go b/backend/internal/service/user_service.go index 07be9c67..0f974e0d 100644 --- a/backend/internal/service/user_service.go +++ b/backend/internal/service/user_service.go @@ -301,15 +301,26 @@ func (s *UserService) CreateUserInternal(ctx context.Context, input dto.UserCrea return model.User{}, err } + // Bump the UpdatedAt timestamp of the groups the new user was added to + // This is necessary for SCIM to work with the newly-created user, or groups may not be synced via SCIM + if len(userGroups) > 0 { + err = s.touchUserGroups(ctx, tx, groupIDs(userGroups)) + if err != nil { + return model.User{}, err + } + } + // Apply default groups and claims for new non-LDAP users if !isLdapSync { if len(input.UserGroupIds) == 0 { - if err := s.applyDefaultGroups(ctx, &user, tx); err != nil { + err = s.applyDefaultGroups(ctx, &user, tx) + if err != nil { return model.User{}, err } } - if err := s.applyDefaultCustomClaims(ctx, &user, tx); err != nil { + err = s.applyDefaultCustomClaims(ctx, &user, tx) + if err != nil { return model.User{}, err } } @@ -348,11 +359,43 @@ func (s *UserService) applyDefaultGroups(ctx context.Context, user *model.User, if err != nil { return fmt.Errorf("failed to associate default user groups: %w", err) } + + // Bump the groups' UpdatedAt so the SCIM sync picks up the new + // membership (see touchUserGroups for details). + touchIDs := make([]string, len(groups)) + for i := range groups { + touchIDs[i] = groups[i].ID + } + if err := s.touchUserGroups(ctx, tx, touchIDs); err != nil { + return fmt.Errorf("failed to update default user groups timestamp: %w", err) + } } } return nil } +// touchUserGroups updates the UpdatedAt timestamp of the given user groups. +// +// Group membership is stored in the user_groups_users join table, so adding or +// removing a member does not modify the group row itself. The SCIM sync only +// re-pushes a group to the provider when its UpdatedAt is not older than the +// remote resource's last-modified time, so any code path that changes a group's +// membership must bump this timestamp explicitly. Otherwise the membership +// change is never synced to the SCIM provider. +func (s *UserService) touchUserGroups(ctx context.Context, tx *gorm.DB, ids []string) error { + if len(ids) == 0 { + return nil + } + + now := datatype.DateTime(time.Now()) + return tx. + WithContext(ctx). + Model(&model.UserGroup{}). + Where("id IN ?", ids). + Update("updated_at", now). + Error +} + func (s *UserService) applyDefaultCustomClaims(ctx context.Context, user *model.User, tx *gorm.DB) error { config := s.appConfigService.GetDbConfig() diff --git a/backend/internal/service/user_service_test.go b/backend/internal/service/user_service_test.go new file mode 100644 index 00000000..3799b36f --- /dev/null +++ b/backend/internal/service/user_service_test.go @@ -0,0 +1,104 @@ +package service + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + + "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/storage" + testutils "github.com/pocket-id/pocket-id/backend/internal/utils/testing" +) + +func newTestUserService(t *testing.T, appConfig *AppConfigService) (*UserService, *UserGroupService) { + t.Helper() + + db := testutils.NewDatabaseForTest(t) + + fileStorage, err := storage.NewDatabaseStorage(db) + require.NoError(t, err) + + userService := NewUserService( + db, + nil, + nil, + nil, + appConfig, + NewCustomClaimService(db), + NewAppImagesService(map[string]string{}, fileStorage), + nil, + fileStorage, + ) + groupService := NewUserGroupService(db, appConfig, nil) + + return userService, groupService +} + +func TestCreateUserBumpsGroupUpdatedAt(t *testing.T) { + appConfig := NewTestAppConfigService(&model.AppConfig{ + RequireUserEmail: model.AppConfigVariable{Value: "false"}, + }) + userService, groupService := newTestUserService(t, appConfig) + + group, err := groupService.Create(t.Context(), dto.UserGroupCreateDto{ + Name: "members", + FriendlyName: "Members", + }) + require.NoError(t, err) + require.Nil(t, group.UpdatedAt, "a freshly created group has no UpdatedAt yet") + + // Create a user that is a member of the group + // This mirrors signing up via an invite link that adds the user to a group + email := "member@example.com" + _, err = userService.CreateUser(t.Context(), dto.UserCreateDto{ + Username: "member", + Email: &email, + FirstName: "Group", + LastName: "Member", + UserGroupIds: []string{group.ID}, + }) + require.NoError(t, err) + + // The group's UpdatedAt must now be set + updated, err := groupService.Get(t.Context(), group.ID) + require.NoError(t, err) + require.NotNil(t, updated.UpdatedAt, "creating a group member must bump the group's UpdatedAt") + require.False(t, updated.LastModified().Before(updated.CreatedAt.ToTime()), "group LastModified must not predate its CreatedAt after a membership change") + require.Len(t, updated.Users, 1, "the user should be a member of the group") +} + +func TestCreateUserBumpsDefaultGroupUpdatedAt(t *testing.T) { + appConfig := NewTestAppConfigService(&model.AppConfig{ + RequireUserEmail: model.AppConfigVariable{Value: "false"}, + }) + userService, groupService := newTestUserService(t, appConfig) + + group, err := groupService.Create(t.Context(), dto.UserGroupCreateDto{ + Name: "default", + FriendlyName: "Default", + }) + require.NoError(t, err) + require.Nil(t, group.UpdatedAt) + + // Configure the group as a default signup group + defaultGroups, err := json.Marshal([]string{group.ID}) + require.NoError(t, err) + appConfig.dbConfig.Load().SignupDefaultUserGroupIDs.Value = string(defaultGroups) + + // Create a user without explicit group IDs, so the default groups apply + email := "default@example.com" + _, err = userService.CreateUser(t.Context(), dto.UserCreateDto{ + Username: "defaultmember", + Email: &email, + FirstName: "Default", + LastName: "Member", + }) + require.NoError(t, err) + + updated, err := groupService.Get(t.Context(), group.ID) + require.NoError(t, err) + require.NotNil(t, updated.UpdatedAt, "adding a default group member must bump the group's UpdatedAt") + require.Len(t, updated.Users, 1) +} diff --git a/email-templates/package.json b/email-templates/package.json index 80b1fd02..1adba44f 100644 --- a/email-templates/package.json +++ b/email-templates/package.json @@ -19,7 +19,7 @@ "@types/node": "^25.9.0", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", - "react-email": "6.6.6", + "react-email": "6.7.0", "tsx": "^4.22.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1d6c0f64..fd0de042 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -49,7 +49,7 @@ importers: 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 + version: 25.9.5 '@types/react': specifier: ^19.2.14 version: 19.2.17 @@ -57,8 +57,8 @@ importers: specifier: ^19.2.3 version: 19.2.3(@types/react@19.2.17) react-email: - specifier: 6.6.6 - version: 6.6.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + specifier: 6.7.0 + version: 6.7.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) tsx: specifier: ^4.22.2 version: 4.23.0 @@ -79,13 +79,13 @@ importers: version: 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/semantic-conventions': specifier: ^1.41.1 - version: 1.41.1 + version: 1.43.0 '@simplewebauthn/browser': specifier: ^13.3.0 version: 13.3.0 '@tailwindcss/vite': specifier: ^4.3.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)) + version: 4.3.2(vite@8.1.4(@types/node@25.9.5)(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 @@ -100,10 +100,10 @@ importers: version: 1.5.4 runed: specifier: ^0.37.1 - 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) + version: 0.37.1(@sveltejs/kit@2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(zod@4.4.3) sveltekit-superforms: specifier: ^2.30.1 - 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) + version: 2.30.2(@sveltejs/kit@2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(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.63.0))(typescript@6.0.3) tailwind-merge: specifier: ^3.6.0 version: 3.6.0 @@ -113,7 +113,7 @@ importers: devDependencies: '@inlang/paraglide-js': specifier: ^2.18.0 - version: 2.20.2(typescript@6.0.3) + version: 2.21.0(typescript@6.0.3) '@inlang/plugin-m-function-matcher': specifier: ^2.2.6 version: 2.2.9 @@ -125,25 +125,25 @@ importers: version: 3.12.2 '@lucide/svelte': specifier: ^1.16.0 - version: 1.23.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)) + version: 1.24.0(svelte@5.56.4(@typescript-eslint/types@8.63.0)) '@sveltejs/adapter-static': specifier: ^3.0.10 - 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))) + version: 3.0.10(@sveltejs/kit@2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0))) '@sveltejs/kit': specifier: ^2.60.1 - 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)) + version: 2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(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.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)) + version: 7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(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 + version: 25.9.5 '@types/qrcode': specifier: ^1.5.6 version: 1.5.6 bits-ui: specifier: ^2.18.1 - 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)) + version: 2.18.1(@internationalized/date@3.12.2)(@sveltejs/kit@2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0)) eslint: specifier: ^10.4.0 version: 10.6.0(jiti@2.7.0) @@ -152,37 +152,37 @@ importers: 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.6.0(jiti@2.7.0))(svelte@5.56.4(@typescript-eslint/types@8.62.1)) + version: 3.20.0(eslint@10.6.0(jiti@2.7.0))(svelte@5.56.4(@typescript-eslint/types@8.63.0)) formsnap: specifier: ^2.0.1 - 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)) + version: 2.0.1(svelte@5.56.4(@typescript-eslint/types@8.63.0))(sveltekit-superforms@2.30.2(@sveltejs/kit@2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(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.63.0))(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.1)) + version: 1.1.0(svelte@5.56.4(@typescript-eslint/types@8.63.0)) prettier: specifier: ^3.8.3 - version: 3.9.4 + version: 3.9.5 prettier-plugin-svelte: specifier: ^3.5.2 - version: 3.5.2(prettier@3.9.4)(svelte@5.56.4(@typescript-eslint/types@8.62.1)) + version: 3.5.2(prettier@3.9.5)(svelte@5.56.4(@typescript-eslint/types@8.63.0)) prettier-plugin-tailwindcss: specifier: ^0.8.0 - 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) + version: 0.8.0(prettier-plugin-svelte@3.5.2(prettier@3.9.5)(svelte@5.56.4(@typescript-eslint/types@8.63.0)))(prettier@3.9.5) shadcn-svelte: specifier: ^1.3.0 - version: 1.3.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)) + version: 1.4.1(svelte@5.56.4(@typescript-eslint/types@8.63.0)) svelte: specifier: ^5.55.8 - version: 5.56.4(@typescript-eslint/types@8.62.1) + version: 5.56.4(@typescript-eslint/types@8.63.0) svelte-check: specifier: ^4.4.8 - version: 4.7.1(picomatch@4.0.5)(svelte@5.56.4(@typescript-eslint/types@8.62.1))(typescript@6.0.3) + version: 4.7.2(picomatch@4.0.5)(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3) svelte-sonner: specifier: ^1.1.1 - version: 1.1.1(svelte@5.56.4(@typescript-eslint/types@8.62.1)) + version: 1.1.1(svelte@5.56.4(@typescript-eslint/types@8.63.0)) tailwind-variants: specifier: ^3.2.2 version: 3.2.2(tailwind-merge@3.6.0)(tailwindcss@4.3.2) @@ -200,13 +200,13 @@ importers: version: 6.0.3 typescript-eslint: specifier: ^8.59.4 - version: 8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) + version: 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) vite: specifier: ^8.0.16 - 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) + version: 8.1.4(@types/node@25.9.5)(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.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) + version: 0.5.1(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) tests: dependencies: @@ -222,7 +222,7 @@ importers: version: 0.5.8 '@types/node': specifier: ^25.9.0 - version: 25.9.4 + version: 25.9.5 dotenv: specifier: ^17.4.2 version: 17.4.2 @@ -231,7 +231,7 @@ importers: version: 6.2.3 prettier: specifier: ^3.8.3 - version: 3.9.4 + version: 3.9.5 packages: @@ -672,8 +672,8 @@ packages: cpu: [x64] os: [win32] - '@inlang/paraglide-js@2.20.2': - resolution: {integrity: sha512-V8iY3uu/vQU94gEag1bdC3glMJSp4Dg3XMwfnabZLBh1Dv0F++DvDYlMeniqv2+nHbnS/twB75AM140OmpHDEg==} + '@inlang/paraglide-js@2.21.0': + resolution: {integrity: sha512-t+OOui1i5p73zQ2Dk28yFzu6rZhh7hsDOCHjkXDmSwFFsUsS5HyUD4CvPJ+wf02qgmlCMu2z1qffJ5aWwgx1CA==} hasBin: true peerDependencies: typescript: '>=5.6' @@ -720,8 +720,8 @@ packages: '@lix-js/server-protocol-schema@0.1.1': resolution: {integrity: sha512-jBeALB6prAbtr5q4vTuxnRZZv1M2rKe8iNqRQhFJ4Tv7150unEa0vKyz0hs8Gl3fUGsWaNJBh3J8++fpbrpRBQ==} - '@lucide/svelte@1.23.0': - resolution: {integrity: sha512-3LQbKXx9vId6Nx4E2Nu2qwgJfdmr5+CVeVJbxe5cy+HcnCRd9QVVtZXqvgBYAV1OJrPmQAf9/3gJWLCpASC/Ng==} + '@lucide/svelte@1.24.0': + resolution: {integrity: sha512-yXwewA7ANQ5hfaSDrvsecosWjZn5RglzeXUZRSnxeANBskpNwblOkEJTqD0ujDdNKIKL8E9eVc2U/P3ziJr7OA==} peerDependencies: svelte: ^5 @@ -854,12 +854,12 @@ packages: peerDependencies: '@opentelemetry/api': '>=1.3.0 <1.10.0' - '@opentelemetry/semantic-conventions@1.41.1': - resolution: {integrity: sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA==} + '@opentelemetry/semantic-conventions@1.43.0': + resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==} engines: {node: '>=14'} - '@oxc-project/types@0.138.0': - resolution: {integrity: sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA==} + '@oxc-project/types@0.139.0': + resolution: {integrity: sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==} '@playwright/test@1.61.1': resolution: {integrity: sha512-8nKv6+0RJSL9FE4jYOEGXnPeM/Hg12qZpmqzZjRh3qM0Y7c3z1mrOTfFLids72RDQYVh9WpLEfR5WdpNX4fkig==} @@ -988,15 +988,15 @@ packages: peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc - '@react-email/render@2.0.10': - resolution: {integrity: sha512-QbgVvXeYenVi0LqkO+upcZzbyrD5Tz2wwCV+6CSzgo6ZsvwHLjf0x5Sr1C7DepcMhjYo++maJWRbcSoKMXKr1g==} + '@react-email/render@2.0.6': + resolution: {integrity: sha512-xOzaYkH3jLZKqN5MqrTXYnmqBYUnZSVbkxdb5PGGmDcK6sKDVMliaDiSwfXajRC9JtSHTcGc2tmGLHWuCgVpog==} engines: {node: '>=20.0.0'} peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc react-dom: ^18.0 || ^19.0 || ^19.0.0-rc - '@react-email/render@2.0.6': - resolution: {integrity: sha512-xOzaYkH3jLZKqN5MqrTXYnmqBYUnZSVbkxdb5PGGmDcK6sKDVMliaDiSwfXajRC9JtSHTcGc2tmGLHWuCgVpog==} + '@react-email/render@2.1.0': + resolution: {integrity: sha512-F+zE3O6d6sW6Aj2UjvZAA17R7tJKM7kcq2mgV6k4HCT8jeLLFaVP2txMtH1lgqYFRMZ0Gxsd37q2PRyiXLXXxA==} engines: {node: '>=20.0.0'} peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc @@ -1062,97 +1062,97 @@ packages: peerDependencies: react: ^18.0 || ^19.0 || ^19.0.0-rc - '@rolldown/binding-android-arm64@1.1.4': - resolution: {integrity: sha512-EZLpf/8y7GXkkra90ML47kzik/GMP3EMcE9bPyHmRfxLC6z9+aW5A8poCsoxjrT5GfEcNAAvWwUHjvP1pUQkfw==} + '@rolldown/binding-android-arm64@1.1.5': + resolution: {integrity: sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.1.4': - resolution: {integrity: sha512-aUi+HBvmYb7j8krl1+qJgkG8C17fO79gk3c+jPw4S8glRFc1DTija9S3EyaTSQUm5GJXYKDAsugBEhFHH2vYiQ==} + '@rolldown/binding-darwin-arm64@1.1.5': + resolution: {integrity: sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.1.4': - resolution: {integrity: sha512-F7hHC3gwY11+vByKPRWqwGbeXWVgKmL+pTGCinaEhdihzBV2aQ0fvZOch9cXYUOKuKKq429HeYXOqQLc7wFCEg==} + '@rolldown/binding-darwin-x64@1.1.5': + resolution: {integrity: sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.1.4': - resolution: {integrity: sha512-sI5yw+7s92SK6odiEhD5lKCBlWcpjHS5qyqpVQbZAJ0fIzEUXrmbl3DH2ybR3PZogulNJF+COLtmA8hUfvkCCQ==} + '@rolldown/binding-freebsd-x64@1.1.5': + resolution: {integrity: sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.1.4': - resolution: {integrity: sha512-mCi0OKgEieFircrtVYmQAFGszRtMnZ6fpZAXrxanXAu7lqZcsK1E1RAaZNG0uKAnxox3B1f4EyQNnoyMfN1vAA==} + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': + resolution: {integrity: sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.1.4': - resolution: {integrity: sha512-B9Ial3Kv5sh0SHnB1g/QWcUQCEvCF6QKGAl4zXypYj65mVI+B4AhFBwPtSN7pDrJeIx8Z7zdy4ntx+wQABom7w==} + '@rolldown/binding-linux-arm64-gnu@1.1.5': + resolution: {integrity: sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.1.4': - resolution: {integrity: sha512-lZVym0PuHE1KZ22gmFTC15lAkrg9iTszR617oYRB/iPY1A56ywoJzVKOJBKaot5RiikCObmur6pogpse3gRcng==} + '@rolldown/binding-linux-arm64-musl@1.1.5': + resolution: {integrity: sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.1.4': - resolution: {integrity: sha512-t2DNiLJWNTbnEHyUzTumldML6ET4/g16467LZoDDJ3tSxGvguL5/NyC2lCsNKuyRycg9XeDQF5SSv+TNOhQEXg==} + '@rolldown/binding-linux-ppc64-gnu@1.1.5': + resolution: {integrity: sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.1.4': - resolution: {integrity: sha512-0WIRnL1Uw4BvTZRLQt+PVgo6ZKTJadlC2btP+/EOXv2f/DWbY0rEgl+y834mIVwP1FkTlWVTrGGJXf12lru7EQ==} + '@rolldown/binding-linux-s390x-gnu@1.1.5': + resolution: {integrity: sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.1.4': - resolution: {integrity: sha512-JWtGshGfX+oENAKonoNkqEJX+7hC8yfhi9GUyPX1VX4mdh1y5r+ZiJLR5XzAB0aoP6s/PcILsGjKq8O0mm24bw==} + '@rolldown/binding-linux-x64-gnu@1.1.5': + resolution: {integrity: sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.1.4': - resolution: {integrity: sha512-rT6yQcxUuXs4CnbofqwHRRV0iem349rLMYpTjkgQGLjrY4ado/eDzwPZPTCgTOlF6Nkp8NEv70yLMTn6qkWxsQ==} + '@rolldown/binding-linux-x64-musl@1.1.5': + resolution: {integrity: sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@rolldown/binding-openharmony-arm64@1.1.4': - resolution: {integrity: sha512-KXMGoboq5cyaCQjDA4GLuRiOwBQ0EyFnJoVViLeZ45/3rFItRODEr+NdsBcVpll40hhNArlm/speWGRvj08LzA==} + '@rolldown/binding-openharmony-arm64@1.1.5': + resolution: {integrity: sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.1.4': - resolution: {integrity: sha512-5K83rb36oJiY7BCyE9zLZtGcPV4g5wvq+xwdO0XPIwDVZI8cyB/AUjkNXGb92/rnmezEkjMOpgY61rtwjQtFwg==} + '@rolldown/binding-wasm32-wasi@1.1.5': + resolution: {integrity: sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.1.4': - resolution: {integrity: sha512-PnWBtw3TV5KOg69HQQDR0mnQuyCmSGR2pAB4DC1rPF808fgKeTUMj2EOEyKATpgiuxuR5APQmiDO7PDgEjTFSA==} + '@rolldown/binding-win32-arm64-msvc@1.1.5': + resolution: {integrity: sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.1.4': - resolution: {integrity: sha512-M1lpniBePobTfsa7Ks9a199e1akxsXn+GYBUKsEzv3YFzOm1HJAMNwKI3qr0Zq+mxwx9gOZoTdP1yXRYsZUocQ==} + '@rolldown/binding-win32-x64-msvc@1.1.5': + resolution: {integrity: sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -1175,8 +1175,8 @@ packages: '@simplewebauthn/browser@13.3.0': resolution: {integrity: sha512-BE/UWv6FOToAdVk0EokzkqQQDOWtNydYlY6+OrmiZ5SCNmb41VehttboTetUM3T/fr6EAFYVXjz4My2wg230rQ==} - '@sinclair/typebox@0.31.28': - resolution: {integrity: sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ==} + '@sinclair/typebox@0.31.30': + resolution: {integrity: sha512-MGsM7bVmHg3sUKCphlu3SGQ+T+5JTbygZWYArfKQCW5anJeACHeqLhcnf+R7XlCWZ+QR3C8JTBx3kCKJTN69ow==} '@socket.io/component-emitter@3.1.2': resolution: {integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==} @@ -1193,13 +1193,18 @@ packages: peerDependencies: acorn: ^8.9.0 + '@sveltejs/acorn-typescript@1.0.11': + resolution: {integrity: sha512-LFuZUkjJ9iF7JZye/aG5XM0SFcQ5VyL0oVX4WJ9dc0Va3R3s0OauX1BESVCb+YN/ol8TAfqGDDAQsTG627Y5kw==} + peerDependencies: + acorn: ^8.9.0 + '@sveltejs/adapter-static@3.0.10': resolution: {integrity: sha512-7D9lYFWJmB7zxZyTE/qxjksvMqzMuYrrsyh1f4AlZqeZeACPRySjbC3aFiY55wb1tWUaKOQG9PVbm74JcN2Iew==} peerDependencies: '@sveltejs/kit': ^2.0.0 - '@sveltejs/kit@2.69.1': - resolution: {integrity: sha512-+nz8Fx/cElzb2ZPHC+6Ll3y3NEVIe4Na75PeplLlyTmd1dBXAjz2KR14y1ZgNjb2ThfAYzulu+PFy1UE3RCUzA==} + '@sveltejs/kit@2.69.2': + resolution: {integrity: sha512-CMdPDbYjRwRu4KXTxBVMuOpFPCt1i/v0ANennotec+K9Cmb2e3w2yYzJiC6Vh/WSvm9Khi5sJMZa0rJPqfHlDw==} engines: {node: '>=18.13'} hasBin: true peerDependencies: @@ -1218,8 +1223,8 @@ packages: resolution: {integrity: sha512-1LgZ/qUqSoq+QorD83lk2hka79Px0wXNW2q5V1nZlxGhQgw1jrsIbVz5YiCeucVLo4XvFLjXukUaQjIiqowkcg==} engines: {node: '>= 18.0.0'} - '@sveltejs/vite-plugin-svelte@7.1.2': - resolution: {integrity: sha512-DrUBA2UXRfDmUX/ZTiEopd3X40yavsJF1FX2RygcuIScHL7o5YX1fMvoYnDhjeJQC4weCOklirpNWlcb2NiSeA==} + '@sveltejs/vite-plugin-svelte@7.2.0': + resolution: {integrity: sha512-1SpkuMSRLfugrVX+IrKfE1RUegzo8AQzKQ6qQPfVzbcWi5IhuTPaKb5ZrLpucleFznkc4/RTeSPoRnGWFxX+EQ==} engines: {node: ^20.19 || ^22.12 || >=24} peerDependencies: svelte: ^5.46.4 @@ -1346,8 +1351,8 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - '@types/node@25.9.4': - resolution: {integrity: sha512-dszCsrKb5U7ZsVZBWiHFklTloVl0mSEnWH/iZXfZUlI4rzCUnsvGmgqfuVRHL54ugE7/wRuxEIXRa2iMZ+BG6g==} + '@types/node@25.9.5': + resolution: {integrity: sha512-OScDchr2fwuUmWdf4kZ9h7PcJiYDVInhJizG/biAq3cAvqwYktuy/TYGGdZNMtNTFUP7rnb0NU4TUdm82kt4Rg==} '@types/qrcode@1.5.6': resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==} @@ -1385,63 +1390,63 @@ packages: '@types/json-schema': optional: true - '@typescript-eslint/eslint-plugin@8.62.1': - resolution: {integrity: sha512-4EQM77WgVNxj7OkL/5b/D/xZsw00G577+UriYTC7JF5opcF3T2AuoeY7ueLaZgSVjSgCS6yOAJB5bRGLPSJUzA==} + '@typescript-eslint/eslint-plugin@8.63.0': + resolution: {integrity: sha512-rvwSgqT+DHpWdzfSzPatRLm02a0GlESt++9iy3hLCDY4BgkaLcl8LBi9Yh7XGFBpwcBE/K3024QuXWTpbz4FfQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.62.1 + '@typescript-eslint/parser': ^8.63.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.62.1': - resolution: {integrity: sha512-sPhE4iHuJDSvoAiec+Ro8JyXw8f0ql13HFR82P99nCm9GwTEKG0KYLvDe6REk8BCXuit6vJAv/Yxg5ABaNS2rA==} + '@typescript-eslint/parser@8.63.0': + resolution: {integrity: sha512-gwh4gvvlaVDKKxyfxMG+Gnu1u9X0OQBwyGLkbwB65dIzBKnxeRiJlNFqlI3zwVhNXJIs6qV7mlFCn/BIajlVig==} 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.1': - resolution: {integrity: sha512-yQ3RgY5RkSBpsNS1Bx/JQEcA24FOSdfGktoyprAr5u18390UQdtVcfnEv4nIrIshNnavlVyZBKxQwT1fIAE6cg==} + '@typescript-eslint/project-service@8.63.0': + resolution: {integrity: sha512-e5dh0/UI0ok53AlZ5wRkXCB32z/f2jUZqPR/ygAw5WYaSw8j9EoJWlS7wQjr/dmOaqWjnPIn2m+HhVPCMWGZVQ==} 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.1': - resolution: {integrity: sha512-r4d249KbQ1SFdpeStvob8Ih6aPPIzfqllPVOtvhve6ZcpuVcYo5/7zUWckKpHE7StASX4kTKZTLf0WQm/wPkcg==} + '@typescript-eslint/scope-manager@8.63.0': + resolution: {integrity: sha512-uUyfMWCnDSN8bCpcrY8nGP2BLkQ9Xn0GsipcONcpIDWhwhO4ZSyHvyS14U3X75mzxWxL3I2UZIrenTzdzcJO8A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.62.1': - resolution: {integrity: sha512-xadytJqX9vJVQ2fdQjkcIVigwaOJNWkpjdLt6cEQ+xPnrI1fkp+/jZE/I97k9KUjqtpd25i0HeyZf3T6dutv2g==} + '@typescript-eslint/tsconfig-utils@8.63.0': + resolution: {integrity: sha512-sUAbkulqBAsncKnbRP3+7CtQFRKicexnj7ZwNC6ddCR7EmrXvjvdCYMJbUIqMd6lwoEriZjwLo08aS5tSjVMHg==} 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.1': - resolution: {integrity: sha512-aXM5xlqXiTxPibXB93cLAURfT3rlizf7uMXISCXy66Isr/9hISJx3yDsKl0L7lKa51b8JpFuNKby0/O0pEm9jg==} + '@typescript-eslint/type-utils@8.63.0': + resolution: {integrity: sha512-Nzzh/OGxVCOjObjaj1CQF2RUasyYy2Jfuh+zZ3PjLzG2fYRriAiZLib9UKtO+CpQAS3YHiAS+ckZDclwqI1TPA==} 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.1': - resolution: {integrity: sha512-ooCzJFaf+Hg+uG6fA3NRFGuFjlfNlDhBthbv4ZPU/0elCAFUfnyXUvf/WOpHz/jYwSmvU2GkR2LtyUfy1AxZ1Q==} + '@typescript-eslint/types@8.63.0': + resolution: {integrity: sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.62.1': - resolution: {integrity: sha512-xMcW9oP9u7fAMXYs9A65CVmtLQe2r//oXINHfi8HV+oiqhih17sbLdhXr4540YWlgpDKQdY854OL5ZrdCiQsAA==} + '@typescript-eslint/typescript-estree@8.63.0': + resolution: {integrity: sha512-ygBkU+B7ex5UI/gKhaqexWev79uISfIv7XQCRNYO/jmD8rGLPyWLAb3KMRT6nd8Gt9bmUBi9+iX6tBdYfOY81Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.62.1': - resolution: {integrity: sha512-sHtbPfuKNZCG+ih8SyjjucqRntSVmp8XgL5u6o9mAhiSn8ds5o/M/XdM0abweme2Tln3szOstOrZ9OXitvPh0g==} + '@typescript-eslint/utils@8.63.0': + resolution: {integrity: sha512-fUKaeAvrTuQg/Tgt3nliAUSZHJM6DlCcfyEmxCvlX8kieWSStBX+5O5Fnidtc3i2JrH+9c/GL4RY2iasd/GPTA==} 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.1': - resolution: {integrity: sha512-4g3BLxfdTMy8iZG0MaBkadnlRrCJ74cQiFbyEVMrkwIoqdyaXXQM22cotDvrl4x28wgIZ9rEJRoM+mmhSJpJ1g==} + '@typescript-eslint/visitor-keys@8.63.0': + resolution: {integrity: sha512-UexrHGnGTpbuQHct2ExOc2ZcFbGUS9FOesCxxqdBGcpI1BxYu/LZ6U8Aq6/72XtF/qRBk9nhuGHFJIXXMhPMdw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@valibot/to-json-schema@1.7.1': @@ -2019,6 +2024,9 @@ packages: resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==} engines: {node: '>=14'} + html5parser@3.0.0: + resolution: {integrity: sha512-iNpSopa+4YHX50UOk825tBy7MghmXHo/ZpLskBYN0kAr1xhH8GlIMk5bLRXcZlfP3AnLUcSuFMu8C4MdOUxA8A==} + htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} @@ -2034,8 +2042,8 @@ packages: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} - ignore@7.0.5: - resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + ignore@7.0.6: + resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} engines: {node: '>= 4'} imurmurhash@0.1.4: @@ -2133,8 +2141,8 @@ packages: known-css-properties@0.37.0: resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==} - kysely@0.29.2: - resolution: {integrity: sha512-s6WVJyEZrbm6jhBpiKHsGHyePMrVQKJ85wZCFCr9W4QHv6WTjWIrdvTmO9hDEA3bNK0xkrE2DqrHsXMLWuZpQg==} + kysely@0.29.3: + resolution: {integrity: sha512-VHtBdW6XB/pgoTSqraM3UAa2rYoYdNXqnNPpX+8XXP+cwYbVEFuAp3HyPt1vpNfU9l7Y2kpUrA9QDPsy8uUqOQ==} engines: {node: '>=22.0.0'} leac@0.6.0: @@ -2318,6 +2326,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -2472,6 +2485,10 @@ packages: resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} engines: {node: ^10 || ^12 || >=14} + postcss@8.5.19: + resolution: {integrity: sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ==} + engines: {node: ^10 || ^12 || >=14} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -2537,8 +2554,8 @@ packages: prettier-plugin-svelte: optional: true - prettier@3.9.4: - resolution: {integrity: sha512-yWG/o/4oJfo036EKAfK6ACAoDOfHeRHx4tuxkfBZiauURiaSmYwlpOr5LQqKtIkRD2z1PLteme2WoxEnj4tHTg==} + prettier@3.9.5: + resolution: {integrity: sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==} engines: {node: '>=14'} hasBin: true @@ -2574,8 +2591,8 @@ packages: peerDependencies: react: ^19.2.7 - react-email@6.6.6: - resolution: {integrity: sha512-4eYmISIQbmEzibkl8pW+ZQHIFMwUVqZQRpTDYj6Ow7kk/gpUjtA95ggxoXji8pRq/y76tOhe7D3vNUJkZWO58g==} + react-email@6.7.0: + resolution: {integrity: sha512-lJFdxPB7A1bLgTFYSzGWxNYyP63IbJsCSe+2EOl51lSQzKCs95+dBgiJdmW+wk7+Oix7UdkWhyJMMvbdQt9HJQ==} engines: {node: '>=20.0.0'} hasBin: true peerDependencies: @@ -2601,8 +2618,8 @@ packages: require-main-filename@2.0.0: resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} - rolldown@1.1.4: - resolution: {integrity: sha512-IjZYiLxZwpnhwhdBH2ugdTGVSdhCQUmLxLoqyjiL0JxYjyRst+5a0P3xfrTxJ5F638j4Mvvw5FAX5XE6eHpXbA==} + rolldown@1.1.5: + resolution: {integrity: sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true @@ -2660,11 +2677,11 @@ packages: set-blocking@2.0.0: resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} - set-cookie-parser@3.1.1: - resolution: {integrity: sha512-vM9SUhjsUYs6UeJUmygc5Ofm5eQGe85riob5ju6XCgFGJI5PLV4nrDAQpQjd+LkFBpAkADn5BQQpZ9EUNkyLuA==} + set-cookie-parser@3.1.2: + resolution: {integrity: sha512-5/r/lTwbJ3zQ+qwdUFZYeRNqda7P5HD8zQKqlSjdGt1/S0cjLAphHusj4Y58ahDtWn/g32xrIS58/ikOvwl0Lw==} - shadcn-svelte@1.3.0: - resolution: {integrity: sha512-Pd4ICWTkTks/b2YU4c9vF2XsX1x5HFPRl5bKszS1LcnWS83x+7T4WiIvbWz8Qh9knkcGZ+SCz1+Dmhdq+AYooA==} + shadcn-svelte@1.4.1: + resolution: {integrity: sha512-lpvnuHJOMf/CRlOMo5h7HpmdpcWmqVoPyvnCcgpJWJWzVkiwmisF80mR54GIAExSTSk1g4+ere3QrSty2PCIIg==} hasBin: true peerDependencies: svelte: ^5.0.0 @@ -2691,8 +2708,8 @@ packages: socket.io-adapter@2.5.8: resolution: {integrity: sha512-6Oy52pbg+kvdCVvjcN+FnY7BvxZ7cIHNScbvztT/It5d0vbwoJoVZmF2gjJmnV0/4WlXRfG15zc45ySk9Ah8bw==} - socket.io-parser@4.2.6: - resolution: {integrity: sha512-asJqbVBDsBCJx0pTqw3WfesSY0iRX+2xzWEWzrpcH7L6fLzrhyF8WPI8UaeM4YCuDfpwA/cgsdugMsmtz8EJeg==} + socket.io-parser@4.2.7: + resolution: {integrity: sha512-IH/iSeO9T6gz1KkFleGDWkG9N3dl4jXVYUtMhIqH10Md0ttMer8nUNWiP1DKuNrybD2xBrixLJdCC9J6ECoYkg==} engines: {node: '>=10.0.0'} socket.io@4.8.3: @@ -2750,8 +2767,8 @@ packages: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} - svelte-check@4.7.1: - resolution: {integrity: sha512-FGUOmAqxXdN/H9Zm8slrqO7SLtFisXRB7rfOsHNJ3MLTD2po/+Stg8XyErkpumPHbuUiYTcqrEIzxpVWKTLqtg==} + svelte-check@4.7.2: + resolution: {integrity: sha512-GoS4XJdGswlq0rIT1vtFLzJY1bvHtY37McY9H9Gkm1Ggw/ICdZYn8J/Z8Yi0BEL0i3R4+jtaWVePjyppMlij/A==} engines: {node: '>= 18.0.0'} hasBin: true peerDependencies: @@ -2823,6 +2840,9 @@ packages: tailwindcss@4.3.2: resolution: {integrity: sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==} + tailwindcss@4.3.3: + resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==} + tapable@2.3.3: resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} engines: {node: '>=6'} @@ -2888,8 +2908,8 @@ packages: typebox@1.3.3: resolution: {integrity: sha512-URXGUE31PJDQC+PtRMJeLdF4kmmOdFoVPikPCtV2oOIhUpNpppEdIz7W8bH8cFYPYHdDpaRvqwdegMTmHliudg==} - typescript-eslint@8.62.1: - resolution: {integrity: sha512-vymnnM5g0AKQDSAyfP12nMIBvgwgA42syg74kkuZ4x1VuTzwQKwc5h9rGxeShCjny5o+zWAb6OEoz7XLgrIkIw==} + typescript-eslint@8.63.0: + resolution: {integrity: sha512-xgwXyzG4sK9ALkBxbyGkTMMOS+imnW65iPhxCQMK83KhxyoDNW7l+IDqEf9vMdoUidHpOoS967RCq4eMiTexwQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 @@ -2949,8 +2969,8 @@ packages: peerDependencies: vite: '>=2.0.0' - vite@8.1.3: - resolution: {integrity: sha512-Ds+gBRbj0lwRO2Y5hwnUBdxSwlAve9LeRyU4sNnAr0ewW0gWF0n5bgXgUzbgZ49MV9BVUAQUFYVcDUcilUExMA==} + vite@8.1.4: + resolution: {integrity: sha512-bTT9PsdWO+MQMNG9ZXIP/qM9wGh37DFxTV/sPq9cFpHr3w4jkgef032PkAL9jAqhk3Nz8NQw3O8n6/xFkqO4QQ==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3022,8 +3042,8 @@ packages: resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} engines: {node: '>=8'} - ws@8.21.0: - resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + ws@8.21.1: + resolution: {integrity: sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -3398,7 +3418,7 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true - '@inlang/paraglide-js@2.20.2(typescript@6.0.3)': + '@inlang/paraglide-js@2.21.0(typescript@6.0.3)': dependencies: '@inlang/recommend-sherlock': 0.2.1 '@inlang/sdk': 2.10.2 @@ -3429,9 +3449,9 @@ snapshots: '@inlang/sdk@2.10.2': dependencies: '@lix-js/sdk': 0.4.10 - '@sinclair/typebox': 0.31.28 - kysely: 0.29.2 - sqlite-wasm-kysely: 0.3.0(kysely@0.29.2) + '@sinclair/typebox': 0.31.30 + kysely: 0.29.3 + sqlite-wasm-kysely: 0.3.0(kysely@0.29.3) uuid: 14.0.1 transitivePeerDependencies: - babel-plugin-macros @@ -3465,17 +3485,17 @@ snapshots: dedent: 1.5.1 human-id: 4.2.0 js-sha256: 0.11.1 - kysely: 0.29.2 - sqlite-wasm-kysely: 0.3.0(kysely@0.29.2) + kysely: 0.29.3 + sqlite-wasm-kysely: 0.3.0(kysely@0.29.3) uuid: 14.0.1 transitivePeerDependencies: - babel-plugin-macros '@lix-js/server-protocol-schema@0.1.1': {} - '@lucide/svelte@1.23.0(svelte@5.56.4(@typescript-eslint/types@8.62.1))': + '@lucide/svelte@1.24.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))': dependencies: - svelte: 5.56.4(@typescript-eslint/types@8.62.1) + svelte: 5.56.4(@typescript-eslint/types@8.63.0) '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: @@ -3519,7 +3539,7 @@ snapshots: '@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/semantic-conventions': 1.41.1 + '@opentelemetry/semantic-conventions': 1.43.0 '@opentelemetry/exporter-trace-otlp-http@0.220.0(@opentelemetry/api@1.9.1)': dependencies: @@ -3550,7 +3570,7 @@ snapshots: dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/semantic-conventions': 1.41.1 + '@opentelemetry/semantic-conventions': 1.43.0 '@opentelemetry/sdk-logs@0.220.0(@opentelemetry/api@1.9.1)': dependencies: @@ -3558,7 +3578,7 @@ snapshots: '@opentelemetry/api-logs': 0.220.0 '@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.43.0 '@opentelemetry/sdk-metrics@2.9.0(@opentelemetry/api@1.9.1)': dependencies: @@ -3572,7 +3592,7 @@ snapshots: '@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/semantic-conventions': 1.43.0 '@opentelemetry/sdk-trace-web@2.9.0(@opentelemetry/api@1.9.1)': dependencies: @@ -3585,11 +3605,11 @@ snapshots: '@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.43.0 - '@opentelemetry/semantic-conventions@1.41.1': {} + '@opentelemetry/semantic-conventions@1.43.0': {} - '@oxc-project/types@0.138.0': {} + '@oxc-project/types@0.139.0': {} '@playwright/test@1.61.1': dependencies: @@ -3702,17 +3722,19 @@ snapshots: dependencies: react: 19.2.7 - '@react-email/render@2.0.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': - dependencies: - html-to-text: 9.0.5 - prettier: 3.9.4 - react: 19.2.7 - react-dom: 19.2.7(react@19.2.7) - '@react-email/render@2.0.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: html-to-text: 9.0.5 - prettier: 3.9.4 + prettier: 3.9.5 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + + '@react-email/render@2.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': + dependencies: + entities: 4.5.0 + html-to-text: 9.0.5 + html5parser: 3.0.0 + prettier: 3.9.5 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) @@ -3728,7 +3750,7 @@ snapshots: dependencies: '@react-email/text': 0.1.6(react@19.2.7) react: 19.2.7 - tailwindcss: 4.3.2 + tailwindcss: 4.3.3 optionalDependencies: '@react-email/body': 0.3.0(react@19.2.7) '@react-email/button': 0.2.1(react@19.2.7) @@ -3745,53 +3767,53 @@ snapshots: dependencies: react: 19.2.7 - '@rolldown/binding-android-arm64@1.1.4': + '@rolldown/binding-android-arm64@1.1.5': optional: true - '@rolldown/binding-darwin-arm64@1.1.4': + '@rolldown/binding-darwin-arm64@1.1.5': optional: true - '@rolldown/binding-darwin-x64@1.1.4': + '@rolldown/binding-darwin-x64@1.1.5': optional: true - '@rolldown/binding-freebsd-x64@1.1.4': + '@rolldown/binding-freebsd-x64@1.1.5': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.1.4': + '@rolldown/binding-linux-arm-gnueabihf@1.1.5': optional: true - '@rolldown/binding-linux-arm64-gnu@1.1.4': + '@rolldown/binding-linux-arm64-gnu@1.1.5': optional: true - '@rolldown/binding-linux-arm64-musl@1.1.4': + '@rolldown/binding-linux-arm64-musl@1.1.5': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.1.4': + '@rolldown/binding-linux-ppc64-gnu@1.1.5': optional: true - '@rolldown/binding-linux-s390x-gnu@1.1.4': + '@rolldown/binding-linux-s390x-gnu@1.1.5': optional: true - '@rolldown/binding-linux-x64-gnu@1.1.4': + '@rolldown/binding-linux-x64-gnu@1.1.5': optional: true - '@rolldown/binding-linux-x64-musl@1.1.4': + '@rolldown/binding-linux-x64-musl@1.1.5': optional: true - '@rolldown/binding-openharmony-arm64@1.1.4': + '@rolldown/binding-openharmony-arm64@1.1.5': optional: true - '@rolldown/binding-wasm32-wasi@1.1.4': + '@rolldown/binding-wasm32-wasi@1.1.5': dependencies: '@emnapi/core': 1.11.1 '@emnapi/runtime': 1.11.1 '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1) optional: true - '@rolldown/binding-win32-arm64-msvc@1.1.4': + '@rolldown/binding-win32-arm64-msvc@1.1.5': optional: true - '@rolldown/binding-win32-x64-msvc@1.1.4': + '@rolldown/binding-win32-x64-msvc@1.1.5': optional: true '@rolldown/pluginutils@1.0.1': {} @@ -3814,7 +3836,7 @@ snapshots: '@simplewebauthn/browser@13.3.0': {} - '@sinclair/typebox@0.31.28': {} + '@sinclair/typebox@0.31.30': {} '@socket.io/component-emitter@3.1.2': {} @@ -3826,15 +3848,19 @@ snapshots: dependencies: acorn: 8.17.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)))': + '@sveltejs/acorn-typescript@1.0.11(acorn@8.17.0)': dependencies: - '@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)) + acorn: 8.17.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/adapter-static@3.0.10(@sveltejs/kit@2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))': + dependencies: + '@sveltejs/kit': 2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) + + '@sveltejs/kit@2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(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.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)) + '@sveltejs/acorn-typescript': 1.0.11(acorn@8.17.0) + '@sveltejs/vite-plugin-svelte': 7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(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 @@ -3843,24 +3869,24 @@ snapshots: kleur: 4.1.5 magic-string: 0.30.21 mrmime: 2.0.1 - set-cookie-parser: 3.1.1 + set-cookie-parser: 3.1.2 sirv: 3.0.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.63.0) + vite: 8.1.4(@types/node@25.9.5)(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.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))': + '@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(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.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)) + svelte: 5.56.4(@typescript-eslint/types@8.63.0) + vite: 8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0) + vitefu: 1.1.3(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) '@swc/helpers@0.5.15': dependencies: @@ -3931,12 +3957,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.2 '@tailwindcss/oxide-win32-x64-msvc': 4.3.2 - '@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))': + '@tailwindcss/vite@4.3.2(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0))': dependencies: '@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) + vite: 8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0) '@tybys/wasm-util@0.10.3': dependencies: @@ -3945,13 +3971,13 @@ snapshots: '@types/adm-zip@0.5.8': dependencies: - '@types/node': 25.9.4 + '@types/node': 25.9.5 '@types/cookie@0.6.0': {} '@types/cors@2.8.19': dependencies: - '@types/node': 25.9.4 + '@types/node': 25.9.5 '@types/esrecurse@4.3.1': {} @@ -3959,13 +3985,13 @@ snapshots: '@types/json-schema@7.0.15': {} - '@types/node@25.9.4': + '@types/node@25.9.5': dependencies: undici-types: 7.24.6 '@types/qrcode@1.5.6': dependencies: - '@types/node': 25.9.4 + '@types/node': 25.9.5 '@types/react-dom@19.2.3(@types/react@19.2.17)': dependencies: @@ -3982,7 +4008,7 @@ snapshots: '@types/ws@8.18.1': dependencies: - '@types/node': 25.9.4 + '@types/node': 25.9.5 '@typeschema/class-validator@0.3.0(@types/json-schema@7.0.15)(class-validator@0.14.4)': dependencies: @@ -3998,57 +4024,57 @@ snapshots: '@types/json-schema': 7.0.15 optional: true - '@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/eslint-plugin@8.63.0(@typescript-eslint/parser@8.63.0(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.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 + '@typescript-eslint/parser': 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/scope-manager': 8.63.0 + '@typescript-eslint/type-utils': 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/utils': 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.63.0 eslint: 10.6.0(jiti@2.7.0) - ignore: 7.0.5 + ignore: 7.0.6 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@6.0.3) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/parser@8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@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 + '@typescript-eslint/scope-manager': 8.63.0 + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/typescript-estree': 8.63.0(typescript@6.0.3) + '@typescript-eslint/visitor-keys': 8.63.0 debug: 4.4.3 eslint: 10.6.0(jiti@2.7.0) typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.62.1(typescript@6.0.3)': + '@typescript-eslint/project-service@8.63.0(typescript@6.0.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.62.1(typescript@6.0.3) - '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/tsconfig-utils': 8.63.0(typescript@6.0.3) + '@typescript-eslint/types': 8.63.0 debug: 4.4.3 typescript: 6.0.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.62.1': + '@typescript-eslint/scope-manager@8.63.0': dependencies: - '@typescript-eslint/types': 8.62.1 - '@typescript-eslint/visitor-keys': 8.62.1 + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/visitor-keys': 8.63.0 - '@typescript-eslint/tsconfig-utils@8.62.1(typescript@6.0.3)': + '@typescript-eslint/tsconfig-utils@8.63.0(typescript@6.0.3)': dependencies: typescript: 6.0.3 - '@typescript-eslint/type-utils@8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/type-utils@8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: - '@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) + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/typescript-estree': 8.63.0(typescript@6.0.3) + '@typescript-eslint/utils': 8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) debug: 4.4.3 eslint: 10.6.0(jiti@2.7.0) ts-api-utils: 2.5.0(typescript@6.0.3) @@ -4056,14 +4082,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.62.1': {} + '@typescript-eslint/types@8.63.0': {} - '@typescript-eslint/typescript-estree@8.62.1(typescript@6.0.3)': + '@typescript-eslint/typescript-estree@8.63.0(typescript@6.0.3)': dependencies: - '@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 + '@typescript-eslint/project-service': 8.63.0(typescript@6.0.3) + '@typescript-eslint/tsconfig-utils': 8.63.0(typescript@6.0.3) + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/visitor-keys': 8.63.0 debug: 4.4.3 minimatch: 10.2.5 semver: 7.8.5 @@ -4073,20 +4099,20 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)': + '@typescript-eslint/utils@8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3)': dependencies: '@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) + '@typescript-eslint/scope-manager': 8.63.0 + '@typescript-eslint/types': 8.63.0 + '@typescript-eslint/typescript-estree': 8.63.0(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.1': + '@typescript-eslint/visitor-keys@8.63.0': dependencies: - '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/types': 8.63.0 eslint-visitor-keys: 5.0.1 '@valibot/to-json-schema@1.7.1(valibot@1.4.2(typescript@6.0.3))': @@ -4193,15 +4219,15 @@ snapshots: baseline-browser-mapping@2.10.42: {} - 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)): + bits-ui@2.18.1(@internationalized/date@3.12.2)(@sveltejs/kit@2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0)): 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.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)) + runed: 0.35.1(@sveltejs/kit@2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0)) + svelte: 5.56.4(@typescript-eslint/types@8.63.0) + svelte-toolbelt: 0.10.6(@sveltejs/kit@2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0)) tabbable: 6.5.0 transitivePeerDependencies: - '@sveltejs/kit' @@ -4386,7 +4412,7 @@ snapshots: engine.io@6.6.9: dependencies: '@types/cors': 2.8.19 - '@types/node': 25.9.4 + '@types/node': 25.9.5 '@types/ws': 8.18.1 accepts: 1.3.8 base64id: 2.0.0 @@ -4394,7 +4420,7 @@ snapshots: cors: 2.8.6 debug: 4.4.3 engine.io-parser: 5.2.3 - ws: 8.21.0 + ws: 8.21.1 transitivePeerDependencies: - bufferutil - supports-color @@ -4459,7 +4485,7 @@ snapshots: dependencies: eslint: 10.6.0(jiti@2.7.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)): + eslint-plugin-svelte@3.20.0(eslint@10.6.0(jiti@2.7.0))(svelte@5.56.4(@typescript-eslint/types@8.63.0)): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0(jiti@2.7.0)) '@jridgewell/sourcemap-codec': 1.5.5 @@ -4471,9 +4497,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.1)) + svelte-eslint-parser: 1.8.0(svelte@5.56.4(@typescript-eslint/types@8.63.0)) optionalDependencies: - svelte: 5.56.4(@typescript-eslint/types@8.62.1) + svelte: 5.56.4(@typescript-eslint/types@8.63.0) transitivePeerDependencies: - ts-node @@ -4552,11 +4578,11 @@ snapshots: dependencies: estraverse: 5.3.0 - esrap@2.2.13(@typescript-eslint/types@8.62.1): + esrap@2.2.13(@typescript-eslint/types@8.63.0): dependencies: '@jridgewell/sourcemap-codec': 1.5.5 optionalDependencies: - '@typescript-eslint/types': 8.62.1 + '@typescript-eslint/types': 8.63.0 esrecurse@4.3.0: dependencies: @@ -4616,11 +4642,11 @@ snapshots: hasown: 2.0.4 mime-types: 2.1.35 - 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)): + formsnap@2.0.1(svelte@5.56.4(@typescript-eslint/types@8.63.0))(sveltekit-superforms@2.30.2(@sveltejs/kit@2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(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.63.0))(typescript@6.0.3)): dependencies: - 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) + svelte: 5.56.4(@typescript-eslint/types@8.63.0) + svelte-toolbelt: 0.5.0(svelte@5.56.4(@typescript-eslint/types@8.63.0)) + sveltekit-superforms: 2.30.2(@sveltejs/kit@2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(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.63.0))(typescript@6.0.3) fs-extra@10.1.0: dependencies: @@ -4696,6 +4722,8 @@ snapshots: htmlparser2: 8.0.2 selderee: 0.11.0 + html5parser@3.0.0: {} + htmlparser2@8.0.2: dependencies: domelementtype: 2.3.0 @@ -4714,7 +4742,7 @@ snapshots: ignore@5.3.2: {} - ignore@7.0.5: {} + ignore@7.0.6: {} imurmurhash@0.1.4: {} @@ -4791,7 +4819,7 @@ snapshots: known-css-properties@0.37.0: {} - kysely@0.29.2: {} + kysely@0.29.3: {} leac@0.6.0: {} @@ -4907,11 +4935,11 @@ snapshots: minipass@7.1.3: {} - mode-watcher@1.1.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)): + mode-watcher@1.1.0(svelte@5.56.4(@typescript-eslint/types@8.63.0)): dependencies: - 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)) + runed: 0.25.0(svelte@5.56.4(@typescript-eslint/types@8.63.0)) + svelte: 5.56.4(@typescript-eslint/types@8.63.0) + svelte-toolbelt: 0.7.1(svelte@5.56.4(@typescript-eslint/types@8.63.0)) mri@1.2.0: {} @@ -4921,6 +4949,8 @@ snapshots: nanoid@3.3.15: {} + nanoid@3.3.16: {} + natural-compare@1.4.0: {} negotiator@0.6.3: {} @@ -4931,7 +4961,7 @@ snapshots: '@swc/helpers': 0.5.15 baseline-browser-mapping: 2.10.42 caniuse-lite: 1.0.30001800 - postcss: 8.5.16 + postcss: 8.5.19 react: 19.2.7 react-dom: 19.2.7(react@19.2.7) styled-jsx: 5.1.6(react@19.2.7) @@ -5055,20 +5085,26 @@ snapshots: picocolors: 1.1.1 source-map-js: 1.2.1 + postcss@8.5.19: + dependencies: + nanoid: 3.3.16 + picocolors: 1.1.1 + source-map-js: 1.2.1 + prelude-ls@1.2.1: {} - prettier-plugin-svelte@3.5.2(prettier@3.9.4)(svelte@5.56.4(@typescript-eslint/types@8.62.1)): + prettier-plugin-svelte@3.5.2(prettier@3.9.5)(svelte@5.56.4(@typescript-eslint/types@8.63.0)): dependencies: - prettier: 3.9.4 - svelte: 5.56.4(@typescript-eslint/types@8.62.1) + prettier: 3.9.5 + svelte: 5.56.4(@typescript-eslint/types@8.63.0) - 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): + prettier-plugin-tailwindcss@0.8.0(prettier-plugin-svelte@3.5.2(prettier@3.9.5)(svelte@5.56.4(@typescript-eslint/types@8.63.0)))(prettier@3.9.5): dependencies: - prettier: 3.9.4 + prettier: 3.9.5 optionalDependencies: - prettier-plugin-svelte: 3.5.2(prettier@3.9.4)(svelte@5.56.4(@typescript-eslint/types@8.62.1)) + prettier-plugin-svelte: 3.5.2(prettier@3.9.5)(svelte@5.56.4(@typescript-eslint/types@8.63.0)) - prettier@3.9.4: {} + prettier@3.9.5: {} prismjs@1.30.0: {} @@ -5098,11 +5134,11 @@ snapshots: react: 19.2.7 scheduler: 0.27.0 - react-email@6.6.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7): + react-email@6.7.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: '@babel/parser': 7.27.0 '@babel/traverse': 7.27.0 - '@react-email/render': 2.0.10(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@react-email/render': 2.1.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) chokidar: 4.0.3 commander: 13.1.0 conf: 15.1.0 @@ -5122,7 +5158,7 @@ snapshots: react: 19.2.7 react-dom: 19.2.7(react@19.2.7) socket.io: 4.8.3 - tailwindcss: 4.3.2 + tailwindcss: 4.3.3 tsconfig-paths: 4.2.0 transitivePeerDependencies: - bufferutil @@ -5139,59 +5175,59 @@ snapshots: require-main-filename@2.0.0: {} - rolldown@1.1.4: + rolldown@1.1.5: dependencies: - '@oxc-project/types': 0.138.0 + '@oxc-project/types': 0.139.0 '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm64': 1.1.4 - '@rolldown/binding-darwin-arm64': 1.1.4 - '@rolldown/binding-darwin-x64': 1.1.4 - '@rolldown/binding-freebsd-x64': 1.1.4 - '@rolldown/binding-linux-arm-gnueabihf': 1.1.4 - '@rolldown/binding-linux-arm64-gnu': 1.1.4 - '@rolldown/binding-linux-arm64-musl': 1.1.4 - '@rolldown/binding-linux-ppc64-gnu': 1.1.4 - '@rolldown/binding-linux-s390x-gnu': 1.1.4 - '@rolldown/binding-linux-x64-gnu': 1.1.4 - '@rolldown/binding-linux-x64-musl': 1.1.4 - '@rolldown/binding-openharmony-arm64': 1.1.4 - '@rolldown/binding-wasm32-wasi': 1.1.4 - '@rolldown/binding-win32-arm64-msvc': 1.1.4 - '@rolldown/binding-win32-x64-msvc': 1.1.4 + '@rolldown/binding-android-arm64': 1.1.5 + '@rolldown/binding-darwin-arm64': 1.1.5 + '@rolldown/binding-darwin-x64': 1.1.5 + '@rolldown/binding-freebsd-x64': 1.1.5 + '@rolldown/binding-linux-arm-gnueabihf': 1.1.5 + '@rolldown/binding-linux-arm64-gnu': 1.1.5 + '@rolldown/binding-linux-arm64-musl': 1.1.5 + '@rolldown/binding-linux-ppc64-gnu': 1.1.5 + '@rolldown/binding-linux-s390x-gnu': 1.1.5 + '@rolldown/binding-linux-x64-gnu': 1.1.5 + '@rolldown/binding-linux-x64-musl': 1.1.5 + '@rolldown/binding-openharmony-arm64': 1.1.5 + '@rolldown/binding-wasm32-wasi': 1.1.5 + '@rolldown/binding-win32-arm64-msvc': 1.1.5 + '@rolldown/binding-win32-x64-msvc': 1.1.5 - runed@0.23.4(svelte@5.56.4(@typescript-eslint/types@8.62.1)): + runed@0.23.4(svelte@5.56.4(@typescript-eslint/types@8.63.0)): dependencies: esm-env: 1.2.2 - svelte: 5.56.4(@typescript-eslint/types@8.62.1) + svelte: 5.56.4(@typescript-eslint/types@8.63.0) - runed@0.25.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)): + runed@0.25.0(svelte@5.56.4(@typescript-eslint/types@8.63.0)): dependencies: esm-env: 1.2.2 - svelte: 5.56.4(@typescript-eslint/types@8.62.1) + svelte: 5.56.4(@typescript-eslint/types@8.63.0) - runed@0.28.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)): + runed@0.28.0(svelte@5.56.4(@typescript-eslint/types@8.63.0)): dependencies: esm-env: 1.2.2 - svelte: 5.56.4(@typescript-eslint/types@8.62.1) + svelte: 5.56.4(@typescript-eslint/types@8.63.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)): + runed@0.35.1(@sveltejs/kit@2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0)): dependencies: dequal: 2.0.3 esm-env: 1.2.2 lz-string: 1.5.0 - svelte: 5.56.4(@typescript-eslint/types@8.62.1) + svelte: 5.56.4(@typescript-eslint/types@8.63.0) optionalDependencies: - '@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.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) - 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): + runed@0.37.1(@sveltejs/kit@2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(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.1) + svelte: 5.56.4(@typescript-eslint/types@8.63.0) optionalDependencies: - '@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.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)) zod: 4.4.3 sade@1.8.1: @@ -5208,14 +5244,14 @@ snapshots: set-blocking@2.0.0: {} - set-cookie-parser@3.1.1: {} + set-cookie-parser@3.1.2: {} - shadcn-svelte@1.3.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)): + shadcn-svelte@1.4.1(svelte@5.56.4(@typescript-eslint/types@8.63.0)): dependencies: commander: 14.0.3 node-fetch-native: 1.6.7 - postcss: 8.5.16 - svelte: 5.56.4(@typescript-eslint/types@8.62.1) + postcss: 8.5.19 + svelte: 5.56.4(@typescript-eslint/types@8.63.0) tailwind-merge: 3.6.0 sharp@0.34.5: @@ -5267,13 +5303,13 @@ snapshots: socket.io-adapter@2.5.8: dependencies: debug: 4.4.3 - ws: 8.21.0 + ws: 8.21.1 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - socket.io-parser@4.2.6: + socket.io-parser@4.2.7: dependencies: '@socket.io/component-emitter': 3.1.2 debug: 4.4.3 @@ -5288,7 +5324,7 @@ snapshots: debug: 4.4.3 engine.io: 6.6.9 socket.io-adapter: 2.5.8 - socket.io-parser: 4.2.6 + socket.io-parser: 4.2.7 transitivePeerDependencies: - bufferutil - supports-color @@ -5296,10 +5332,10 @@ snapshots: source-map-js@1.2.1: {} - sqlite-wasm-kysely@0.3.0(kysely@0.29.2): + sqlite-wasm-kysely@0.3.0(kysely@0.29.3): dependencies: '@sqlite.org/sqlite-wasm': 3.48.0-build4 - kysely: 0.29.2 + kysely: 0.29.3 string-width@4.2.3: dependencies: @@ -5335,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.1))(typescript@6.0.3): + svelte-check@4.7.2(picomatch@4.0.5)(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3): dependencies: '@jridgewell/trace-mapping': 0.3.31 '@sveltejs/load-config': 0.2.0 @@ -5343,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.1) + svelte: 5.56.4(@typescript-eslint/types@8.63.0) typescript: 6.0.3 transitivePeerDependencies: - picomatch - svelte-eslint-parser@1.8.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)): + svelte-eslint-parser@1.8.0(svelte@5.56.4(@typescript-eslint/types@8.63.0)): dependencies: eslint-scope: 8.4.0 eslint-visitor-keys: 4.2.1 @@ -5358,36 +5394,36 @@ snapshots: postcss-selector-parser: 7.1.4 semver: 7.8.5 optionalDependencies: - svelte: 5.56.4(@typescript-eslint/types@8.62.1) + svelte: 5.56.4(@typescript-eslint/types@8.63.0) - svelte-sonner@1.1.1(svelte@5.56.4(@typescript-eslint/types@8.62.1)): + svelte-sonner@1.1.1(svelte@5.56.4(@typescript-eslint/types@8.63.0)): dependencies: - runed: 0.28.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)) - svelte: 5.56.4(@typescript-eslint/types@8.62.1) + runed: 0.28.0(svelte@5.56.4(@typescript-eslint/types@8.63.0)) + svelte: 5.56.4(@typescript-eslint/types@8.63.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)): + svelte-toolbelt@0.10.6(@sveltejs/kit@2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0)): dependencies: clsx: 2.1.1 - 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)) + runed: 0.35.1(@sveltejs/kit@2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0)) style-to-object: 1.0.14 - svelte: 5.56.4(@typescript-eslint/types@8.62.1) + svelte: 5.56.4(@typescript-eslint/types@8.63.0) transitivePeerDependencies: - '@sveltejs/kit' - svelte-toolbelt@0.5.0(svelte@5.56.4(@typescript-eslint/types@8.62.1)): + svelte-toolbelt@0.5.0(svelte@5.56.4(@typescript-eslint/types@8.63.0)): dependencies: clsx: 2.1.1 style-to-object: 1.0.14 - svelte: 5.56.4(@typescript-eslint/types@8.62.1) + svelte: 5.56.4(@typescript-eslint/types@8.63.0) - svelte-toolbelt@0.7.1(svelte@5.56.4(@typescript-eslint/types@8.62.1)): + svelte-toolbelt@0.7.1(svelte@5.56.4(@typescript-eslint/types@8.63.0)): dependencies: clsx: 2.1.1 - runed: 0.23.4(svelte@5.56.4(@typescript-eslint/types@8.62.1)) + runed: 0.23.4(svelte@5.56.4(@typescript-eslint/types@8.63.0)) style-to-object: 1.0.14 - svelte: 5.56.4(@typescript-eslint/types@8.62.1) + svelte: 5.56.4(@typescript-eslint/types@8.63.0) - svelte@5.56.4(@typescript-eslint/types@8.62.1): + svelte@5.56.4(@typescript-eslint/types@8.63.0): dependencies: '@jridgewell/remapping': 2.3.5 '@jridgewell/sourcemap-codec': 1.5.5 @@ -5400,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.1) + esrap: 2.2.13(@typescript-eslint/types@8.63.0) is-reference: 3.0.3 locate-character: 3.0.0 magic-string: 0.30.21 @@ -5408,12 +5444,12 @@ snapshots: transitivePeerDependencies: - '@typescript-eslint/types' - 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): + sveltekit-superforms@2.30.2(@sveltejs/kit@2.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(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.63.0))(typescript@6.0.3): dependencies: - '@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.69.2(@opentelemetry/api@1.9.1)(@sveltejs/vite-plugin-svelte@7.2.0(svelte@5.56.4(@typescript-eslint/types@8.63.0))(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)))(svelte@5.56.4(@typescript-eslint/types@8.63.0))(typescript@6.0.3)(vite@8.1.4(@types/node@25.9.5)(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.1) + svelte: 5.56.4(@typescript-eslint/types@8.63.0) ts-deepmerge: 8.0.0 optionalDependencies: '@exodus/schemasafe': 1.3.0 @@ -5450,6 +5486,8 @@ snapshots: tailwindcss@4.3.2: {} + tailwindcss@4.3.3: {} + tapable@2.3.3: {} tiny-case@1.0.3: @@ -5506,12 +5544,12 @@ snapshots: typebox@1.3.3: optional: true - typescript-eslint@8.62.1(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3): + typescript-eslint@8.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3): dependencies: - '@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) + '@typescript-eslint/eslint-plugin': 8.63.0(@typescript-eslint/parser@8.63.0(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.63.0(eslint@10.6.0(jiti@2.7.0))(typescript@6.0.3) + '@typescript-eslint/typescript-estree': 8.63.0(typescript@6.0.3) + '@typescript-eslint/utils': 8.63.0(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: @@ -5552,33 +5590,33 @@ snapshots: vary@1.1.2: {} - 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)): + vite-plugin-compression@0.5.1(vite@8.1.4(@types/node@25.9.5)(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.3(@types/node@25.9.4)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0) + vite: 8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0) transitivePeerDependencies: - supports-color - 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): + vite@8.1.4(@types/node@25.9.5)(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 - postcss: 8.5.16 - rolldown: 1.1.4 + postcss: 8.5.19 + rolldown: 1.1.5 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 25.9.4 + '@types/node': 25.9.5 esbuild: 0.28.1 fsevents: 2.3.3 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)): + vitefu@1.1.3(vite@8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0)): optionalDependencies: - 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) + vite: 8.1.4(@types/node@25.9.5)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.0)(yaml@2.9.0) webpack-virtual-modules@0.6.2: {} @@ -5598,7 +5636,7 @@ snapshots: string-width: 4.2.3 strip-ansi: 6.0.1 - ws@8.21.0: {} + ws@8.21.1: {} y18n@4.0.3: {}