chore(deps): SvelteKit 3 with adapter-static 4 and TypeScript 6, full-tree npm audit (#5021)

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>
This commit is contained in:
2026-10-08 10:50:39 -04:00
co-authored by Claude Opus 5.5
186 changed files with 916 additions and 924 deletions
+10 -10
View File
@@ -1,14 +1,14 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { createAlbumsAlphaInfiniteQuery } from '$lib/api/albums';
import AlbumCard from '$lib/components/AlbumCard.svelte';
import AlphabeticalGrid from '$lib/components/AlphabeticalGrid.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import InfiniteScrollSentinel from '$lib/components/InfiniteScrollSentinel.svelte';
import QuickFilter from '$lib/components/QuickFilter.svelte';
import EmptyState from '$lib/components/EmptyState.svelte';
import { useDelayed } from '$lib/utils/useDelayed.svelte';
import type { AlbumRef } from '$lib/api/types';
import { pageTitle } from '#lib/branding.js';
import { createAlbumsAlphaInfiniteQuery } from '#lib/api/albums.js';
import AlbumCard from '#lib/components/AlbumCard.svelte';
import AlphabeticalGrid from '#lib/components/AlphabeticalGrid.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import InfiniteScrollSentinel from '#lib/components/InfiniteScrollSentinel.svelte';
import QuickFilter from '#lib/components/QuickFilter.svelte';
import EmptyState from '#lib/components/EmptyState.svelte';
import { useDelayed } from '#lib/utils/useDelayed.svelte.js';
import type { AlbumRef } from '#lib/api/types.js';
const queryStore = createAlbumsAlphaInfiniteQuery();
const query = $derived($queryStore);
+4 -4
View File
@@ -4,7 +4,7 @@ import { readable } from 'svelte/store';
import { emptyLikesMock } from '../../../test-utils/mocks/likes';
import { apiClientMock } from '../../../test-utils/mocks/client';
vi.mock('$lib/api/albums', () => ({
vi.mock('#lib/api/albums.js', () => ({
createAlbumsAlphaInfiniteQuery: () => readable({
data: { pages: [{
items: [
@@ -26,12 +26,12 @@ vi.mock('$lib/api/albums', () => ({
}),
ALBUM_PAGE_SIZE: 50
}));
vi.mock('$lib/api/client', () => apiClientMock());
vi.mock('$lib/player/store.svelte', () => ({
vi.mock('#lib/api/client.js', () => apiClientMock());
vi.mock('#lib/player/store.svelte.js', () => ({
enqueueTracks: vi.fn(),
playQueue: vi.fn()
}));
vi.mock('$lib/api/likes', () => emptyLikesMock());
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
// AlbumCard renders LikeButton, which calls useQueryClient() to get the
// cache for invalidation. The page test isn't wrapped in a QueryClientProvider,
// so we stub useQueryClient with a noop client.
+10 -10
View File
@@ -1,14 +1,14 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { createArtistsQuery } from '$lib/api/queries';
import ArtistCard from '$lib/components/ArtistCard.svelte';
import AlphabeticalGrid from '$lib/components/AlphabeticalGrid.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import InfiniteScrollSentinel from '$lib/components/InfiniteScrollSentinel.svelte';
import QuickFilter from '$lib/components/QuickFilter.svelte';
import EmptyState from '$lib/components/EmptyState.svelte';
import { useDelayed } from '$lib/utils/useDelayed.svelte';
import type { ArtistRef } from '$lib/api/types';
import { pageTitle } from '#lib/branding.js';
import { createArtistsQuery } from '#lib/api/queries.js';
import ArtistCard from '#lib/components/ArtistCard.svelte';
import AlphabeticalGrid from '#lib/components/AlphabeticalGrid.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import InfiniteScrollSentinel from '#lib/components/InfiniteScrollSentinel.svelte';
import QuickFilter from '#lib/components/QuickFilter.svelte';
import EmptyState from '#lib/components/EmptyState.svelte';
import { useDelayed } from '#lib/utils/useDelayed.svelte.js';
import type { ArtistRef } from '#lib/api/types.js';
const queryStore = $derived(createArtistsQuery('alpha'));
const query = $derived($queryStore);
+5 -5
View File
@@ -4,7 +4,7 @@ import { readable } from 'svelte/store';
import { emptyLikesMock } from '../../../test-utils/mocks/likes';
import { apiClientMock } from '../../../test-utils/mocks/client';
vi.mock('$lib/api/queries', () => ({
vi.mock('#lib/api/queries.js', () => ({
createArtistsQuery: () => readable({
data: { pages: [{
items: [
@@ -21,15 +21,15 @@ vi.mock('$lib/api/queries', () => ({
fetchNextPage: () => {}
})
}));
vi.mock('$lib/api/client', () => apiClientMock());
vi.mock('$lib/player/store.svelte', () => ({
vi.mock('#lib/api/client.js', () => apiClientMock());
vi.mock('#lib/player/store.svelte.js', () => ({
playQueue: vi.fn(),
enqueueTracks: vi.fn()
}));
vi.mock('$lib/api/artists', () => ({
vi.mock('#lib/api/artists.js', () => ({
listArtistTracks: vi.fn().mockResolvedValue([])
}));
vi.mock('$lib/api/likes', () => emptyLikesMock());
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
import Page from './+page.svelte';
describe('/library/artists', () => {
+7 -7
View File
@@ -1,18 +1,18 @@
<script lang="ts">
import { page } from '$app/state';
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { ChevronLeft } from 'lucide-svelte';
import {
createGenresQuery,
listAlbumsByGenre,
BROWSE_PAGE_SIZE,
type GenreCount
} from '$lib/api/browse';
import AlbumCard from '$lib/components/AlbumCard.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import EmptyState from '$lib/components/EmptyState.svelte';
import QuickFilter from '$lib/components/QuickFilter.svelte';
import type { AlbumRef } from '$lib/api/types';
} from '#lib/api/browse.js';
import AlbumCard from '#lib/components/AlbumCard.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import EmptyState from '#lib/components/EmptyState.svelte';
import QuickFilter from '#lib/components/QuickFilter.svelte';
import type { AlbumRef } from '#lib/api/types.js';
const indexStore = createGenresQuery();
const index = $derived($indexStore);
+6 -6
View File
@@ -4,7 +4,7 @@ import { mockQuery } from '$test-utils/query';
import { pageUrlModule } from '$test-utils/mocks/appState';
import { apiClientMock } from '$test-utils/mocks/client';
import { emptyLikesMock } from '$test-utils/mocks/likes';
import type { AlbumRef } from '$lib/api/types';
import type { AlbumRef } from '#lib/api/types.js';
const pageState = vi.hoisted(() => ({
pageUrl: new URL('http://localhost/library/genres')
@@ -12,15 +12,15 @@ const pageState = vi.hoisted(() => ({
vi.mock('$app/state', () => pageUrlModule(pageState));
vi.mock('$lib/api/browse', () => ({
vi.mock('#lib/api/browse.js', () => ({
BROWSE_PAGE_SIZE: 2,
createGenresQuery: vi.fn(),
listAlbumsByGenre: vi.fn()
}));
vi.mock('$lib/api/client', () => apiClientMock());
vi.mock('$lib/api/likes', () => emptyLikesMock());
vi.mock('$lib/player/store.svelte', () => ({
vi.mock('#lib/api/client.js', () => apiClientMock());
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
vi.mock('#lib/player/store.svelte.js', () => ({
playQueue: vi.fn(),
playRadio: vi.fn(),
enqueueTrack: vi.fn(),
@@ -29,7 +29,7 @@ vi.mock('$lib/player/store.svelte', () => ({
}));
import GenresPage from './+page.svelte';
import { createGenresQuery, listAlbumsByGenre } from '$lib/api/browse';
import { createGenresQuery, listAlbumsByGenre } from '#lib/api/browse.js';
const asMock = (fn: unknown) => fn as ReturnType<typeof vi.fn>;
+7 -7
View File
@@ -1,13 +1,13 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { Music2, RotateCcw } from 'lucide-svelte';
import { useQueryClient } from '@tanstack/svelte-query';
import { createMyQuarantineQuery, unflagTrack } from '$lib/api/quarantine';
import { qk } from '$lib/api/queries';
import type { LidarrQuarantineMineRow, LidarrQuarantineReason } from '$lib/api/types';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import { coverUrl } from '$lib/media/covers';
import { relativeTime } from '$lib/utils/relativeTime';
import { createMyQuarantineQuery, unflagTrack } from '#lib/api/quarantine.js';
import { qk } from '#lib/api/queries.js';
import type { LidarrQuarantineMineRow, LidarrQuarantineReason } from '#lib/api/types.js';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import { coverUrl } from '#lib/media/covers.js';
import { relativeTime } from '#lib/utils/relativeTime.js';
const client = useQueryClient();
const queryStore = createMyQuarantineQuery();
+3 -3
View File
@@ -2,14 +2,14 @@ import { afterEach, describe, expect, test, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/svelte';
import { mockQuery } from '../../../test-utils/query';
vi.mock('$lib/api/quarantine', () => ({
vi.mock('#lib/api/quarantine.js', () => ({
createMyQuarantineQuery: vi.fn(),
unflagTrack: vi.fn().mockResolvedValue(undefined)
}));
import HiddenPage from './+page.svelte';
import { createMyQuarantineQuery, unflagTrack } from '$lib/api/quarantine';
import type { LidarrQuarantineMineRow } from '$lib/api/types';
import { createMyQuarantineQuery, unflagTrack } from '#lib/api/quarantine.js';
import type { LidarrQuarantineMineRow } from '#lib/api/types.js';
const baseRow: LidarrQuarantineMineRow = {
track_id: 't1',
+9 -9
View File
@@ -1,13 +1,13 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { createHistoryQuery } from '$lib/api/history';
import { groupByDay } from '$lib/utils/dayGroup';
import HistoryRow from '$lib/components/HistoryRow.svelte';
import InfiniteScrollSentinel from '$lib/components/InfiniteScrollSentinel.svelte';
import LibrarySkeleton from '$lib/components/LibrarySkeleton.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import QuickFilter from '$lib/components/QuickFilter.svelte';
import EmptyState from '$lib/components/EmptyState.svelte';
import { pageTitle } from '#lib/branding.js';
import { createHistoryQuery } from '#lib/api/history.js';
import { groupByDay } from '#lib/utils/dayGroup.js';
import HistoryRow from '#lib/components/HistoryRow.svelte';
import InfiniteScrollSentinel from '#lib/components/InfiniteScrollSentinel.svelte';
import LibrarySkeleton from '#lib/components/LibrarySkeleton.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import QuickFilter from '#lib/components/QuickFilter.svelte';
import EmptyState from '#lib/components/EmptyState.svelte';
const queryStore = createHistoryQuery();
const query = $derived($queryStore);
+6 -6
View File
@@ -1,7 +1,7 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen } from '@testing-library/svelte';
import HistoryPage from './+page.svelte';
import type { HistoryEvent, HistoryResponse } from '$lib/api/history';
import type { HistoryEvent, HistoryResponse } from '#lib/api/history.js';
import { makeTrack } from '$test-utils/fixtures/track';
const mkTrack = (id: string) => makeTrack({ id: `track-${id}`, title: `Song ${id}` });
@@ -20,9 +20,9 @@ let mockIsFetchingNextPage = false;
const mockFetchNextPage = vi.fn();
const mockRefetch = vi.fn();
vi.mock('$lib/api/history', async () => {
const actual = await vi.importActual<typeof import('$lib/api/history')>(
'$lib/api/history'
vi.mock('#lib/api/history.js', async () => {
const actual = await vi.importActual<typeof import('#lib/api/history.js')>(
'#lib/api/history.js'
);
return {
...actual,
@@ -44,11 +44,11 @@ vi.mock('$lib/api/history', async () => {
};
});
vi.mock('$lib/player/store.svelte', () => ({
vi.mock('#lib/player/store.svelte.js', () => ({
playQueue: vi.fn()
}));
vi.mock('$lib/branding', () => ({
vi.mock('#lib/branding.js', () => ({
pageTitle: (s: string) => `Minstrel · ${s}`
}));
+7 -7
View File
@@ -1,15 +1,15 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import {
createLikedTracksInfiniteQuery,
createLikedAlbumsInfiniteQuery,
createLikedArtistsInfiniteQuery
} from '$lib/api/likes';
import ArtistCard from '$lib/components/ArtistCard.svelte';
import AlbumCard from '$lib/components/AlbumCard.svelte';
import TrackRow from '$lib/components/TrackRow.svelte';
import QuickFilter from '$lib/components/QuickFilter.svelte';
import EmptyState from '$lib/components/EmptyState.svelte';
} from '#lib/api/likes.js';
import ArtistCard from '#lib/components/ArtistCard.svelte';
import AlbumCard from '#lib/components/AlbumCard.svelte';
import TrackRow from '#lib/components/TrackRow.svelte';
import QuickFilter from '#lib/components/QuickFilter.svelte';
import EmptyState from '#lib/components/EmptyState.svelte';
const artistsStore = $derived(createLikedArtistsInfiniteQuery());
const albumsStore = $derived(createLikedAlbumsInfiniteQuery());
+5 -5
View File
@@ -2,11 +2,11 @@ import { afterEach, describe, expect, test, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/svelte';
import { mockInfiniteQuery } from '../../../test-utils/query';
import { apiClientMock } from '../../../test-utils/mocks/client';
import type { ArtistRef, AlbumRef, TrackRef, Page } from '$lib/api/types';
import type { ArtistRef, AlbumRef, TrackRef, Page } from '#lib/api/types.js';
import { readable } from 'svelte/store';
import { makeTrack } from '$test-utils/fixtures/track';
vi.mock('$lib/api/likes', () => ({
vi.mock('#lib/api/likes.js', () => ({
createLikedIdsQuery: () => readable({
data: { track_ids: [], album_ids: [], artist_ids: [] },
isPending: false,
@@ -19,8 +19,8 @@ vi.mock('$lib/api/likes', () => ({
unlikeEntity: vi.fn()
}));
vi.mock('$lib/api/client', () => apiClientMock());
vi.mock('$lib/player/store.svelte', () => ({
vi.mock('#lib/api/client.js', () => apiClientMock());
vi.mock('#lib/player/store.svelte.js', () => ({
playQueue: vi.fn(), enqueueTrack: vi.fn(), enqueueTracks: vi.fn(),
player: { current: undefined }
}));
@@ -30,7 +30,7 @@ import {
createLikedTracksInfiniteQuery,
createLikedAlbumsInfiniteQuery,
createLikedArtistsInfiniteQuery
} from '$lib/api/likes';
} from '#lib/api/likes.js';
function page<T>(items: T[], total: number, offset = 0, limit = 50): Page<T> {
return { items, total, offset, limit };
@@ -1,14 +1,14 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { goto } from '$app/navigation';
import { Plus } from 'lucide-svelte';
import PlaylistCard from '$lib/components/PlaylistCard.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import QuickFilter from '$lib/components/QuickFilter.svelte';
import EmptyState from '$lib/components/EmptyState.svelte';
import { createPlaylistsQuery, createPlaylist } from '$lib/api/playlists';
import PlaylistCard from '#lib/components/PlaylistCard.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import QuickFilter from '#lib/components/QuickFilter.svelte';
import EmptyState from '#lib/components/EmptyState.svelte';
import { createPlaylistsQuery, createPlaylist } from '#lib/api/playlists.js';
import { useQueryClient } from '@tanstack/svelte-query';
import { qk } from '$lib/api/queries';
import { qk } from '#lib/api/queries.js';
const playlistsStore = $derived(createPlaylistsQuery());
const playlistsQuery = $derived($playlistsStore);
@@ -1,9 +1,9 @@
import { describe, expect, test, vi } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
import { mockQuery } from '../../../test-utils/query';
import type { Playlist } from '$lib/api/types';
import type { Playlist } from '#lib/api/types.js';
vi.mock('$lib/api/playlists', () => ({
vi.mock('#lib/api/playlists.js', () => ({
createPlaylistsQuery: vi.fn(),
createPlaylist: vi.fn().mockResolvedValue({ id: 'p-new', name: 'X' })
}));
@@ -11,7 +11,7 @@ vi.mock('$lib/api/playlists', () => ({
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
import PlaylistsPage from './+page.svelte';
import { createPlaylistsQuery, createPlaylist } from '$lib/api/playlists';
import { createPlaylistsQuery, createPlaylist } from '#lib/api/playlists.js';
const mockedCreatePlaylistsQuery = createPlaylistsQuery as ReturnType<typeof vi.fn>;
const mockedCreatePlaylist = createPlaylist as ReturnType<typeof vi.fn>;
+6 -6
View File
@@ -1,17 +1,17 @@
<script lang="ts">
import { page } from '$app/state';
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { ChevronLeft } from 'lucide-svelte';
import {
createAlbumYearsQuery,
listAlbumsByYear,
BROWSE_PAGE_SIZE,
type YearCount
} from '$lib/api/browse';
import AlbumCard from '$lib/components/AlbumCard.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import EmptyState from '$lib/components/EmptyState.svelte';
import type { AlbumRef } from '$lib/api/types';
} from '#lib/api/browse.js';
import AlbumCard from '#lib/components/AlbumCard.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import EmptyState from '#lib/components/EmptyState.svelte';
import type { AlbumRef } from '#lib/api/types.js';
const indexStore = createAlbumYearsQuery();
const index = $derived($indexStore);
+6 -6
View File
@@ -4,7 +4,7 @@ import { mockQuery } from '$test-utils/query';
import { pageUrlModule } from '$test-utils/mocks/appState';
import { apiClientMock } from '$test-utils/mocks/client';
import { emptyLikesMock } from '$test-utils/mocks/likes';
import type { AlbumRef } from '$lib/api/types';
import type { AlbumRef } from '#lib/api/types.js';
const pageState = vi.hoisted(() => ({
pageUrl: new URL('http://localhost/library/years')
@@ -12,15 +12,15 @@ const pageState = vi.hoisted(() => ({
vi.mock('$app/state', () => pageUrlModule(pageState));
vi.mock('$lib/api/browse', () => ({
vi.mock('#lib/api/browse.js', () => ({
BROWSE_PAGE_SIZE: 2,
createAlbumYearsQuery: vi.fn(),
listAlbumsByYear: vi.fn()
}));
vi.mock('$lib/api/client', () => apiClientMock());
vi.mock('$lib/api/likes', () => emptyLikesMock());
vi.mock('$lib/player/store.svelte', () => ({
vi.mock('#lib/api/client.js', () => apiClientMock());
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
vi.mock('#lib/player/store.svelte.js', () => ({
playQueue: vi.fn(),
playRadio: vi.fn(),
enqueueTrack: vi.fn(),
@@ -29,7 +29,7 @@ vi.mock('$lib/player/store.svelte', () => ({
}));
import YearsPage from './+page.svelte';
import { createAlbumYearsQuery, listAlbumsByYear } from '$lib/api/browse';
import { createAlbumYearsQuery, listAlbumsByYear } from '#lib/api/browse.js';
const asMock = (fn: unknown) => fn as ReturnType<typeof vi.fn>;