import * as Chunk from "../Chunk.js";
import * as Clock from "../Clock.js";
import * as Context from "../Context.js";
import * as Cron from "../Cron.js";
import * as Duration from "../Duration.js";
import * as Either from "../Either.js";
import * as Equal from "../Equal.js";
import { constVoid, dual, pipe } from "../Function.js";
import * as Option from "../Option.js";
import { pipeArguments } from "../Pipeable.js";
import { hasProperty } from "../Predicate.js";
import * as Random from "../Random.js";
import * as ScheduleDecision from "../ScheduleDecision.js";
import * as Interval from "../ScheduleInterval.js";
import * as Intervals from "../ScheduleIntervals.js";
import * as internalCause from "./cause.js";
import * as effect from "./core-effect.js";
import * as core from "./core.js";
import { forkScoped } from "./effect/circular.js";
import * as ref from "./ref.js";
/** @internal */
const ScheduleSymbolKey = "effect/Schedule";
/** @internal */
export const ScheduleTypeId = /*#__PURE__*/Symbol.for(ScheduleSymbolKey);
/** @internal */
export const isSchedule = u => hasProperty(u, ScheduleTypeId);
/** @internal */
const ScheduleDriverSymbolKey = "effect/ScheduleDriver";
/** @internal */
export const ScheduleDriverTypeId = /*#__PURE__*/Symbol.for(ScheduleDriverSymbolKey);
/** @internal */
const defaultIterationMetadata = {
start: 0,
now: 0,
input: undefined,
elapsed: Duration.zero,
elapsedSincePrevious: Duration.zero,
recurrence: 0
};
/** @internal */
export const CurrentIterationMetadata = /*#__PURE__*/Context.Reference()("effect/Schedule/CurrentIterationMetadata", {
defaultValue: () => defaultIterationMetadata
});
const scheduleVariance = {
/* c8 ignore next */
_Out: _ => _,
/* c8 ignore next */
_In: _ => _,
/* c8 ignore next */
_R: _ => _
};
const scheduleDriverVariance = {
/* c8 ignore next */
_Out: _ => _,
/* c8 ignore next */
_In: _ => _,
/* c8 ignore next */
_R: _ => _
};
/** @internal */
class ScheduleImpl {
initial;
step;
[ScheduleTypeId] = scheduleVariance;
constructor(initial, step) {
this.initial = initial;
this.step = step;
}
pipe() {
return pipeArguments(this, arguments);
}
}
/** @internal */
const updateInfo = (iterationMetaRef, now, input) => ref.update(iterationMetaRef, prev => prev.recurrence === 0 ? {
now,
input,
recurrence: prev.recurrence + 1,
elapsed: Duration.zero,
elapsedSincePrevious: Duration.zero,
start: now
} : {
now,
input,
recurrence: prev.recurrence + 1,
elapsed: Duration.millis(now - prev.start),
elapsedSincePrevious: Duration.millis(now - prev.now),
start: prev.start
});
/** @internal */
class ScheduleDriverImpl {
schedule;
ref;
[ScheduleDriverTypeId] = scheduleDriverVariance;
constructor(schedule, ref) {
this.schedule = schedule;
this.ref = ref;
}
get state() {
return core.map(ref.get(this.ref), tuple => tuple[1]);
}
get last() {
return core.flatMap(ref.get(this.ref), ([element, _]) => {
switch (element._tag) {
case "None":
{
return core.failSync(() => new core.NoSuchElementException());
}
case "Some":
{
return core.succeed(element.value);
}
}
});
}
iterationMeta = /*#__PURE__*/ref.unsafeMake(defaultIterationMetadata);
get reset() {
return ref.set(this.ref, [Option.none(), this.schedule.initial]).pipe(core.zipLeft(ref.set(this.iterationMeta, defaultIterationMetadata)));
}
next(input) {
return pipe(core.map(ref.get(this.ref), tuple => tuple[1]), core.flatMap(state => pipe(Clock.currentTimeMillis, core.flatMap(now => pipe(core.suspend(() => this.schedule.step(now, input, state)), core.flatMap(([state, out, decision]) => {
const setState = ref.set(this.ref, [Option.some(out), state]);
if (ScheduleDecision.isDone(decision)) {
return setState.pipe(core.zipRight(core.fail(Option.none())));
}
const millis = Intervals.start(decision.intervals) - now;
if (millis <= 0) {
return setState.pipe(core.zipRight(updateInfo(this.iterationMeta, now, input)), core.as(out));
}
const duration = Duration.millis(millis);
return pipe(setState, core.zipRight(updateInfo(this.iterationMeta, now, input)), core.zipRight(effect.sleep(duration)), core.as(out));
}))))));
}
}
/** @internal */
export const makeWithState = (initial, step) => new ScheduleImpl(initial, step);
/** @internal */
export const addDelay = /*#__PURE__*/dual(2, (self, f) => addDelayEffect(self, out => core.sync(() => f(out))));
/** @internal */
export const addDelayEffect = /*#__PURE__*/dual(2, (self, f) => modifyDelayEffect(self, (out, duration) => core.map(f(out), delay => Duration.sum(duration, Duration.decode(delay)))));
/** @internal */
export const andThen = /*#__PURE__*/dual(2, (self, that) => map(andThenEither(self, that), Either.merge));
/** @internal */
export const andThenEither = /*#__PURE__*/dual(2, (self, that) => makeWithState([self.initial, that.initial, true], (now, input, state) => state[2] ? core.flatMap(self.step(now, input, state[0]), ([lState, out, decision]) => {
if (ScheduleDecision.isDone(decision)) {
return core.map(that.step(now, input, state[1]), ([rState, out, decision]) => [[lState, rState, false], Either.right(out), decision]);
}
return core.succeed([[lState, state[1], true], Either.left(out), decision]);
}) : core.map(that.step(now, input, state[1]), ([rState, out, decision]) => [[state[0], rState, false], Either.right(out), decision])));
/** @internal */
export const as = /*#__PURE__*/dual(2, (self, out) => map(self, () => out));
/** @internal */
export const asVoid = self => map(self, constVoid);
/** @internal */
export const bothInOut = /*#__PURE__*/dual(2, (self, that) => makeWithState([self.initial, that.initial], (now, [in1, in2], state) => core.zipWith(self.step(now, in1, state[0]), that.step(now, in2, state[1]), ([lState, out, lDecision], [rState, out2, rDecision]) => {
if (ScheduleDecision.isContinue(lDecision) && ScheduleDecision.isContinue(rDecision)) {
const interval = pipe(lDecision.intervals, Intervals.union(rDecision.intervals));
return [[lState, rState], [out, out2], ScheduleDecision.continue(interval)];
}
return [[lState, rState], [out, out2], ScheduleDecision.done];
})));
/** @internal */
export const check = /*#__PURE__*/dual(2, (self, test) => checkEffect(self, (input, out) => core.sync(() => test(input, out))));
/** @internal */
export const checkEffect = /*#__PURE__*/dual(2, (self, test) => makeWithState(self.initial, (now, input, state) => core.flatMap(self.step(now, input, state), ([state, out, decision]) => {
if (ScheduleDecision.isDone(decision)) {
return core.succeed([state, out, ScheduleDecision.done]);
}
return core.map(test(input, out), cont => cont ? [state, out, decision] : [state, out, ScheduleDecision.done]);
})));
/** @internal */
export const collectAllInputs = () => collectAllOutputs(identity());
/** @internal */
export const collectAllOutputs = self => reduce(self, Chunk.empty(), (outs, out) => pipe(outs, Chunk.append(out)));
/** @internal */
export const collectUntil = f => collectAllOutputs(recurUntil(f));
/** @internal */
export const collectUntilEffect = f => collectAllOutputs(recurUntilEffect(f));
/** @internal */
export const collectWhile = f => collectAllOutputs(recurWhile(f));
/** @internal */
export const collectWhileEffect = f => collectAllOutputs(recurWhileEffect(f));
/** @internal */
export const compose = /*#__PURE__*/dual(2, (self, that) => makeWithState([self.initial, that.initial], (now, input, state) => core.flatMap(self.step(now, input, state[0]), ([lState, out, lDecision]) => core.map(that.step(now, out, state[1]), ([rState, out2, rDecision]) => ScheduleDecision.isDone(lDecision) ? [[lState, rState], out2, ScheduleDecision.done] : ScheduleDecision.isDone(rDecision) ? [[lState, rState], out2, ScheduleDecision.done] : [[lState, rState], out2, ScheduleDecision.continue(pipe(lDecision.intervals, Intervals.max(rDecision.intervals)))]))));
/** @internal */
export const mapInput = /*#__PURE__*/dual(2, (self, f) => mapInputEffect(self, input2 => core.sync(() => f(input2))));
/** @internal */
export const mapInputContext = /*#__PURE__*/dual(2, (self, f) => makeWithState(self.initial, (now, input, state) => core.mapInputContext(self.step(now, input, state), f)));
/** @internal */
export const mapInputEffect = /*#__PURE__*/dual(2, (self, f) => makeWithState(self.initial, (now, input2, state) => core.flatMap(f(input2), input => self.step(now, input, state))));
/** @internal */
export const cron = (expression, tz) => {
const parsed = Cron.isCron(expression) ? Either.right(expression) : Cron.parse(expression, tz);
return makeWithState([true, [Number.MIN_SAFE_INTEGER, 0, 0]], (now, _, [initial, previous]) => {
if (now < previous[0]) {
return core.succeed([[false, previous], [previous[1], previous[2]], ScheduleDecision.continueWith(Interval.make(previous[1], previous[2]))]);
}
if (Either.isLeft(parsed)) {
return core.die(parsed.left);
}
const cron = parsed.right;
const date = new Date(now);
let next;
if (initial && Cron.match(cron, date)) {
next = now;
}
next = Cron.next(cron, date).getTime();
const start = beginningOfSecond(next);
const end = endOfSecond(next);
return core.succeed([[false, [next, start, end]], [start, end], ScheduleDecision.continueWith(Interval.make(start, end))]);
});
};
/** @internal */
export const dayOfMonth = day => {
return makeWithState([Number.NEGATIVE_INFINITY, 0], (now, _, state) => {
if (!Number.isInteger(day) || day < 1 || 31 < day) {
return core.dieSync(() => new core.IllegalArgumentException(`Invalid argument in: dayOfMonth(${day}). Must be in range 1...31`));
}
const n = state[1];
const initial = n === 0;
const day0 = nextDayOfMonth(now, day, initial);
const start = beginningOfDay(day0);
const end = endOfDay(day0);
const interval = Interval.make(start, end);
return core.succeed([[end, n + 1], n, ScheduleDecision.continueWith(interval)]);
});
};
/** @internal */
export const dayOfWeek = day => {
return makeWithState([Number.MIN_SAFE_INTEGER, 0], (now, _, state) => {
if (!Number.isInteger(day) || day < 1 || 7 < day) {
return core.dieSync(() => new core.IllegalArgumentException(`Invalid argument in: dayOfWeek(${day}). Must be in range 1 (Monday)...7 (Sunday)`));
}
const n = state[1];
const initial = n === 0;
const day0 = nextDay(now, day, initial);
const start = beginningOfDay(day0);
const end = endOfDay(day0);
const interval = Interval.make(start, end);
return core.succeed([[end, n + 1], n, ScheduleDecision.continueWith(interval)]);
});
};
/** @internal */
export const delayed = /*#__PURE__*/dual(2, (self, f) => delayedEffect(self, duration => core.sync(() => f(duration))));
/** @internal */
export const delayedEffect = /*#__PURE__*/dual(2, (self, f) => modifyDelayEffect(self, (_, delay) => f(delay)));
/** @internal */
export const delayedSchedule = schedule => addDelay(schedule, x => x);
/** @internal */
export const delays = self => makeWithState(self.initial, (now, input, state) => pipe(self.step(now, input, state), core.flatMap(([state, _, decision]) => {
if (ScheduleDecision.isDone(decision)) {
return core.succeed([state, Duration.zero, decision]);
}
return core.succeed([state, Duration.millis(Intervals.start(decision.intervals) - now), decision]);
})));
/** @internal */
export const mapBoth = /*#__PURE__*/dual(2, (self, {
onInput,
onOutput
}) => map(mapInput(self, onInput), onOutput));
/** @internal */
export const mapBothEffect = /*#__PURE__*/dual(2, (self, {
onInput,
onOutput
}) => mapEffect(mapInputEffect(self, onInput), onOutput));
/** @internal */
export const driver = self => pipe(ref.make([Option.none(), self.initial]), core.map(ref => new ScheduleDriverImpl(self, ref)));
/** @internal */
export const duration = durationInput => {
const duration = Duration.decode(durationInput);
const durationMillis = Duration.toMillis(duration);
return makeWithState(true, (now, _, state) => core.succeed(state ? [false, duration, ScheduleDecision.continueWith(Interval.after(now + durationMillis))] : [false, Duration.zero, ScheduleDecision.done]));
};
/** @internal */
export const either = /*#__PURE__*/dual(2, (self, that) => union(self, that));
/** @internal */
export const eitherWith = /*#__PURE__*/dual(3, (self, that, f) => unionWith(self, that, f));
/** @internal */
export const ensuring = /*#__PURE__*/dual(2, (self, finalizer) => makeWithState(self.initial, (now, input, state) => core.flatMap(self.step(now, input, state), ([state, out, decision]) => ScheduleDecision.isDone(decision) ? core.as(finalizer, [state, out, decision]) : core.succeed([state, out, decision]))));
/** @internal */
export const exponential = (baseInput, factor = 2.0) => {
const base = Duration.decode(baseInput);
return delayedSchedule(map(forever, i => Duration.times(base, Math.pow(factor, i))));
};
/** @internal */
export const fibonacci = oneInput => {
const one = Duration.decode(oneInput);
return delayedSchedule(pipe(unfold([one, one], ([a, b]) => [b, Duration.sum(a, b)]), map(out => out[0])));
};
/** @internal */
export const fixed = intervalInput => {
const interval = Duration.decode(intervalInput);
const intervalMillis = Duration.toMillis(interval);
return makeWithState([Option.none(), 0], (now, _, [option, n]) => core.sync(() => {
switch (option._tag) {
case "None":
{
return [[Option.some([now, now + intervalMillis]), n + 1], n, ScheduleDecision.continueWith(Interval.after(now + intervalMillis))];
}
case "Some":
{
const [startMillis, lastRun] = option.value;
const runningBehind = now > lastRun + intervalMillis;
const boundary = Equal.equals(interval, Duration.zero) ? interval : Duration.millis(intervalMillis - (now - startMillis) % intervalMillis);
const sleepTime = Equal.equals(boundary, Duration.zero) ? interval : boundary;
const nextRun = runningBehind ? now : now + Duration.toMillis(sleepTime);
return [[Option.some([startMillis, nextRun]), n + 1], n, ScheduleDecision.continueWith(Interval.after(nextRun))];
}
}
}));
};
/** @internal */
export const fromDelay = delay => duration(delay);
/** @internal */
export const fromDelays = (delay, ...delays) => makeWithState([[delay, ...delays].map(_ => Duration.decode(_)), true], (now, _, [durations, cont]) => core.sync(() => {
if (cont) {
const x = durations[0];
const interval = Interval.after(now + Duration.toMillis(x));
if (durations.length >= 2) {
return [[durations.slice(1), true], x, ScheduleDecision.continueWith(interval)];
}
const y = durations.slice(1);
return [[[x, ...y], false], x, ScheduleDecision.continueWith(interval)];
}
return [[durations, false], Duration.zero, ScheduleDecision.done];
}));
/** @internal */
export const fromFunction = f => map(identity(), f);
/** @internal */
export const hourOfDay = hour => makeWithState([Number.NEGATIVE_INFINITY, 0], (now, _, state) => {
if (!Number.isInteger(hour) || hour < 0 || 23 < hour) {
return core.dieSync(() => new core.IllegalArgumentException(`Invalid argument in: hourOfDay(${hour}). Must be in range 0...23`));
}
const n = state[1];
const initial = n === 0;
const hour0 = nextHour(now, hour, initial);
const start = beginningOfHour(hour0);
const end = endOfHour(hour0);
const interval = Interval.make(start, end);
return core.succeed([[end, n + 1], n, ScheduleDecision.continueWith(interval)]);
});
/** @internal */
export const identity = () => makeWithState(void 0, (now, input, state) => core.succeed([state, input, ScheduleDecision.continueWith(Interval.after(now))]));
/** @internal */
export const intersect = /*#__PURE__*/dual(2, (self, that) => intersectWith(self, that, Intervals.intersect));
/** @internal */
export const intersectWith = /*#__PURE__*/dual(3, (self, that, f) => makeWithState([self.initial, that.initial], (now, input, state) => pipe(core.zipWith(self.step(now, input, state[0]), that.step(now, input, state[1]), (a, b) => [a, b]), core.flatMap(([[lState, out, lDecision], [rState, out2, rDecision]]) => {
if (ScheduleDecision.isContinue(lDecision) && ScheduleDecision.isContinue(rDecision)) {
return intersectWithLoop(self, that, input, lState, out, lDecision.intervals, rState, out2, rDecision.intervals, f);
}
return core.succeed([[lState, rState], [out, out2], ScheduleDecision.done]);
}))));
/** @internal */
const intersectWithLoop = (self, that, input, lState, out, lInterval, rState, out2, rInterval, f) => {
const combined = f(lInterval, rInterval);
if (Intervals.isNonEmpty(combined)) {
return core.succeed([[lState, rState], [out, out2], ScheduleDecision.continue(combined)]);
}
if (pipe(lInterval, Intervals.lessThan(rInterval))) {
return core.flatMap(self.step(Intervals.end(lInterval), input, lState), ([lState, out, decision]) => {
if (ScheduleDecision.isDone(decision)) {
return core.succeed([[lState, rState], [out, out2], ScheduleDecision.done]);
}
return intersectWithLoop(self, that, input, lState, out, decision.intervals, rState, out2, rInterval, f);
});
}
return core.flatMap(that.step(Intervals.end(rInterval), input, rState), ([rState, out2, decision]) => {
if (ScheduleDecision.isDone(decision)) {
return core.succeed([[lState, rState], [out, out2], ScheduleDecision.done]);
}
return intersectWithLoop(self, that, input, lState, out, lInterval, rState, out2, decision.intervals, f);
});
};
/** @internal */
export const jittered = self => jitteredWith(self, {
min: 0.8,
max: 1.2
});
/** @internal */
export const jitteredWith = /*#__PURE__*/dual(2, (self, options) => {
const {
max,
min
} = Object.assign({
min: 0.8,
max: 1.2
}, options);
return delayedEffect(self, duration => core.map(Random.next, random => {
const d = Duration.toMillis(duration);
const jittered = d * min * (1 - random) + d * max * random;
return Duration.millis(jittered);
}));
});
/** @internal */
export const linear = baseInput => {
const base = Duration.decode(baseInput);
return delayedSchedule(map(forever, i => Duration.times(base, i + 1)));
};
/** @internal */
export const map = /*#__PURE__*/dual(2, (self, f) => mapEffect(self, out => core.sync(() => f(out))));
/** @internal */
export const mapEffect = /*#__PURE__*/dual(2, (self, f) => makeWithState(self.initial, (now, input, state) => core.flatMap(self.step(now, input, state), ([state, out, decision]) => core.map(f(out), out2 => [state, out2, decision]))));
/** @internal */
export const minuteOfHour = minute => makeWithState([Number.MIN_SAFE_INTEGER, 0], (now, _, state) => {
if (!Number.isInteger(minute) || minute < 0 || 59 < minute) {
return core.dieSync(() => new core.IllegalArgumentException(`Invalid argument in: minuteOfHour(${minute}). Must be in range 0...59`));
}
const n = state[1];
const initial = n === 0;
const minute0 = nextMinute(now, minute, initial);
const start = beginningOfMinute(minute0);
const end = endOfMinute(minute0);
const interval = Interval.make(start, end);
return core.succeed([[end, n + 1], n, ScheduleDecision.continueWith(interval)]);
});
/** @internal */
export const modifyDelay = /*#__PURE__*/dual(2, (self, f) => modifyDelayEffect(self, (out, duration) => core.sync(() => f(out, duration))));
/** @internal */
export const modifyDelayEffect = /*#__PURE__*/dual(2, (self, f) => makeWithState(self.initial, (now, input, state) => core.flatMap(self.step(now, input, state), ([state, out, decision]) => {
if (ScheduleDecision.isDone(decision)) {
return core.succeed([state, out, decision]);
}
const intervals = decision.intervals;
const delay = Interval.size(Interval.make(now, Intervals.start(intervals)));
return core.map(f(out, delay), durationInput => {
const duration = Duration.decode(durationInput);
const oldStart = Intervals.start(intervals);
const newStart = now + Duration.toMillis(duration);
const delta = newStart - oldStart;
const newEnd = Math.max(0, Intervals.end(intervals) + delta);
const newInterval = Interval.make(newStart, newEnd);
return [state, out, ScheduleDecision.continueWith(newInterval)];
});
})));
/** @internal */
export const onDecision = /*#__PURE__*/dual(2, (self, f) => makeWithState(self.initial, (now, input, state) => core.flatMap(self.step(now, input, state), ([state, out, decision]) => core.as(f(out, decision), [state, out, decision]))));
/** @internal */
export const passthrough = self => makeWithState(self.initial, (now, input, state) => pipe(self.step(now, input, state), core.map(([state, _, decision]) => [state, input, decision])));
/** @internal */
export const provideContext = /*#__PURE__*/dual(2, (self, context) => makeWithState(self.initial, (now, input, state) => core.provideContext(self.step(now, input, state), context)));
/** @internal */
export const provideService = /*#__PURE__*/dual(3, (self, tag, service) => makeWithState(self.initial, (now, input, state) => core.contextWithEffect(env => core.provideContext(
// @ts-expect-error
self.step(now, input, state), Context.add(env, tag, service)))));
/** @internal */
export const recurUntil = f => untilInput(identity(), f);
/** @internal */
export const recurUntilEffect = f => untilInputEffect(identity(), f);
/** @internal */
export const recurUntilOption = pf => untilOutput(map(identity(), pf), Option.isSome);
/** @internal */
export const recurUpTo = durationInput => {
const duration = Duration.decode(durationInput);
return whileOutput(elapsed, elapsed => Duration.lessThan(elapsed, duration));
};
/** @internal */
export const recurWhile = f => whileInput(identity(), f);
/** @internal */
export const recurWhileEffect = f => whileInputEffect(identity(), f);
/** @internal */
export const recurs = n => whileOutput(forever, out => out < n);
/** @internal */
export const reduce = /*#__PURE__*/dual(3, (self, zero, f) => reduceEffect(self, zero, (z, out) => core.sync(() => f(z, out))));
/** @internal */
export const reduceEffect = /*#__PURE__*/dual(3, (self, zero, f) => makeWithState([self.initial, zero], (now, input, [s, z]) => core.flatMap(self.step(now, input, s), ([s, out, decision]) => ScheduleDecision.isDone(decision) ? core.succeed([[s, z], z, decision]) : core.map(f(z, out), z2 => [[s, z2], z, decision]))));
/** @internal */
export const repeatForever = self => makeWithState(self.initial, (now, input, state) => {
const step = (now, input, state) => core.flatMap(self.step(now, input, state), ([state, out, decision]) => ScheduleDecision.isDone(decision) ? step(now, input, self.initial) : core.succeed([state, out, decision]));
return step(now, input, state);
});
/** @internal */
export const repetitions = self => reduce(self, 0, (n, _) => n + 1);
/** @internal */
export const resetAfter = /*#__PURE__*/dual(2, (self, durationInput) => {
const duration = Duration.decode(durationInput);
return pipe(self, intersect(elapsed), resetWhen(([, time]) => Duration.greaterThanOrEqualTo(time, duration)), map(out => out[0]));
});
/** @internal */
export const resetWhen = /*#__PURE__*/dual(2, (self, f) => makeWithState(self.initial, (now, input, state) => core.flatMap(self.step(now, input, state), ([state, out, decision]) => f(out) ? self.step(now, input, self.initial) : core.succeed([state, out, decision]))));
/** @internal */
export const run = /*#__PURE__*/dual(3, (self, now, input) => pipe(runLoop(self, now, Chunk.fromIterable(input), self.initial, Chunk.empty()), core.map(list => Chunk.reverse(list))));
/** @internal */
const runLoop = (self, now, inputs, state, acc) => {
if (!Chunk.isNonEmpty(inputs)) {
return core.succeed(acc);
}
const input = Chunk.headNonEmpty(inputs);
const nextInputs = Chunk.tailNonEmpty(inputs);
return core.flatMap(self.step(now, input, state), ([state, out, decision]) => {
if (ScheduleDecision.isDone(decision)) {
return core.sync(() => pipe(acc, Chunk.prepend(out)));
}
return runLoop(self, Intervals.start(decision.intervals), nextInputs, state, Chunk.prepend(acc, out));
});
};
/** @internal */
export const secondOfMinute = second => makeWithState([Number.NEGATIVE_INFINITY, 0], (now, _, state) => {
if (!Number.isInteger(second) || second < 0 || 59 < second) {
return core.dieSync(() => new core.IllegalArgumentException(`Invalid argument in: secondOfMinute(${second}). Must be in range 0...59`));
}
const n = state[1];
const initial = n === 0;
const second0 = nextSecond(now, second, initial);
const start = beginningOfSecond(second0);
const end = endOfSecond(second0);
const interval = Interval.make(start, end);
return core.succeed([[end, n + 1], n, ScheduleDecision.continueWith(interval)]);
});
/** @internal */
export const spaced = duration => addDelay(forever, () => duration);
/** @internal */
export const succeed = value => map(forever, () => value);
/** @internal */
export const sync = evaluate => map(forever, evaluate);
/** @internal */
export const tapInput = /*#__PURE__*/dual(2, (self, f) => makeWithState(self.initial, (now, input, state) => core.zipRight(f(input), self.step(now, input, state))));
/** @internal */
export const tapOutput = /*#__PURE__*/dual(2, (self, f) => makeWithState(self.initial, (now, input, state) => core.tap(self.step(now, input, state), ([, out]) => f(out))));
/** @internal */
export const unfold = (initial, f) => makeWithState(initial, (now, _, state) => core.sync(() => [f(state), state, ScheduleDecision.continueWith(Interval.after(now))]));
/** @internal */
export const union = /*#__PURE__*/dual(2, (self, that) => unionWith(self, that, Intervals.union));
/** @internal */
export const unionWith = /*#__PURE__*/dual(3, (self, that, f) => makeWithState([self.initial, that.initial], (now, input, state) => core.zipWith(self.step(now, input, state[0]), that.step(now, input, state[1]), ([lState, l, lDecision], [rState, r, rDecision]) => {
if (ScheduleDecision.isDone(lDecision) && ScheduleDecision.isDone(rDecision)) {
return [[lState, rState], [l, r], ScheduleDecision.done];
}
if (ScheduleDecision.isDone(lDecision) && ScheduleDecision.isContinue(rDecision)) {
return [[lState, rState], [l, r], ScheduleDecision.continue(rDecision.intervals)];
}
if (ScheduleDecision.isContinue(lDecision) && ScheduleDecision.isDone(rDecision)) {
return [[lState, rState], [l, r], ScheduleDecision.continue(lDecision.intervals)];
}
if (ScheduleDecision.isContinue(lDecision) && ScheduleDecision.isContinue(rDecision)) {
const combined = f(lDecision.intervals, rDecision.intervals);
return [[lState, rState], [l, r], ScheduleDecision.continue(combined)];
}
throw new Error("BUG: Schedule.unionWith - please report an issue at https://github.com/Effect-TS/effect/issues");
})));
/** @internal */
export const untilInput = /*#__PURE__*/dual(2, (self, f) => check(self, (input, _) => !f(input)));
/** @internal */
export const untilInputEffect = /*#__PURE__*/dual(2, (self, f) => checkEffect(self, (input, _) => effect.negate(f(input))));
/** @internal */
export const untilOutput = /*#__PURE__*/dual(2, (self, f) => check(self, (_, out) => !f(out)));
/** @internal */
export const untilOutputEffect = /*#__PURE__*/dual(2, (self, f) => checkEffect(self, (_, out) => effect.negate(f(out))));
/** @internal */
export const upTo = /*#__PURE__*/dual(2, (self, duration) => zipLeft(self, recurUpTo(duration)));
/** @internal */
export const whileInput = /*#__PURE__*/dual(2, (self, f) => check(self, (input, _) => f(input)));
/** @internal */
export const whileInputEffect = /*#__PURE__*/dual(2, (self, f) => checkEffect(self, (input, _) => f(input)));
/** @internal */
export const whileOutput = /*#__PURE__*/dual(2, (self, f) => check(self, (_, out) => f(out)));
/** @internal */
export const whileOutputEffect = /*#__PURE__*/dual(2, (self, f) => checkEffect(self, (_, out) => f(out)));
/** @internal */
export const windowed = intervalInput => {
const interval = Duration.decode(intervalInput);
const millis = Duration.toMillis(interval);
return makeWithState([Option.none(), 0], (now, _, [option, n]) => {
switch (option._tag) {
case "None":
{
return core.succeed([[Option.some(now), n + 1], n, ScheduleDecision.continueWith(Interval.after(now + millis))]);
}
case "Some":
{
return core.succeed([[Option.some(option.value), n + 1], n, ScheduleDecision.continueWith(Interval.after(now + (millis - (now - option.value) % millis)))]);
}
}
});
};
/** @internal */
export const zipLeft = /*#__PURE__*/dual(2, (self, that) => map(intersect(self, that), out => out[0]));
/** @internal */
export const zipRight = /*#__PURE__*/dual(2, (self, that) => map(intersect(self, that), out => out[1]));
/** @internal */
export const zipWith = /*#__PURE__*/dual(3, (self, that, f) => map(intersect(self, that), ([out, out2]) => f(out, out2)));
// -----------------------------------------------------------------------------
// Seconds
// -----------------------------------------------------------------------------
/** @internal */
export const beginningOfSecond = now => {
const date = new Date(now);
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds(), 0).getTime();
};
/** @internal */
export const endOfSecond = now => {
const date = new Date(beginningOfSecond(now));
return date.setSeconds(date.getSeconds() + 1);
};
/** @internal */
export const nextSecond = (now, second, initial) => {
const date = new Date(now);
if (date.getSeconds() === second && initial) {
return now;
}
if (date.getSeconds() < second) {
return date.setSeconds(second);
}
// Set seconds to the provided value and add one minute
const newDate = new Date(date.setSeconds(second));
return newDate.setTime(newDate.getTime() + 1000 * 60);
};
// -----------------------------------------------------------------------------
// Minutes
// -----------------------------------------------------------------------------
/** @internal */
export const beginningOfMinute = now => {
const date = new Date(now);
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), date.getMinutes(), 0, 0).getTime();
};
/** @internal */
export const endOfMinute = now => {
const date = new Date(beginningOfMinute(now));
return date.setMinutes(date.getMinutes() + 1);
};
/** @internal */
export const nextMinute = (now, minute, initial) => {
const date = new Date(now);
if (date.getMinutes() === minute && initial) {
return now;
}
if (date.getMinutes() < minute) {
return date.setMinutes(minute);
}
// Set minutes to the provided value and add one hour
const newDate = new Date(date.setMinutes(minute));
return newDate.setTime(newDate.getTime() + 1000 * 60 * 60);
};
// -----------------------------------------------------------------------------
// Hours
// -----------------------------------------------------------------------------
/** @internal */
export const beginningOfHour = now => {
const date = new Date(now);
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), date.getHours(), 0, 0, 0).getTime();
};
/** @internal */
export const endOfHour = now => {
const date = new Date(beginningOfHour(now));
return date.setHours(date.getHours() + 1);
};
/** @internal */
export const nextHour = (now, hour, initial) => {
const date = new Date(now);
if (date.getHours() === hour && initial) {
return now;
}
if (date.getHours() < hour) {
return date.setHours(hour);
}
// Set hours to the provided value and add one day
const newDate = new Date(date.setHours(hour));
return newDate.setTime(newDate.getTime() + 1000 * 60 * 60 * 24);
};
// -----------------------------------------------------------------------------
// Days
// -----------------------------------------------------------------------------
/** @internal */
export const beginningOfDay = now => {
const date = new Date(now);
return new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0, 0).getTime();
};
/** @internal */
export const endOfDay = now => {
const date = new Date(beginningOfDay(now));
return date.setDate(date.getDate() + 1);
};
/** @internal */
export const nextDay = (now, dayOfWeek, initial) => {
const date = new Date(now);
if (date.getDay() === dayOfWeek && initial) {
return now;
}
const nextDayOfWeek = (7 + dayOfWeek - date.getDay()) % 7;
return date.setDate(date.getDate() + (nextDayOfWeek === 0 ? 7 : nextDayOfWeek));
};
/** @internal */
export const nextDayOfMonth = (now, day, initial) => {
const date = new Date(now);
if (date.getDate() === day && initial) {
return now;
}
if (date.getDate() < day) {
return date.setDate(day);
}
return findNextMonth(now, day, 1);
};
/** @internal */
export const findNextMonth = (now, day, months) => {
const d = new Date(now);
const tmp1 = new Date(d.setDate(day));
const tmp2 = new Date(tmp1.setMonth(tmp1.getMonth() + months));
if (tmp2.getDate() === day) {
const d2 = new Date(now);
const tmp3 = new Date(d2.setDate(day));
return tmp3.setMonth(tmp3.getMonth() + months);
}
return findNextMonth(now, day, months + 1);
};
// circular with Effect
const ScheduleDefectTypeId = /*#__PURE__*/Symbol.for("effect/Schedule/ScheduleDefect");
class ScheduleDefect {
error;
[ScheduleDefectTypeId];
constructor(error) {
this.error = error;
this[ScheduleDefectTypeId] = ScheduleDefectTypeId;
}
}
const isScheduleDefect = u => hasProperty(u, ScheduleDefectTypeId);
const scheduleDefectWrap = self => core.catchAll(self, e => core.die(new ScheduleDefect(e)));
/** @internal */
export const scheduleDefectRefailCause = cause => Option.match(internalCause.find(cause, _ => internalCause.isDieType(_) && isScheduleDefect(_.defect) ? Option.some(_.defect) : Option.none()), {
onNone: () => cause,
onSome: error => internalCause.fail(error.error)
});
/** @internal */
export const scheduleDefectRefail = effect => core.catchAllCause(effect, cause => core.failCause(scheduleDefectRefailCause(cause)));
/** @internal */
export const repeat_Effect = /*#__PURE__*/dual(2, (self, schedule) => repeatOrElse_Effect(self, schedule, (e, _) => core.fail(e)));
/** @internal */
export const repeat_combined = /*#__PURE__*/dual(2, (self, options) => {
if (isSchedule(options)) {
return repeat_Effect(self, options);
}
const base = options.schedule ?? passthrough(forever);
const withWhile = options.while ? whileInputEffect(base, a => {
const applied = options.while(a);
if (typeof applied === "boolean") {
return core.succeed(applied);
}
return scheduleDefectWrap(applied);
}) : base;
const withUntil = options.until ? untilInputEffect(withWhile, a => {
const applied = options.until(a);
if (typeof applied === "boolean") {
return core.succeed(applied);
}
return scheduleDefectWrap(applied);
}) : withWhile;
const withTimes = options.times ? intersect(withUntil, recurs(options.times)).pipe(map(intersectionPair => intersectionPair[0])) : withUntil;
return scheduleDefectRefail(repeat_Effect(self, withTimes));
});
/** @internal */
export const repeatOrElse_Effect = /*#__PURE__*/dual(3, (self, schedule, orElse) => core.flatMap(driver(schedule), driver => core.matchEffect(self, {
onFailure: error => orElse(error, Option.none()),
onSuccess: value => repeatOrElseEffectLoop(effect.provideServiceEffect(self, CurrentIterationMetadata, ref.get(driver.iterationMeta)), driver, (error, option) => effect.provideServiceEffect(orElse(error, option), CurrentIterationMetadata, ref.get(driver.iterationMeta)), value)
})));
/** @internal */
const repeatOrElseEffectLoop = (self, driver, orElse, value) => core.matchEffect(driver.next(value), {
onFailure: () => core.orDie(driver.last),
onSuccess: b => core.matchEffect(self, {
onFailure: error => orElse(error, Option.some(b)),
onSuccess: value => repeatOrElseEffectLoop(self, driver, orElse, value)
})
});
/** @internal */
export const retry_Effect = /*#__PURE__*/dual(2, (self, policy) => retryOrElse_Effect(self, policy, (e, _) => core.fail(e)));
/** @internal */
export const retry_combined = /*#__PURE__*/dual(2, (self, options) => {
if (isSchedule(options)) {
return retry_Effect(self, options);
}
return scheduleDefectRefail(retry_Effect(self, fromRetryOptions(options)));
});
/** @internal */
export const fromRetryOptions = options => {
const base = options.schedule ?? forever;
const withWhile = options.while ? whileInputEffect(base, e => {
const applied = options.while(e);
if (typeof applied === "boolean") {
return core.succeed(applied);
}
return scheduleDefectWrap(applied);
}) : base;
const withUntil = options.until ? untilInputEffect(withWhile, e => {
const applied = options.until(e);
if (typeof applied === "boolean") {
return core.succeed(applied);
}
return scheduleDefectWrap(applied);
}) : withWhile;
return options.times ? intersect(withUntil, recurs(options.times)) : withUntil;
};
/** @internal */
export const retryOrElse_Effect = /*#__PURE__*/dual(3, (self, policy, orElse) => core.flatMap(driver(policy), driver => retryOrElse_EffectLoop(effect.provideServiceEffect(self, CurrentIterationMetadata, ref.get(driver.iterationMeta)), driver, (e, out) => effect.provideServiceEffect(orElse(e, out), CurrentIterationMetadata, ref.get(driver.iterationMeta)))));
/** @internal */
const retryOrElse_EffectLoop = (self, driver, orElse) => {
return core.catchAll(self, e => core.matchEffect(driver.next(e), {
onFailure: () => pipe(driver.last, core.orDie, core.flatMap(out => orElse(e, out))),
onSuccess: () => retryOrElse_EffectLoop(self, driver, orElse)
}));
};
/** @internal */
export const schedule_Effect = /*#__PURE__*/dual(2, (self, schedule) => scheduleFrom_Effect(self, void 0, schedule));
/** @internal */
export const scheduleFrom_Effect = /*#__PURE__*/dual(3, (self, initial, schedule) => core.flatMap(driver(schedule), driver => scheduleFrom_EffectLoop(effect.provideServiceEffect(self, CurrentIterationMetadata, ref.get(driver.iterationMeta)), initial, driver)));
/** @internal */
const scheduleFrom_EffectLoop = (self, initial, driver) => core.matchEffect(driver.next(initial), {
onFailure: () => core.orDie(driver.last),
onSuccess: () => core.flatMap(self, a => scheduleFrom_EffectLoop(self, a, driver))
});
/** @internal */
export const count = /*#__PURE__*/unfold(0, n => n + 1);
/** @internal */
export const elapsed = /*#__PURE__*/makeWithState(/*#__PURE__*/Option.none(), (now, _, state) => {
switch (state._tag) {
case "None":
{
return core.succeed([Option.some(now), Duration.zero, ScheduleDecision.continueWith(Interval.after(now))]);
}
case "Some":
{
return core.succeed([Option.some(state.value), Duration.millis(now - state.value), ScheduleDecision.continueWith(Interval.after(now))]);
}
}
});
/** @internal */
export const forever = /*#__PURE__*/unfold(0, n => n + 1);
/** @internal */
export const once = /*#__PURE__*/asVoid(/*#__PURE__*/recurs(1));
/** @internal */
export const stop = /*#__PURE__*/asVoid(/*#__PURE__*/recurs(0));
/** @internal */
export const scheduleForked = /*#__PURE__*/dual(2, (self, schedule) => forkScoped(schedule_Effect(self, schedule)));
//# sourceMappingURL=schedule.js.map