refactor(api): collapse src/api into src/lib/api

The seven remaining src/api/ modules (analysis, azuracast, bandsintown,
coverCache, migration, network, runtimeLogs) were the last IPC-layer files
outside src/lib/api/, where the rest of the FE IPC clients already live. Move
them (+ the colocated coverCache test) into lib/api and repoint every consumer;
relative-up imports in the moved files become the depth-independent @/ alias.

Pure move + import updates — no behavior change. The layering baseline is
regenerated in the same commit: the moved files' pre-existing store/cover edges
are only now caught under lib/, and the cover<->coverCache cycles are the same
edges re-keyed to the new path (E4 shrinks the allowlist later).
This commit is contained in:
Psychotoxical
2026-07-01 14:17:56 +02:00
parent 751ad35365
commit f4fa766b20
53 changed files with 5301 additions and 3016 deletions
@@ -8,7 +8,7 @@ import { open as shellOpen } from '@tauri-apps/plugin-shell';
import { usePlayerStore } from '@/features/playback/store/playerStore';
import { useAuthStore } from '@/store/authStore';
import { usePlaybackServerId } from '@/features/playback/hooks/usePlaybackServerId';
import { fetchBandsintownEvents, type BandsintownEvent } from '@/api/bandsintown';
import { fetchBandsintownEvents, type BandsintownEvent } from '@/lib/api/bandsintown';
import CachedImage from '@/ui/CachedImage';
import OverlayScrollArea from '@/ui/OverlayScrollArea';
import { primaryTrackArtistRef } from '@/features/playback/utils/playback/trackArtistRefs';
@@ -2,7 +2,7 @@ import React, { memo, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Calendar, Info } from 'lucide-react';
import { open as shellOpen } from '@tauri-apps/plugin-shell';
import type { BandsintownEvent } from '@/api/bandsintown';
import type { BandsintownEvent } from '@/lib/api/bandsintown';
import { isoToParts } from '@/features/nowPlaying/utils/nowPlayingHelpers';
interface TourCardProps {
@@ -15,7 +15,7 @@ import type { SubsonicArtistInfo, SubsonicSong, SubsonicAlbum, SubsonicArtist }
vi.mock('@/lib/api/subsonicArtists');
vi.mock('@/lib/api/subsonicLibrary');
vi.mock('@/api/bandsintown');
vi.mock('@/lib/api/bandsintown');
vi.mock('@/lib/network/subsonicNetworkGuard', () => ({
shouldAttemptSubsonicForServer: vi.fn(() => true),
}));
@@ -23,7 +23,7 @@ vi.mock('@/lib/network/subsonicNetworkGuard', () => ({
import { shouldAttemptSubsonicForServer } from '@/lib/network/subsonicNetworkGuard';
import { getArtistForServer, getArtistInfoForServer, getTopSongsForServer } from '@/lib/api/subsonicArtists';
import { getAlbumForServer, getSongForServer } from '@/lib/api/subsonicLibrary';
import { fetchBandsintownEvents } from '@/api/bandsintown';
import { fetchBandsintownEvents } from '@/lib/api/bandsintown';
import { useNowPlayingFetchers, type NowPlayingFetchersDeps } from '@/features/nowPlaying/hooks/useNowPlayingFetchers';
// Resolved return shapes of the mocked API calls — used to cast deliberately
@@ -2,7 +2,7 @@ import { useEffect, useState } from 'react';
import { getArtistInfoForServer } from '@/lib/api/subsonicArtists';
import type { SubsonicAlbum, SubsonicArtistInfo, SubsonicSong } from '@/lib/api/subsonicTypes';
import { resolveNpAlbum, resolveNpDiscography, resolveNpSongMeta, resolveNpTopSongs } from '@/features/nowPlaying/utils/nowPlayingMetadataResolve';
import { fetchBandsintownEvents, type BandsintownEvent } from '@/api/bandsintown';
import { fetchBandsintownEvents, type BandsintownEvent } from '@/lib/api/bandsintown';
import type { ArtistStats, TrackStats } from '@/music-network';
import { getMusicNetworkRuntimeOrNull } from '@/music-network';
import { makeCache } from '@/lib/cache/nowPlayingCache';
@@ -1,6 +1,6 @@
import { renderHook, waitFor } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { coverCacheEnsure, coverCachePeekBatch } from '@/api/coverCache';
import { coverCacheEnsure, coverCachePeekBatch } from '@/lib/api/coverCache';
import { coverIndexKeyFromRef } from '@/cover/storageKeys';
import { useNowPlayingPrewarm } from '@/features/nowPlaying/hooks/useNowPlayingPrewarm';
import { prewarmNowPlayingFetchers } from '@/features/nowPlaying/hooks/useNowPlayingFetchers';
@@ -10,8 +10,8 @@ import { makeTrack } from '@/test/helpers/factories';
import { resetAllStores } from '@/test/helpers/storeReset';
import { toQueueItemRefs } from '@/features/playback/store/queueItemRef';
vi.mock('@/api/coverCache', async importOriginal => {
const actual = await importOriginal<typeof import('@/api/coverCache')>();
vi.mock('@/lib/api/coverCache', async importOriginal => {
const actual = await importOriginal<typeof import('@/lib/api/coverCache')>();
return {
...actual,
coverCachePeekBatch: vi.fn(async () => ({})),
@@ -1,5 +1,5 @@
import { useEffect } from 'react';
import { coverCacheEnsure, coverCachePeekBatch } from '@/api/coverCache';
import { coverCacheEnsure, coverCachePeekBatch } from '@/lib/api/coverCache';
import { albumCoverRef } from '@/cover/ref';
import { resolvePlaybackCoverScope } from '@/cover/ref';
import { resolveTrackCoverRefFromLibrary } from '@/cover/resolveEntryLibrary';
+1 -1
View File
@@ -16,7 +16,7 @@ import {
isOfflinePinComplete,
pendingOfflinePinSongs,
} from '@/features/offline/utils/offlineLibraryHelpers';
import { librarySqlServerId } from '@/api/coverCache';
import { librarySqlServerId } from '@/lib/api/coverCache';
import { resolveIndexKey, serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
import { isSmartPlaylistName } from '@/lib/format/playlistDetailHelpers';
import {
@@ -1,5 +1,5 @@
import { libraryUpsertSongsFromApi } from '@/lib/api/library';
import { librarySqlServerId } from '@/api/coverCache';
import { librarySqlServerId } from '@/lib/api/coverCache';
import { getAlbumForServer } from '@/lib/api/subsonicLibrary';
import { getArtistForServer } from '@/lib/api/subsonicArtists';
import { getStarredForServer } from '@/lib/api/subsonicStarRating';
@@ -1,5 +1,5 @@
import { libraryUpsertSongsFromApi } from '@/lib/api/library';
import { librarySqlServerId } from '@/api/coverCache';
import { librarySqlServerId } from '@/lib/api/coverCache';
import type { SubsonicSong } from '@/lib/api/subsonicTypes';
import { invoke } from '@tauri-apps/api/core';
import { useAuthStore } from '@/store/authStore';
@@ -22,7 +22,7 @@ vi.mock('@/lib/api/subsonicStreamUrl', () => ({
buildStreamUrlForServer: (_sid: string, id: string) => `https://mock/stream/${id}`,
}));
vi.mock('@/api/coverCache', () => ({
vi.mock('@/lib/api/coverCache', () => ({
librarySqlServerId: (k: string) => k,
}));
@@ -3,7 +3,7 @@ import type { Track } from '@/lib/media/trackTypes';
import { invoke } from '@tauri-apps/api/core';
import { useHotCacheStore } from '@/features/playback/store/hotCacheStore';
import { getMediaDir } from '@/lib/media/mediaDir';
import { librarySqlServerId } from '@/api/coverCache';
import { librarySqlServerId } from '@/lib/api/coverCache';
import { hasLocalPersistentPlaybackBytes } from '@/store/localPlaybackResolve';
/**
+1 -1
View File
@@ -8,7 +8,7 @@ import {
fetchAzuraCastNowPlaying,
type AzuraCastNowPlaying,
type AzuraCastSong,
} from '@/api/azuracast';
} from '@/lib/api/azuracast';
// ─── Public types ─────────────────────────────────────────────────────────────
@@ -6,7 +6,7 @@ import { AddServerForm } from '@/features/settings/components/AddServerForm';
import { encodeServerMagicString } from '@/lib/server/serverMagicString';
// resolve_host_addresses Tauri command — hint-only, must not block save.
vi.mock('@/api/network', () => ({
vi.mock('@/lib/api/network', () => ({
resolveHostAddresses: vi.fn(async () => [] as string[]),
}));
@@ -16,7 +16,7 @@ import {
} from '@/lib/server/serverMagicString';
import { shortHostFromServerUrl } from '@/lib/server/serverDisplayName';
import { isLanUrl } from '@/lib/server/serverEndpoint';
import { resolveHostAddresses } from '@/api/network';
import { resolveHostAddresses } from '@/lib/api/network';
type FormState = {
name: string;
@@ -18,7 +18,7 @@ import {
type AnalysisFailedTrackDto,
libraryAnalysisProgress,
type LibraryAnalysisProgressDto,
} from '@/api/analysis';
} from '@/lib/api/analysis';
import { libraryGetTracksBatch, type LibraryTrackDto, type TrackRefDto } from '@/lib/api/library';
import { buildStreamUrlForServer } from '@/lib/api/subsonicStreamUrl';
import { serverListDisplayLabel } from '@/lib/server/serverDisplayName';
@@ -12,7 +12,7 @@ import {
coverCacheStatsServer,
libraryCoverCatalogSize,
libraryCoverProgress,
} from '@/api/coverCache';
} from '@/lib/api/coverCache';
import { clearDiskSrcCacheForServer } from '@/cover/diskSrcCache';
import { serverListDisplayLabel } from '@/lib/server/serverDisplayName';
import { serverIndexKeyForProfile } from '@/lib/server/serverIndexKey';
@@ -10,7 +10,7 @@ import { BackdropSourceList } from '@/features/settings/components/BackdropSourc
import type { BackdropSurface } from '@/store/themeStore';
import type { BackdropSource } from '@/cover/artistBackdrop';
import { MusicNetworkSection } from '@/features/settings/components/musicNetwork/MusicNetworkSection';
import { purgeExternalArtworkAllServers } from '@/api/coverCache';
import { purgeExternalArtworkAllServers } from '@/lib/api/coverCache';
export function IntegrationsTab() {
const { t } = useTranslation();
@@ -4,7 +4,7 @@ import {
libraryCoverBackfillResetCursor,
libraryCoverBackfillRunFullPass,
libraryCoverBackfillSetParallel,
} from '@/api/coverCache';
} from '@/lib/api/coverCache';
const COVER_THREADS_MIN = 1;
const COVER_THREADS_MAX = 16;
@@ -11,7 +11,7 @@ import { Copy, Download, Pause, Play, Trash2 } from 'lucide-react';
import { createPortal } from 'react-dom';
import { save as saveDialog } from '@tauri-apps/plugin-dialog';
import { writeFile } from '@tauri-apps/plugin-fs';
import { getLoggingMode, tailRuntimeLogs, type RuntimeLogLine } from '@/api/runtimeLogs';
import { getLoggingMode, tailRuntimeLogs, type RuntimeLogLine } from '@/lib/api/runtimeLogs';
import { invoke } from '@tauri-apps/api/core';
import { useAuthStore } from '@/store/authStore';
import type { LoggingMode } from '@/store/authStoreTypes';