Merges Renovate's kit 3 (PR #149) and adapter-static 4 (PR #148) bumps,
plus the migration they need. The mechanical part is `sv migrate
sveltekit-3`, run one task at a time and reviewed:
- svelte.config.js is gone. Its options move into sveltekit() in
vite.config.ts, exported as kitOptions so vitest.config.ts runs the
same kit setup, including the $test-utils alias the tests import.
- $lib becomes #lib through package.json "imports". There is no
src/lib/index, so only the "#lib/*" entry is kept.
- tsconfig extends $app/tsconfig.
- Peer floors raised to kit 3's requirements: svelte ^5.57.1, vite
^8.0.12, svelte-check ^4.7.5.
By hand, from the codemod's list of non-automated tasks:
- goto's replaceState option is now replace; keepFocus becomes
reset: false. For the search typeahead, reset: false also stops the
scroll-to-top, which is wanted while typing.
- The test setup mocks drop pushState/replaceState and $app/paths
base/assets, which kit 3 removed, and mock refreshAll in place of
invalidateAll.
- The other flagged files only read page.url or goto internal routes,
so they needed no change.
TypeScript goes to ^6, not the ^7 Renovate offers: kit 3 declares
typescript ^6 as a peer and svelte-check 4.7 accepts ^5 || ^6. Move to
7 once both accept it.
With Tailwind 4 and kit 3 in, `npm audit` on the whole tree reports 0,
so the web lane now audits every dependency rather than only what
ships to browsers.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Renovate's tailwindcss bump (PR #150) plus the migration it needs:
- Theme moves from tailwind.config.js into app.css as `@theme inline`,
mapping the same FabledSword tokens. tailwind.config.js is gone.
- PostCSS runs @tailwindcss/postcss; autoprefixer is dropped, since
Tailwind 4 prefixes through Lightning CSS.
- Class renames from @tailwindcss/upgrade 4.3.3, reviewed: outline-none
-> outline-hidden, focus-visible:outline -> outline-solid, shadow ->
shadow-sm, shadow-sm -> shadow-xs, flex-shrink-0 -> shrink-0. Bare
`rounded` stays: v4 keeps it at 0.25rem, as before.
- Three v3 preflight defaults kept in a base layer so nothing changes on
screen: gray-200 default border colour, gray-400 placeholder text and
the pointer cursor on buttons.
- The unused class-based dark variant is not carried over; no template
uses `dark:`.
Clears the five high and two moderate npm audit findings that came in
through Tailwind 3 (braces, chokidar, micromatch, fast-glob,
postcss-selector-parser).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The raw accent fails AA as text on every dark surface, not only on its
own tint: 3.04:1 on the page, 2.70 on iron, 2.21 on slate, against 4.5.
accent-fg (the house formula, 45% toward parchment) measures 5.62 at
worst across both modes. The operator chose the readable colour over the
signature teal for text, on 2026-10-08.
- 36 sites swap. They are 35 Tailwind uses: links, "Now playing", the
ingest progress line, active shuffle/repeat, the liked heart, the app
download icon and its hover. The last is the alphabet rail's pending
spinner in CSS. Icons follow the text: as graphics they need only
3:1, and the raw accent misses even that on iron.
- check-tint-contrast adds accent to TEXT_NEVER_RAW, so a new raw
text-accent or color: var(--fs-accent) fails the web lane. Run against
the files before the swap, it finds all 36. Borders, rings and fills
keep the raw accent.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every password-shaped check was mounted bare, so guessing was limited only
by bcrypt cost. A shared in-memory AttemptLimiter now sits in front of them:
- login: 10 failures per account and 50 per address per 15 min, checked
before the user lookup and bcrypt; 429 with Retry-After. A success clears
the account's count but not the address's.
- unknown usernames run a dummy bcrypt compare, so timing no longer says
which accounts exist.
- register: 10 per address per hour; forgot-password: 5 per address and 3
per email per hour (applied whether or not the email matches); reset: 20
failed tokens per address per 15 min.
- Subsonic /rest: same limits as login, counting only wrong credentials,
since clients authenticate on every request.
Web login, register, reset and forgot-password screens say how long to
wait; web and Android carry copy for the rate_limited code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Five fixes in one commit, all from the U1+U2+U3 push:
1. internal/api/library_test.go's Mount call wasn't updated when
U3-T4 added the mailer.Sender param. 17 args, sig wants 18.
Adds a trailing nil for the mailer. Same shape of test-fixture
lag the project has hit before — flagged as a recurring pattern
for the DRY-pass slice.
2. web/src/routes/settings/settings.test.ts mocked $lib/api/me with
`getAPIToken: vi.fn()` (no return value). The new /settings API
Token card's $effect calls `getAPIToken().then(...)` which threw
"Cannot read properties of undefined (reading 'then')" on every
ListenBrainz test. Default the mock to mockResolvedValue so any
test that doesn't override gets a valid resolution.
3. web/src/routes/admin/users/users.test.ts queried row buttons by
/^Delete$/ but T3 added per-user aria-labels ("Delete alice"),
so the row buttons' accessible name is no longer "Delete". The
modal's confirm button has no aria-label so its name IS "Delete"
exactly. Tests now click by per-user aria-label first, then by
/^Delete$/ for the modal confirm.
4. Same file: /Set password/i regex matched both the dialog's
submit button AND the row's "Reset password for ..." buttons
(because regex `Set` matches "ReSet" case-insensitively). Switched
to /^Set password$/ exact-match.
5. web/src/routes/reset-password/[token]/reset-password.test.ts had
/New password/i which matched both the "New password" and
"Confirm new password" labels. Switched to exact-match string
selectors.
6. web/src/routes/forgot-password/+page.svelte's onSubmit handler
had no catch — a rejected forgotPassword() bubbled as an
unhandled rejection in vitest. Added a silent catch since the
page intentionally shows the same success message regardless of
outcome (no-enumeration posture).
Three CI fixes from the U1+U2+U3 push:
- internal/api/admin_users.go imported the standalone
github.com/jackc/pgconn — that module isn't in go.sum and
go vet caught it. Switch to github.com/jackc/pgx/v5/pgconn
(the path used elsewhere in the package, e.g. auth_register.go
and me_profile.go).
- /reset-password/[token] dereferenced page.params.token without
the undefined narrowing svelte-kit's typegen requires. Coalesce
to '' on read and reject empty token at submit time with a
clear error.
- /admin/integrations SMTP card had a label without an associated
control as the TLS row's leading column header. Switched to a
span — the actual checkbox lives in the wrapping label below it.
Completes the U3 frontend that T5a's crashed dispatch left half-done.
- /reset-password/[token] — public; new password + confirm. Calls
POST /api/auth/reset-password with the URL's token. invalid_token,
password_too_short, and mismatched-passwords surface as inline
errors. Success redirects to /login?reset=ok.
- /admin/integrations gains an SMTP card (alongside Lidarr) with
enabled toggle + all fields + Save and Send-test-email buttons.
Password input is a separate state from the loaded form, so empty
submission preserves the stored server-side value (matches the
backend's empty-password-preserves contract). Test button error
codes (no_email_on_file / not_configured / send_failed) surface
as actionable toasts.
Tests cover both new pages plus extensions to settings + integrations
test files for the cards landed in T5a.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>