src/voauth.erl

-module(voauth).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/voauth.gleam").
-export([config/1, with_on_refresh/2, with_call_timeout_ms/2, with_init_timeout_ms/2, with_refresh_at_percent/2, with_min_refresh_delay_ms/2, with_retry_backoff_ms/2, token_decoder/0, refresh_response_decoder/0, merge_response/2, get_token/1, refresh_now/1, set_token/2, start/1, supervised/1]).
-export_type([vault/0, refresh_error/0, vault_error/0, token/0, refresh_response/0, config/0, message/0, state/0]).

-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.

?MODULEDOC(
    " OAuth2 access-token vault with proactive refresh and bounded retry.\n"
    "\n"
    " The vault holds the current access token, refreshes it before it\n"
    " expires, retries transient failures, and gives up with a typed\n"
    " error when the refresh token has been revoked. It does not perform\n"
    " the OAuth flow itself; you provide a `Refresh` callback that talks\n"
    " to your provider's token endpoint.\n"
    "\n"
    " One vault per service. To handle multiple providers, start one\n"
    " `Vault` per provider and supervise them with your application's\n"
    " own supervisor.\n"
    "\n"
    " A vault always starts without a token. Install one with `set_token`\n"
    " after the user completes the OAuth flow, or after rehydrating from\n"
    " durable storage at boot.\n"
    "\n"
    " See the README for a quickstart.\n"
).

-opaque vault() :: {vault, gleam@erlang@process:subject(message()), integer()}.

-type refresh_error() :: {refresh_retryable, binary()} |
    {refresh_unauthorized, binary()}.

-type vault_error() :: {refresh_failed, refresh_error()} |
    no_refresh_token |
    {start_error, binary()}.

-type token() :: {token,
        binary(),
        integer(),
        gleam@option:option(binary()),
        binary(),
        binary()}.

-type refresh_response() :: {refresh_response,
        binary(),
        gleam@option:option(integer()),
        gleam@option:option(binary()),
        gleam@option:option(binary()),
        gleam@option:option(binary())}.

-opaque config() :: {config,
        fun((binary()) -> {ok, refresh_response()} | {error, refresh_error()}),
        gleam@option:option(fun((token()) -> {ok, nil} | {error, binary()})),
        integer(),
        integer(),
        integer(),
        integer(),
        list(integer())}.

-type message() :: {get_token,
        gleam@erlang@process:subject({ok, binary()} | {error, vault_error()})} |
    {refresh_now,
        gleam@erlang@process:subject({ok, nil} | {error, vault_error()})} |
    {scheduled_refresh, integer()} |
    {set_token, gleam@erlang@process:subject(nil), token()}.

-type state() :: {state,
        config(),
        gleam@option:option(token()),
        integer(),
        integer(),
        gleam@option:option(gleam@erlang@process:timer()),
        gleam@erlang@process:subject(message())}.

-file("src/voauth.gleam", 137).
?DOC(
    " Build a `Config` with defaults. The provider-specific `Refresh`\n"
    " callback is the only required argument.\n"
    "\n"
    " Defaults:\n"
    " - `on_refresh`: `None`\n"
    " - `call_timeout_ms`: 30_000 — must exceed worst-case `Refresh`\n"
    "   HTTP latency.\n"
    " - `init_timeout_ms`: 1_000\n"
    " - `refresh_at_percent`: 80 — proactive refresh at 80% of `expires_in`.\n"
    " - `min_refresh_delay_ms`: 60_000 — floor on the proactive delay.\n"
    " - `retry_backoff_ms`: `[30_000, 60_000, 120_000]` (30s/1m/2m).\n"
).
-spec config(
    fun((binary()) -> {ok, refresh_response()} | {error, refresh_error()})
) -> config().
config(Refresh) ->
    {config, Refresh, none, 30000, 1000, 80, 60000, [30000, 60000, 120000]}.

-file("src/voauth.gleam", 151).
?DOC(
    " Persist tokens after every successful refresh. Runs inside the\n"
    " vault's mailbox; keep it fast.\n"
).
-spec with_on_refresh(config(), fun((token()) -> {ok, nil} | {error, binary()})) -> config().
with_on_refresh(Config, Callback) ->
    {config,
        erlang:element(2, Config),
        {some, Callback},
        erlang:element(4, Config),
        erlang:element(5, Config),
        erlang:element(6, Config),
        erlang:element(7, Config),
        erlang:element(8, Config)}.

-file("src/voauth.gleam", 158).
?DOC(
    " Timeout (ms) for synchronous calls into the vault. Must exceed\n"
    " worst-case `Refresh` HTTP latency, because `get_token` blocks on\n"
    " a refresh when the cached token has expired.\n"
).
-spec with_call_timeout_ms(config(), integer()) -> config().
with_call_timeout_ms(Config, Ms) ->
    {config,
        erlang:element(2, Config),
        erlang:element(3, Config),
        Ms,
        erlang:element(5, Config),
        erlang:element(6, Config),
        erlang:element(7, Config),
        erlang:element(8, Config)}.

-file("src/voauth.gleam", 163).
?DOC(" Timeout (ms) for the actor's initialise phase.\n").
-spec with_init_timeout_ms(config(), integer()) -> config().
with_init_timeout_ms(Config, Ms) ->
    {config,
        erlang:element(2, Config),
        erlang:element(3, Config),
        erlang:element(4, Config),
        Ms,
        erlang:element(6, Config),
        erlang:element(7, Config),
        erlang:element(8, Config)}.

-file("src/voauth.gleam", 168).
?DOC(" Proactive refresh fires at this percent of `expires_in`. Default 80.\n").
-spec with_refresh_at_percent(config(), integer()) -> config().
with_refresh_at_percent(Config, Percent) ->
    {config,
        erlang:element(2, Config),
        erlang:element(3, Config),
        erlang:element(4, Config),
        erlang:element(5, Config),
        Percent,
        erlang:element(7, Config),
        erlang:element(8, Config)}.

-file("src/voauth.gleam", 174).
?DOC(
    " Floor (ms) on the proactive delay. Guards against providers that\n"
    " hand out very short `expires_in` values.\n"
).
-spec with_min_refresh_delay_ms(config(), integer()) -> config().
with_min_refresh_delay_ms(Config, Ms) ->
    {config,
        erlang:element(2, Config),
        erlang:element(3, Config),
        erlang:element(4, Config),
        erlang:element(5, Config),
        erlang:element(6, Config),
        Ms,
        erlang:element(8, Config)}.

-file("src/voauth.gleam", 180).
?DOC(
    " Backoff schedule for retrying a failed scheduled refresh, indexed\n"
    " by failed-attempt number. `[]` disables retries.\n"
).
-spec with_retry_backoff_ms(config(), list(integer())) -> config().
with_retry_backoff_ms(Config, Schedule) ->
    {config,
        erlang:element(2, Config),
        erlang:element(3, Config),
        erlang:element(4, Config),
        erlang:element(5, Config),
        erlang:element(6, Config),
        erlang:element(7, Config),
        Schedule}.

-file("src/voauth.gleam", 189).
?DOC(" Decoder for an OAuth2 token JSON object.\n").
-spec token_decoder() -> gleam@dynamic@decode:decoder(token()).
token_decoder() ->
    gleam@dynamic@decode:field(
        <<"access_token"/utf8>>,
        {decoder, fun gleam@dynamic@decode:decode_string/1},
        fun(Access_token) ->
            gleam@dynamic@decode:field(
                <<"expires_in"/utf8>>,
                {decoder, fun gleam@dynamic@decode:decode_int/1},
                fun(Expires_in) ->
                    gleam@dynamic@decode:field(
                        <<"refresh_token"/utf8>>,
                        gleam@dynamic@decode:optional(
                            {decoder, fun gleam@dynamic@decode:decode_string/1}
                        ),
                        fun(Refresh_token) ->
                            gleam@dynamic@decode:field(
                                <<"scope"/utf8>>,
                                {decoder,
                                    fun gleam@dynamic@decode:decode_string/1},
                                fun(Scope) ->
                                    gleam@dynamic@decode:field(
                                        <<"token_type"/utf8>>,
                                        {decoder,
                                            fun gleam@dynamic@decode:decode_string/1},
                                        fun(Token_type) ->
                                            gleam@dynamic@decode:success(
                                                {token,
                                                    Access_token,
                                                    Expires_in,
                                                    Refresh_token,
                                                    Scope,
                                                    Token_type}
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/voauth.gleam", 209).
?DOC(
    " Decoder for a refresh response. Fields other than `access_token`\n"
    " are optional per RFC 6749 §6 and decode to `None` when absent.\n"
).
-spec refresh_response_decoder() -> gleam@dynamic@decode:decoder(refresh_response()).
refresh_response_decoder() ->
    gleam@dynamic@decode:field(
        <<"access_token"/utf8>>,
        {decoder, fun gleam@dynamic@decode:decode_string/1},
        fun(Access_token) ->
            gleam@dynamic@decode:optional_field(
                <<"expires_in"/utf8>>,
                none,
                gleam@dynamic@decode:optional(
                    {decoder, fun gleam@dynamic@decode:decode_int/1}
                ),
                fun(Expires_in) ->
                    gleam@dynamic@decode:optional_field(
                        <<"refresh_token"/utf8>>,
                        none,
                        gleam@dynamic@decode:optional(
                            {decoder, fun gleam@dynamic@decode:decode_string/1}
                        ),
                        fun(Refresh_token) ->
                            gleam@dynamic@decode:optional_field(
                                <<"scope"/utf8>>,
                                none,
                                gleam@dynamic@decode:optional(
                                    {decoder,
                                        fun gleam@dynamic@decode:decode_string/1}
                                ),
                                fun(Scope) ->
                                    gleam@dynamic@decode:optional_field(
                                        <<"token_type"/utf8>>,
                                        none,
                                        gleam@dynamic@decode:optional(
                                            {decoder,
                                                fun gleam@dynamic@decode:decode_string/1}
                                        ),
                                        fun(Token_type) ->
                                            gleam@dynamic@decode:success(
                                                {refresh_response,
                                                    Access_token,
                                                    Expires_in,
                                                    Refresh_token,
                                                    Scope,
                                                    Token_type}
                                            )
                                        end
                                    )
                                end
                            )
                        end
                    )
                end
            )
        end
    ).

-file("src/voauth.gleam", 242).
?DOC(
    " Merge a refresh response onto the previous token, carrying\n"
    " forward any field the server omitted.\n"
).
-spec merge_response(token(), refresh_response()) -> token().
merge_response(Previous, Resp) ->
    {token,
        erlang:element(2, Resp),
        gleam@option:unwrap(
            erlang:element(3, Resp),
            erlang:element(3, Previous)
        ),
        gleam@option:'or'(erlang:element(4, Resp), erlang:element(4, Previous)),
        gleam@option:unwrap(
            erlang:element(5, Resp),
            erlang:element(5, Previous)
        ),
        gleam@option:unwrap(
            erlang:element(6, Resp),
            erlang:element(6, Previous)
        )}.

-file("src/voauth.gleam", 276).
?DOC(
    " Return the current valid access token. Refreshes synchronously if\n"
    " the cached token has expired. Returns `NoRefreshToken` if no token\n"
    " has been installed yet.\n"
).
-spec get_token(vault()) -> {ok, binary()} | {error, vault_error()}.
get_token(Vault) ->
    gleam@erlang@process:call(
        erlang:element(2, Vault),
        erlang:element(3, Vault),
        fun(Field@0) -> {get_token, Field@0} end
    ).

-file("src/voauth.gleam", 282).
?DOC(
    " Force a refresh now, regardless of remaining validity. Returns\n"
    " `NoRefreshToken` if no token has been installed yet.\n"
).
-spec refresh_now(vault()) -> {ok, nil} | {error, vault_error()}.
refresh_now(Vault) ->
    gleam@erlang@process:call(
        erlang:element(2, Vault),
        erlang:element(3, Vault),
        fun(Field@0) -> {refresh_now, Field@0} end
    ).

-file("src/voauth.gleam", 289).
?DOC(
    " Install or replace the vault's token. Schedules a fresh proactive\n"
    " refresh. Use it after the OAuth flow completes, or after a\n"
    " re-authorisation hands you a new token.\n"
).
-spec set_token(vault(), token()) -> nil.
set_token(Vault, Token) ->
    gleam@erlang@process:call(
        erlang:element(2, Vault),
        erlang:element(3, Vault),
        fun(Reply) -> {set_token, Reply, Token} end
    ).

-file("src/voauth.gleam", 511).
-spec run_on_refresh(config(), token()) -> {ok, nil} | {error, binary()}.
run_on_refresh(Config, Token) ->
    case erlang:element(3, Config) of
        none ->
            {ok, nil};

        {some, Callback} ->
            Callback(Token)
    end.

-file("src/voauth.gleam", 518).
-spec describe_error(vault_error()) -> binary().
describe_error(Err) ->
    case Err of
        {refresh_failed, {refresh_retryable, Reason}} ->
            <<"retryable: "/utf8, Reason/binary>>;

        {refresh_failed, {refresh_unauthorized, Reason@1}} ->
            <<"unauthorized: "/utf8, Reason@1/binary>>;

        no_refresh_token ->
            <<"no refresh token"/utf8>>;

        {start_error, Reason@2} ->
            <<"vault start error: "/utf8, Reason@2/binary>>
    end.

-file("src/voauth.gleam", 533).
?DOC(
    " Look up the backoff delay for the given failed-attempt number.\n"
    " Returns `None` if the schedule is exhausted (caller should give up).\n"
).
-spec retry_backoff_at(list(integer()), integer()) -> gleam@option:option(integer()).
retry_backoff_at(Schedule, Failed_attempt) ->
    _pipe = Schedule,
    _pipe@1 = gleam@list:drop(_pipe, Failed_attempt),
    _pipe@2 = gleam@list:first(_pipe@1),
    gleam@option:from_result(_pipe@2).

-file("src/voauth.gleam", 540).
-spec schedule_proactive(
    gleam@erlang@process:subject(message()),
    config(),
    integer()
) -> gleam@erlang@process:timer().
schedule_proactive(Self, Config, Expires_in_seconds) ->
    Delay_ms = ((Expires_in_seconds * 1000) * erlang:element(6, Config)) div 100,
    Delay_ms@1 = gleam@int:max(Delay_ms, erlang:element(7, Config)),
    gleam@erlang@process:send_after(Self, Delay_ms@1, {scheduled_refresh, 0}).

-file("src/voauth.gleam", 552).
?DOC(
    " Schedule the next retry attempt after a failed scheduled refresh.\n"
    " Returns `None` if the backoff schedule is exhausted.\n"
).
-spec schedule_retry(
    gleam@erlang@process:subject(message()),
    list(integer()),
    integer()
) -> gleam@option:option(gleam@erlang@process:timer()).
schedule_retry(Self, Schedule, Failed_attempt) ->
    case retry_backoff_at(Schedule, Failed_attempt) of
        {some, Delay} ->
            {some,
                gleam@erlang@process:send_after(
                    Self,
                    Delay,
                    {scheduled_refresh, Failed_attempt + 1}
                )};

        none ->
            none
    end.

-file("src/voauth.gleam", 436).
-spec handle_refresh_failure(integer(), vault_error(), state()) -> gleam@otp@actor:next(state(), message()).
handle_refresh_failure(Attempt, Err, State) ->
    case Err of
        {refresh_failed, {refresh_unauthorized, _}} ->
            logging:log(
                warning,
                <<"voauth: refresh giving up (not retryable): "/utf8,
                    (describe_error(Err))/binary>>
            ),
            gleam@otp@actor:continue(State);

        no_refresh_token ->
            logging:log(
                warning,
                <<"voauth: refresh giving up (not retryable): "/utf8,
                    (describe_error(Err))/binary>>
            ),
            gleam@otp@actor:continue(State);

        {start_error, _} ->
            logging:log(
                warning,
                <<"voauth: refresh giving up (not retryable): "/utf8,
                    (describe_error(Err))/binary>>
            ),
            gleam@otp@actor:continue(State);

        {refresh_failed, {refresh_retryable, _}} ->
            case schedule_retry(
                erlang:element(7, State),
                erlang:element(8, erlang:element(2, State)),
                Attempt
            ) of
                {some, Timer} ->
                    gleam@otp@actor:continue(
                        {state,
                            erlang:element(2, State),
                            erlang:element(3, State),
                            erlang:element(4, State),
                            erlang:element(5, State),
                            {some, Timer},
                            erlang:element(7, State)}
                    );

                none ->
                    logging:log(
                        warning,
                        <<<<<<"voauth: refresh giving up after "/utf8,
                                    (erlang:integer_to_binary(Attempt + 1))/binary>>/binary,
                                " attempts: "/utf8>>/binary,
                            (describe_error(Err))/binary>>
                    ),
                    gleam@otp@actor:continue(State)
            end
    end.

-file("src/voauth.gleam", 564).
-spec cancel_timer_in_state(state()) -> state().
cancel_timer_in_state(State) ->
    case erlang:element(6, State) of
        {some, Timer} ->
            gleam@erlang@process:cancel_timer(Timer),
            {state,
                erlang:element(2, State),
                erlang:element(3, State),
                erlang:element(4, State),
                erlang:element(5, State),
                none,
                erlang:element(7, State)};

        none ->
            State
    end.

-file("src/voauth.gleam", 417).
-spec handle_set_token(gleam@erlang@process:subject(nil), token(), state()) -> gleam@otp@actor:next(state(), message()).
handle_set_token(Reply, Token, State) ->
    State@1 = cancel_timer_in_state(State),
    Timer = schedule_proactive(
        erlang:element(7, State@1),
        erlang:element(2, State@1),
        erlang:element(3, Token)
    ),
    New_state = {state,
        erlang:element(2, State@1),
        {some, Token},
        voauth_ffi:monotonic_ms(),
        erlang:element(3, Token) * 1000,
        {some, Timer},
        erlang:element(7, State@1)},
    gleam@erlang@process:send(Reply, nil),
    gleam@otp@actor:continue(New_state).

-file("src/voauth.gleam", 470).
-spec attempt_refresh(state()) -> {state(), {ok, nil} | {error, vault_error()}}.
attempt_refresh(State) ->
    State@1 = cancel_timer_in_state(State),
    case erlang:element(3, State@1) of
        {some, Current_token} ->
            case erlang:element(4, Current_token) of
                {some, Refresh_token} ->
                    case (erlang:element(2, erlang:element(2, State@1)))(
                        Refresh_token
                    ) of
                        {ok, Resp} ->
                            Token = merge_response(Current_token, Resp),
                            case run_on_refresh(
                                erlang:element(2, State@1),
                                Token
                            ) of
                                {ok, nil} ->
                                    nil;

                                {error, Reason} ->
                                    logging:log(
                                        error,
                                        <<"voauth: on_refresh callback failed: "/utf8,
                                            Reason/binary>>
                                    )
                            end,
                            Timer = schedule_proactive(
                                erlang:element(7, State@1),
                                erlang:element(2, State@1),
                                erlang:element(3, Token)
                            ),
                            New_state = {state,
                                erlang:element(2, State@1),
                                {some, Token},
                                voauth_ffi:monotonic_ms(),
                                erlang:element(3, Token) * 1000,
                                {some, Timer},
                                erlang:element(7, State@1)},
                            {New_state, {ok, nil}};

                        {error, Refresh_error} ->
                            {State@1, {error, {refresh_failed, Refresh_error}}}
                    end;

                none ->
                    {State@1, {error, no_refresh_token}}
            end;

        none ->
            {State@1, {error, no_refresh_token}}
    end.

-file("src/voauth.gleam", 347).
-spec handle_get_token(
    gleam@erlang@process:subject({ok, binary()} | {error, vault_error()}),
    state()
) -> gleam@otp@actor:next(state(), message()).
handle_get_token(Reply, State) ->
    case erlang:element(3, State) of
        none ->
            gleam@erlang@process:send(Reply, {error, no_refresh_token}),
            gleam@otp@actor:continue(State);

        {some, Token} ->
            Elapsed = voauth_ffi:monotonic_ms() - erlang:element(4, State),
            case Elapsed < erlang:element(5, State) of
                true ->
                    gleam@erlang@process:send(
                        Reply,
                        {ok, erlang:element(2, Token)}
                    ),
                    gleam@otp@actor:continue(State);

                false ->
                    {New_state, Result} = attempt_refresh(State),
                    Reply_value = case Result of
                        {ok, _} ->
                            T@1 = case erlang:element(3, New_state) of
                                {some, T} -> T;
                                _assert_fail ->
                                    erlang:error(#{gleam_error => let_assert,
                                                message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
                                                file => <<?FILEPATH/utf8>>,
                                                module => <<"voauth"/utf8>>,
                                                function => <<"handle_get_token"/utf8>>,
                                                line => 367,
                                                value => _assert_fail,
                                                start => 12046,
                                                'end' => 12082,
                                                pattern_start => 12057,
                                                pattern_end => 12064})
                            end,
                            {ok, erlang:element(2, T@1)};

                        {error, Err} ->
                            {error, Err}
                    end,
                    gleam@erlang@process:send(Reply, Reply_value),
                    gleam@otp@actor:continue(New_state)
            end
    end.

-file("src/voauth.gleam", 380).
-spec handle_refresh_now(
    gleam@erlang@process:subject({ok, nil} | {error, vault_error()}),
    state()
) -> gleam@otp@actor:next(state(), message()).
handle_refresh_now(Reply, State) ->
    case erlang:element(3, State) of
        none ->
            gleam@erlang@process:send(Reply, {error, no_refresh_token}),
            gleam@otp@actor:continue(State);

        {some, _} ->
            {New_state, Result} = attempt_refresh(State),
            gleam@erlang@process:send(Reply, Result),
            gleam@otp@actor:continue(New_state)
    end.

-file("src/voauth.gleam", 397).
-spec handle_scheduled_refresh(integer(), state()) -> gleam@otp@actor:next(state(), message()).
handle_scheduled_refresh(Attempt, State) ->
    State@1 = {state,
        erlang:element(2, State),
        erlang:element(3, State),
        erlang:element(4, State),
        erlang:element(5, State),
        none,
        erlang:element(7, State)},
    case erlang:element(3, State@1) of
        {some, _} ->
            {New_state, Result} = attempt_refresh(State@1),
            case Result of
                {ok, _} ->
                    gleam@otp@actor:continue(New_state);

                {error, Err} ->
                    handle_refresh_failure(Attempt, Err, New_state)
            end;

        none ->
            gleam@otp@actor:continue(State@1)
    end.

-file("src/voauth.gleam", 338).
-spec handle_message(state(), message()) -> gleam@otp@actor:next(state(), message()).
handle_message(State, Message) ->
    case Message of
        {get_token, Reply} ->
            handle_get_token(Reply, State);

        {refresh_now, Reply@1} ->
            handle_refresh_now(Reply@1, State);

        {scheduled_refresh, Attempt} ->
            handle_scheduled_refresh(Attempt, State);

        {set_token, Reply@2, Token} ->
            handle_set_token(Reply@2, Token, State)
    end.

-file("src/voauth.gleam", 317).
-spec do_start(config()) -> {ok, gleam@otp@actor:started(vault())} |
    {error, gleam@otp@actor:start_error()}.
do_start(Config) ->
    Initialise = fun(Self) ->
        State = {state, Config, none, 0, 0, none, Self},
        _pipe = gleam@otp@actor:initialised(State),
        _pipe@1 = gleam@otp@actor:returning(
            _pipe,
            {vault, Self, erlang:element(4, Config)}
        ),
        {ok, _pipe@1}
    end,
    _pipe@2 = gleam@otp@actor:new_with_initialiser(
        erlang:element(5, Config),
        Initialise
    ),
    _pipe@3 = gleam@otp@actor:on_message(_pipe@2, fun handle_message/2),
    gleam@otp@actor:start(_pipe@3).

-file("src/voauth.gleam", 258).
?DOC(
    " Start a vault. The vault begins without a token; install one with\n"
    " `set_token` before calling `get_token` or `refresh_now`.\n"
).
-spec start(config()) -> {ok, vault()} | {error, vault_error()}.
start(Config) ->
    _pipe = do_start(Config),
    _pipe@1 = gleam@result:map(
        _pipe,
        fun(Started) -> erlang:element(3, Started) end
    ),
    gleam@result:map_error(
        _pipe@1,
        fun(E) -> {start_error, gleam@string:inspect(E)} end
    ).

-file("src/voauth.gleam", 269).
?DOC(
    " Build a child specification for a `gleam_otp` supervisor. On\n"
    " restart the supervisor calls `start` with the same `config`. To\n"
    " rehydrate a persisted token on restart, write your own\n"
    " `supervision.worker` that reads from durable storage and calls\n"
    " `start` and `set_token`.\n"
).
-spec supervised(config()) -> gleam@otp@supervision:child_specification(vault()).
supervised(Config) ->
    gleam@otp@supervision:worker(fun() -> do_start(Config) end).