/** * Full implementation of useSyncExternalStoreWithSelector. * Required for zustand compatibility in React 19 environments. * * This replaces the "use-sync-external-store/with-selector" package * which ships as CJS and breaks ESM-only browser builds. * * @module @burdenoff/fe-libs/shared/shims/use-sync-external-store-with-selector */ import { useDebugValue, useEffect, useMemo, useRef, useSyncExternalStore } from 'react'; type Selector = (snapshot: TSnapshot) => TSelection; type IsEqual = (a: TSelection, b: TSelection) => boolean; interface SelectorInstance { hasValue: boolean; value: TSelection | null; } const objectIs = Object.is; export function useSyncExternalStoreWithSelector( subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => TSnapshot, getServerSnapshot: (() => TSnapshot) | undefined, selector: Selector, isEqual?: IsEqual ): TSelection { const instRef = useRef | null>(null); if (instRef.current === null) { instRef.current = { hasValue: false, value: null }; } const inst = instRef.current; const [getSelection, getServerSelection] = useMemo(() => { let hasMemo = false; let memoizedSnapshot: TSnapshot; let memoizedSelection: TSelection; const memoizedSelector = (nextSnapshot: TSnapshot): TSelection => { if (!hasMemo) { hasMemo = true; memoizedSnapshot = nextSnapshot; const nextSelection = selector(nextSnapshot); if (isEqual !== undefined && inst.hasValue) { const currentSelection = inst.value as TSelection; if (isEqual(currentSelection, nextSelection)) { memoizedSelection = currentSelection; return currentSelection; } } memoizedSelection = nextSelection; return nextSelection; } const currentSelection = memoizedSelection; if (objectIs(memoizedSnapshot, nextSnapshot)) { return currentSelection; } const nextSelection = selector(nextSnapshot); if (isEqual !== undefined && isEqual(currentSelection, nextSelection)) { memoizedSnapshot = nextSnapshot; return currentSelection; } memoizedSnapshot = nextSnapshot; memoizedSelection = nextSelection; return nextSelection; }; const getSnapshotWithSelector = () => memoizedSelector(getSnapshot()); const getServerSnapshotWithSelector = getServerSnapshot === undefined ? undefined : () => memoizedSelector(getServerSnapshot()); return [getSnapshotWithSelector, getServerSnapshotWithSelector] as const; }, [getSnapshot, getServerSnapshot, selector, isEqual, inst]); const value = useSyncExternalStore(subscribe, getSelection, getServerSelection); useEffect(() => { inst.hasValue = true; inst.value = value; }, [inst, value]); useDebugValue(value); return value; } export default { useSyncExternalStoreWithSelector, };