refactor(decouple): move coerceOpenArtistRefs to lib/api (off artist feature)

First step of decoupling the audio core from feature imports. coerceOpenArtist
Refs is a pure Subsonic-response normalizer (one-object-vs-array quirk) that was
mis-membered in features/artist but consumed by the audio core (songToTrack,
trackArtistRefs) and others. Relocate it beside its SubsonicOpenArtistRef type in
lib/api/; drop the artist-barrel re-export; repoint the 4 barrel consumers.

Removes the @/features/artist runtime edge from the audio core (utils/playback)
entirely. tsc 0, lint 0/0, suite 319/2353 green.

(composerBrowseSessionStore's ALL_SENTINEL import from @/features/artist remains
— that's a non-audio global browse store, separate from this decouple.)
This commit is contained in:
Psychotoxical
2026-06-30 08:53:56 +02:00
parent a27b26abbd
commit 36b0042d9a
7 changed files with 5 additions and 6 deletions
@@ -1,5 +1,5 @@
import type { SubsonicAlbum, SubsonicOpenArtistRef, SubsonicSong } from '@/lib/api/subsonicTypes';
import { coerceOpenArtistRefs } from '@/features/artist';
import { coerceOpenArtistRefs } from '@/lib/api/openArtistRefs';
function nonEmpty(refs: SubsonicOpenArtistRef[]): refs is SubsonicOpenArtistRef[] {
return refs.length > 0;
-1
View File
@@ -27,7 +27,6 @@ export * from './store/artistAlbumYearSortStore';
export * from './store/artistBrowseSessionStore';
export * from './store/artistLayoutStore';
export * from './utils/artistsHelpers';
export * from './utils/openArtistRefs';
export * from './utils/runArtistDetailActions';
export * from './utils/runArtistDetailPlay';
export * from './utils/sortArtistAlbums';
@@ -1,19 +0,0 @@
import { describe, expect, it } from 'vitest';
import { coerceOpenArtistRefs } from '@/features/artist/utils/openArtistRefs';
describe('coerceOpenArtistRefs', () => {
it('returns an empty array for nullish input', () => {
expect(coerceOpenArtistRefs(undefined)).toEqual([]);
expect(coerceOpenArtistRefs(null)).toEqual([]);
});
it('passes through arrays', () => {
const refs = [{ id: 'a1', name: 'One' }, { id: 'a2', name: 'Two' }];
expect(coerceOpenArtistRefs(refs)).toBe(refs);
});
it('wraps a single ref object from Subsonic JSON', () => {
const ref = { id: 'a1', name: 'Solo' };
expect(coerceOpenArtistRefs(ref)).toEqual([ref]);
});
});
@@ -1,11 +0,0 @@
import type { SubsonicOpenArtistRef } from '@/lib/api/subsonicTypes';
/** Subsonic JSON may return one ref object instead of a one-element array. */
export function coerceOpenArtistRefs(
refs: SubsonicOpenArtistRef[] | SubsonicOpenArtistRef | undefined | null,
): SubsonicOpenArtistRef[] {
if (refs == null) return [];
if (Array.isArray(refs)) return refs;
if (typeof refs === 'object') return [refs];
return [];
}