Files
siftlode/frontend/src/lib/store.ts
T
peter c4298b0a73 docs(comments): no WebSocket and no CI in the present tense
The chat cut removed the only WebSocket and CI went before it, but comments still leaned on both:
`?account=` was justified by the socket, job polling by an in-process WS registry, the e2e flows as
"proxy-safe (none use the WebSocket)", and pyrightconfig named a CI container the engine contract
test never checked. Each now says what holds today (the wallet gate, no push channel, the two
contract test halves, the image build's `npm ci`, the gate).
2026-10-05 00:50:36 +02:00

37 lines
1.3 KiB
TypeScript

import { useSyncExternalStore } from "react";
// A minimal observable value shared across React components AND non-React code (the api
// layer, the debug recorder, the error dialog, etc.). Replaces the hand-rolled
// `listeners`/`subscribe`/`emit` triad that several modules each re-implemented slightly differently.
export interface Store<T> {
get(): T;
set(next: T | ((prev: T) => T)): void;
subscribe(listener: () => void): () => void;
/** Subscribe from a component; re-renders on every change. Named `use…` on purpose: the React
* Compiler knows a hook only by that name, and a plain `use()` was compiled as an ordinary call —
* memoized, so it ran on the first render alone (usePlaylistActions then rendered fewer hooks
* and threw; Back to top never re-rendered). */
useValue(): T;
}
export function createStore<T>(initial: T): Store<T> {
let value = initial;
const listeners = new Set<() => void>();
const get = () => value;
const subscribe = (l: () => void) => {
listeners.add(l);
return () => {
listeners.delete(l);
};
};
return {
get,
set(next) {
value = typeof next === "function" ? (next as (p: T) => T)(value) : next;
listeners.forEach((l) => l());
},
subscribe,
useValue: () => useSyncExternalStore(subscribe, get, get),
};
}