import * as Data from "effect/Data";
import * as Effect from "effect/Effect";
import * as Predicate from "effect/Predicate";
import * as Schedule from "effect/Schedule";
export class PredicateFailed extends Data.TaggedError("PredicateFailed")<{
message: string;
actual: unknown;
}> {}
export const isPredicateFailed = (e: unknown): e is PredicateFailed =>
Predicate.isTagged(e, "PredicateFailed");
/**
* Retries an effect until a predicate is met.
* @param input - The input to the poll function.
* @param input.description - The description of what is being polled; used in the error message if the predicate fails.
* @param input.effect - The effect to execute until the predicate is met.
* @param input.predicate - The predicate to check if the effect has met the desired state.
* @param input.schedule - The schedule to use for retries; defaults to every 3 seconds.
* @param input.times - The maximum number of times to poll; defaults to 50.
* @returns The value that satisfies the predicate.
*/
export const poll = Effect.fn("poll")(
(input: {
description?: string;
effect: Effect.Effect;
predicate: (value: A) => boolean;
schedule?: Schedule.Schedule;
}) =>
input.effect.pipe(
Effect.filterOrFail(
input.predicate,
(actual) =>
new PredicateFailed({
message: `Predicate failed: ${input.description ?? ""}`,
actual,
}),
),
Effect.retry({
while: isPredicateFailed,
schedule:
input.schedule ??
Schedule.max([Schedule.spaced("5 seconds"), Schedule.recurs(50)]),
}),
),
);