[{"name":"Again.Decision","comment":" Describe whether to retry an action and an optional minimum wait.\n\n@docs Decision, fromBool, shouldRetry, retryDelay\n\n","unions":[{"name":"Decision","comment":" Describes a decision made to retry an action.\n\n`Stop` requests no further attempt.\n\n`Retry` requests another attempt with an unspecified delay.\n\n`RetryAfter` requests another attempt after at least the given number of\nmilliseconds. The delay must be finite and nonnegative.\n\n","args":[],"cases":[["Stop",[]],["Retry",[]],["RetryAfter",["Basics.Float"]]]}],"aliases":[],"values":[{"name":"fromBool","comment":" Converts `True` to `Retry` and `False` to `Stop`.\n","type":"Basics.Bool -> Again.Decision.Decision"},{"name":"retryDelay","comment":" Calculates the next delay in milliseconds. Count attempts already made,\nstarting at one. Returns `Nothing` when another attempt is not permitted.\n","type":"Again.Decision.Decision -> Again.Policy.Policy -> Basics.Int -> Maybe.Maybe Basics.Float"},{"name":"shouldRetry","comment":" Whether the decision requests another attempt.\n","type":"Again.Decision.Decision -> Basics.Bool"}],"binops":[]},{"name":"Again.Http","comment":" Helpers for HTTP tasks and retry decisions.\n\n@docs isRetryable, resolve\n\n","unions":[],"aliases":[],"values":[{"name":"isRetryable","comment":" Accepts timeouts, network errors, and status codes 408, 429, 500, 502, 503,\nand 504. Other errors are rejected.\n\nAssumes the operation is safe to repeat: a timeout does not mean a write failed.\nDoes not account for `Retry-After`, since `Http.Error` does not retain headers.\n\n","type":"Http.Error -> Basics.Bool"},{"name":"resolve","comment":" Keep the body of a successful response, or turn a failed response into an\n`Http.Error`. Use with `Http.stringResolver` or `Http.bytesResolver`.\n","type":"Http.Response body -> Result.Result Http.Error body"}],"binops":[]},{"name":"Again.Policy","comment":" Choose a retry schedule and how many attempts to allow.\n\n@docs Policy, AttemptLimit, unlimited, attemptsLeft, allowsRetry, retryDelay\n\n","unions":[{"name":"AttemptLimit","comment":" `Unlimited` keeps offering retries.\n\n`MaxAttempts` counts the initial operation as an attempt, so `MaxAttempts 3`\nallows two retries. A limit of one or less allows no retries.\n\n","args":[],"cases":[["Unlimited",[]],["MaxAttempts",["Basics.Int"]]]}],"aliases":[{"name":"Policy","comment":" A retry schedule and an attempt limit.\n","args":[],"type":"{ schedule : Again.Schedule.Schedule, limit : Again.Policy.AttemptLimit }"}],"values":[{"name":"allowsRetry","comment":" Whether another attempt is allowed. The count includes the initial operation\nand any retries already made.\n","type":"Again.Policy.Policy -> Basics.Int -> Basics.Bool"},{"name":"attemptsLeft","comment":" Remaining attempts after the supplied count, or `Nothing` for an unlimited policy.\nExhausted limits return `Just 0`.\n","type":"Again.Policy.Policy -> Basics.Int -> Maybe.Maybe Basics.Int"},{"name":"retryDelay","comment":" Delay in milliseconds before another attempt, or `Nothing` when exhausted.\nCount attempts already made, starting at one for the initial operation.\n","type":"Again.Policy.Policy -> Basics.Int -> Maybe.Maybe Basics.Float"},{"name":"unlimited","comment":" Uses a schedule with no attempt limit.\n","type":"Again.Schedule.Schedule -> Again.Policy.Policy"}],"binops":[]},{"name":"Again.Remote","comment":" Keep track of a remote value and retry it when things go wrong.\n\nSuppose your operation reports a `Result` through a message:\n\n    attempt : (Result String String -> msg) -> Cmd msg\n\nYou can track it in your model and schedule retries from `update`:\n\n    import Again.Policy as Policy\n    import Again.Remote as Remote\n    import Again.Schedule exposing (Schedule(..))\n    import Process\n    import Task\n\n    type alias Model =\n        Remote.Remote String String\n\n    type Msg\n        = Received (Result String String)\n        | Retry\n\n    init : () -> ( Model, Cmd Msg )\n    init _ =\n        ( Remote.init (Policy.unlimited (Periodic { delay = 1000 }))\n        , attempt Received\n        )\n\n    update : Msg -> Model -> ( Model, Cmd Msg )\n    update msg model =\n        case msg of\n            Received (Ok value) ->\n                ( Remote.succeed value model, Cmd.none )\n\n            Received (Err error) ->\n                let\n                    ( next, delay ) =\n                        Remote.fail error model\n                in\n                ( next\n                , case delay of\n                    Just milliseconds ->\n                        Process.sleep milliseconds\n                            |> Task.perform (always Retry)\n\n                    Nothing ->\n                        Cmd.none\n                )\n\n            Retry ->\n                ( Remote.beginRetry model, attempt Received )\n\nA successful value can fail later. For example, a connection may close after it\nhas opened.\n\n@docs Remote, State, RetryContext\n@docs init, withRetryable, state, stateToString, result, get\n@docs beginRetry, succeed, fail\n\n","unions":[{"name":"Remote","comment":" A remote value together with its retry policy and error classifier.\n","args":["error","value"],"cases":[]},{"name":"State","comment":" See whether your value is available, still being attempted, or has failed.\n","args":["error","value"],"cases":[["Attempting",[]],["WaitingForRetry",["Again.Remote.RetryContext error"]],["Retrying",["Again.Remote.RetryContext error"]],["Successful",["value"]],["Failed",["error"]]]}],"aliases":[{"name":"RetryContext","comment":" The number of completed attempts and the latest error in a retry sequence.\n","args":["error"],"type":"{ attempts : Basics.Int, lastError : error }"}],"values":[{"name":"beginRetry","comment":" Tell the `Remote` value that the wait time has elapsed. It will assume you\nare actually retrying afterwards.\n\nThis keeps your retry context, if any. Otherwise you start a fresh attempt,\nso any previous value or error is discarded.\n\n","type":"Again.Remote.Remote error value -> Again.Remote.Remote error value"},{"name":"fail","comment":" Tell the `Remote` that an attempt failed, or that a successful value was lost.\nThe stored classifier and policy decide whether to retry.\n\nYou get a retry delay in milliseconds, or `Nothing` to give up. A failure after\nsuccess discards the value and starts counting failures from one again.\n\n","type":"error -> Again.Remote.Remote error value -> ( Again.Remote.Remote error value, Maybe.Maybe Basics.Float )"},{"name":"get","comment":" Get the value if it is currently successful.\n","type":"Again.Remote.Remote error value -> Maybe.Maybe value"},{"name":"init","comment":" Start tracking a value with its first attempt already under way. By default,\nall failures are retryable, subject to your policy.\n","type":"Again.Policy.Policy -> Again.Remote.Remote error value"},{"name":"result","comment":" Get the successful value or the error that ended the retry sequence.\nYou get `Nothing` while an attempt or retry is pending.\n","type":"Again.Remote.Remote error value -> Maybe.Maybe (Result.Result error value)"},{"name":"state","comment":" Look at the current state of your remote value.\n","type":"Again.Remote.Remote error value -> Again.Remote.State error value"},{"name":"stateToString","comment":" Describe your remote value with a readable label, such as\n`\"waiting to retry (attempt 2/3)\"`.\n","type":"Again.Remote.Remote error value -> String.String"},{"name":"succeed","comment":" Tell the `Remote` that an attempt succeeded. This keeps the value and clears\nits previous failures and attempt count.\n","type":"value -> Again.Remote.Remote error value -> Again.Remote.Remote error value"},{"name":"withRetryable","comment":" Choose which errors should be retried. Changing the classifier leaves your\ncurrent value and retry progress alone.\n","type":"(error -> Again.Decision.Decision) -> Again.Remote.Remote error value -> Again.Remote.Remote error value"}],"binops":[]},{"name":"Again.Remote.Cmd","comment":" A helper module for using `Again.Remote` in your `update` function.\n\n@docs fail, wakeAfter\n\n","unions":[],"aliases":[],"values":[{"name":"fail","comment":" Tell the `Remote` that an attempt failed and schedule your wakeup message\nfor the next retry. You get `Cmd.none` when no retry is allowed.\n","type":"msg -> error -> Again.Remote.Remote error value -> ( Again.Remote.Remote error value, Platform.Cmd.Cmd msg )"},{"name":"wakeAfter","comment":" Send a message after the given delay in milliseconds. With `Nothing`, there\nis no message to schedule.\n","type":"msg -> Maybe.Maybe Basics.Float -> Platform.Cmd.Cmd msg"}],"binops":[]},{"name":"Again.Schedule","comment":" Choose how long to wait between retries.\n\n    import Again.Schedule as Schedule exposing (Schedule(..))\n\n    schedule =\n        ImmediatelyThen\n            (ExponentialBackoff\n                { initialDelay = 1000\n                , multiplier = 2\n                , maxDelay = 4000\n                }\n            )\n\n    delays =\n        List.map (Schedule.delay schedule) [ 0, 1, 2, 3, 4 ]\n\nThis produces `[ 0, 1000, 2000, 4000, 4000 ]` milliseconds: retry immediately,\nthen back off up to four seconds.\n\n@docs Schedule, immediately, delay\n\n","unions":[{"name":"Schedule","comment":" Choose how long to wait before each retry. All delays are in milliseconds.\n\n`Periodic` uses the same `delay` for every retry.\n\n`ExponentialBackoff` starts with `initialDelay`, then multiplies the delay by\n`multiplier` for each subsequent retry, up to `maxDelay`.\n\n`ImmediatelyThen` retries once without waiting, then follows the wrapped schedule\nfrom its first delay. Wrap it around either of the other variants, or nest it\nfor several immediate retries.\n\nChoose a `multiplier` of one or more and delays of zero or more, with\n`maxDelay` at least as large as `initialDelay`. The schedule uses your values\nas given, so make sure they're finite numbers.\n\n","args":[],"cases":[["Periodic",["{ delay : Basics.Float }"]],["ExponentialBackoff",["{ initialDelay : Basics.Float, multiplier : Basics.Float, maxDelay : Basics.Float }"]],["ImmediatelyThen",["Again.Schedule.Schedule"]]]}],"aliases":[],"values":[{"name":"delay","comment":" Returns a delay for a nonnegative, zero-based retry index.\n","type":"Again.Schedule.Schedule -> Basics.Int -> Basics.Float"},{"name":"immediately","comment":" Retry without waiting, every time. Equivalent to `Periodic { delay = 0 }`.\n","type":"Again.Schedule.Schedule"}],"binops":[]},{"name":"Again.Task","comment":" Run tasks with a retry policy. The first attempt runs immediately; the\nschedule controls the waits between subsequent attempts.\n\nRetry a GET up to three total attempts on timeouts or network errors:\n\n    import Again.Http\n    import Again.Policy as Policy\n    import Again.Schedule exposing (Schedule(..))\n    import Again.Task exposing (retryIf)\n    import Http\n    import Task\n\n    type Msg\n        = ReceivedResponse (Result Http.Error String)\n\n    loadMessage : Cmd Msg\n    loadMessage =\n        Http.task\n            { method = \"GET\"\n            , headers = []\n            , url = \"/message.txt\"\n            , body = Http.emptyBody\n            , resolver = Http.stringResolver Again.Http.resolve\n            , timeout = Just 5000\n            }\n            |> retryIf isTransient\n                { schedule = Periodic { delay = 1000 }\n                , limit = Policy.MaxAttempts 3\n                }\n            |> Task.attempt ReceivedResponse\n\n    isTransient : Http.Error -> Bool\n    isTransient error =\n        error == Http.Timeout || error == Http.NetworkError\n\n@docs retry, retryIf, retryWith\n\n","unions":[],"aliases":[],"values":[{"name":"retry","comment":" Retries any failure until the task succeeds or the attempt limit is reached.\n","type":"Again.Policy.Policy -> Task.Task error value -> Task.Task error value"},{"name":"retryIf","comment":" Retries only errors accepted by the predicate. Returns the last error when\nit is rejected or the attempt limit is reached.\n\nFor HTTP tasks, [Again.Http.isRetryable](Again-Http#isRetryable) supplies a default predicate.\n\n","type":"(error -> Basics.Bool) -> Again.Policy.Policy -> Task.Task error value -> Task.Task error value"},{"name":"retryWith","comment":" Classifies each error with a [Decision](Again-Decision#Decision).\n`RetryAfter` waits for the greater of the requested delay and the policy's delay.\nReturns the last error when stopped or when the attempt limit is reached.\n","type":"(error -> Again.Decision.Decision) -> Again.Policy.Policy -> Task.Task error value -> Task.Task error value"}],"binops":[]}]