refactor(network): decouple network-reachability layer from features → lib/network

The connection-reachability + Subsonic network-guard helpers were pinned low by
four lib/api consumers (subsonicLibrary/Playlists/Ratings/Scrobble) yet ran two
lower-layer→feature inversions. Both removed without a registry:

- devOfflineBrowseStore is a self-contained DEV-only toggle with zero offline-
  feature coupling — it was only colocated there. Relocated features/offline/store
  → store/ (global). The @/features/offline barrel re-exports it so feature/UI
  consumers are unchanged; the three lower-layer readers (subsonicNetworkGuard,
  activeServerReachability, useConnectionStatus) now import it from @/store directly.

- subsonicNetworkGuard's only other feature dep was playback's resolvePlaybackUrl,
  used solely for the psysonic-local:// skip check. Added hasLocalPlaybackUrl to the
  existing M4 substrate store/localPlaybackResolve — it mirrors resolvePlaybackUrl's
  local-source branch exactly (same profile resolution; the empty-serverId playback
  fallback never applies in the guard), so the skip stays bit-identical.

network/ (subsonicNetworkGuard + activeServerReachability + tests) → lib/network,
now @/features-free. useConnectionStatus is now iron-rule-clean and stays in hooks/
(cross-cutting). Test mocks retargeted to the new seam modules.

Behavior-adjacent (covered by suite; default paths identical): the local-bytes skip
helper — flag for offline-playback runtime QA alongside the M4 media-resolver seam.
This commit is contained in:
Psychotoxical
2026-06-30 21:23:44 +02:00
parent d4ab56f5a6
commit c37d5f7389
40 changed files with 81 additions and 68 deletions
@@ -0,0 +1,46 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { useDevOfflineBrowseStore } from '@/store/devOfflineBrowseStore';
import {
getActiveServerReachable,
isActiveServerReachable,
onActiveServerBecameReachable,
resetActiveServerConnectionSnapshot,
setActiveServerReachable,
} from '@/lib/network/activeServerReachability';
describe('activeServerReachability', () => {
beforeEach(() => {
useDevOfflineBrowseStore.setState({ forceOffline: false });
resetActiveServerConnectionSnapshot();
});
it('isActiveServerReachable requires an explicit successful probe', () => {
expect(isActiveServerReachable()).toBe(false);
setActiveServerReachable(true);
expect(isActiveServerReachable()).toBe(true);
setActiveServerReachable(false);
expect(isActiveServerReachable()).toBe(false);
});
it('exposes the last probe result', () => {
setActiveServerReachable(true);
expect(getActiveServerReachable()).toBe(true);
});
it('isActiveServerReachable is false when DEV force-offline is enabled', () => {
if (!import.meta.env.DEV) return;
setActiveServerReachable(true);
useDevOfflineBrowseStore.setState({ forceOffline: true });
expect(isActiveServerReachable()).toBe(false);
});
it('onActiveServerBecameReachable fires only on false/null → true', () => {
const listener = vi.fn();
onActiveServerBecameReachable(listener);
setActiveServerReachable(false);
setActiveServerReachable(true);
expect(listener).toHaveBeenCalledTimes(1);
setActiveServerReachable(true);
expect(listener).toHaveBeenCalledTimes(1);
});
});