ES
<?phpnamespace App\Model;use JumPHP\Async\Task;/** * Steps one after the other, each a fn (mixed $previous): Task|mixed; the first failure stops them. Task of the last result. * Chain::run([fn () => $c->run(... 'VM.clone' ...), fn (string $vm) => $c->run(... 'VM.provision', [$vm] ...), ...]) */final class Chain{    /** @param list<\Closure> $steps */    public static function run(array $steps, mixed $value = null): Task    {        $done = new Task();        $next = function (mixed $value, int $i) use (&$next, $steps, $done): void {            if (!isset($steps[$i])) {                $done->settle($value);                return;            }            try {                $out = $steps[$i]($value);            } catch (\Throwable $e) {                $done->settle(null, $e);                return;            }            if ($out instanceof Task) {                $out->then(fn ($r) => $next($r ?? $value, $i + 1), fn (\Throwable $e) => $done->settle(null, $e));            } else {                $next($out ?? $value, $i + 1);            }        };        $next($value, 0);        return $done;    }    /** A Task that gives $value when $task succeeds (a step whose own result is not what the next step needs). */    public static function keep(Task $task, mixed $value): Task    {        $out = new Task();        $task->then(fn () => $out->settle($value), fn (\Throwable $e) => $out->settle(null, $e));        return $out;    }}