1import { AsyncResult, type Atom, type AtomRegistry } from "effect/reactivity";
2
3/**
4 * Resolves with the first defined `settle` result for a query atom's value.
5 * Resolves `undefined` once `signal` aborts or the query fails, because a
6 * failed query can no longer report the state the caller is waiting for.
7 * Subscribing keeps the atom mounted, so the subscription ends on every exit.
8 */
9export function waitForQuerySettled<A, E, R>(input: {
10 readonly registry: AtomRegistry.AtomRegistry;
11 readonly atom: Atom.Atom<AsyncResult.AsyncResult<A, E>>;
12 readonly settle: (value: A | null) => R | undefined;
13 readonly signal: AbortSignal;
14}): Promise<R | undefined> {
15 const { registry, atom, settle, signal } = input;
16 return new Promise((resolve) => {
17 let done = false;
18 let unsubscribe: (() => void) | undefined;
19 const finish = (result: R | undefined) => {
20 if (done) return;
21 done = true;
22 unsubscribe?.();
23 signal.removeEventListener("abort", onAbort);
24 resolve(result);
25 };
26 const onAbort = () => finish(undefined);
27 const check = (result: AsyncResult.AsyncResult<A, E>) => {
28 if (AsyncResult.isFailure(result)) return finish(undefined);
29 const settled = settle(AsyncResult.getOrElse(result, () => null));
30 if (settled !== undefined) finish(settled);
31 };
32 if (signal.aborted) return finish(undefined);
33 signal.addEventListener("abort", onAbort);
34 unsubscribe = registry.subscribe(atom, check, { immediate: true });
35 if (done) unsubscribe();
36 });
37}