src/rally@generator@ssr_handler.erl

-module(rally@generator@ssr_handler).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/rally/generator/ssr_handler.gleam").
-export([generate/12]).

-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(
    " SSR handler codegen.\n"
    "\n"
    " Generates ssr_handler.gleam: takes a parsed Route, calls the page's\n"
    " load function, renders view wrapped in layout, embeds the model as\n"
    " base64 flags for client hydration. Handles auth resolve, from_session,\n"
    " authorize, and LoadResult (Page/Redirect with cookies) when auth is\n"
    " configured.\n"
).

-file("src/rally/generator/ssr_handler.gleam", 295).
-spec marker_helper(boolean()) -> binary().
marker_helper(Has_load_pages) ->
    case Has_load_pages of
        true ->
            Body = [<<"fn insert_rendered(shell: String, rendered: String) -> String {"/utf8>>,
                <<"  case find_app_marker(shell) {"/utf8>>,
                <<"    Ok(marker) -> string.replace(shell, marker, marker <> rendered)"/utf8>>,
                <<"    Error(Nil) -> shell"/utf8>>,
                <<"  }"/utf8>>,
                <<"}"/utf8>>,
                <<""/utf8>>,
                <<"fn find_app_marker(shell: String) -> Result(String, Nil) {"/utf8>>,
                <<"  // Find the element with id=app (any tag, any attributes, any quoting)."/utf8>>,
                <<"  let id_dq = \"id=\\\"app\\\"\""/utf8>>,
                <<"  let id_sq = \"id='app'\""/utf8>>,
                <<"  let id_dq_spaced = \"id = \\\"app\\\"\""/utf8>>,
                <<"  let id_sq_spaced = \"id = 'app'\""/utf8>>,
                <<"  let id_attr = case string.contains(shell, id_dq) {"/utf8>>,
                <<"    True -> Ok(id_dq)"/utf8>>,
                <<"    False -> case string.contains(shell, id_sq) {"/utf8>>,
                <<"      True -> Ok(id_sq)"/utf8>>,
                <<"      False -> case string.contains(shell, id_dq_spaced) {"/utf8>>,
                <<"        True -> Ok(id_dq_spaced)"/utf8>>,
                <<"        False -> case string.contains(shell, id_sq_spaced) {"/utf8>>,
                <<"          True -> Ok(id_sq_spaced)"/utf8>>,
                <<"          False -> Error(Nil)"/utf8>>,
                <<"        }"/utf8>>,
                <<"      }"/utf8>>,
                <<"    }"/utf8>>,
                <<"  }"/utf8>>,
                <<"  use id_attr <- result.try(id_attr)"/utf8>>,
                <<"  use #(before_id, after_id) <- result.try(string.split_once(shell, id_attr))"/utf8>>,
                <<"  let reversed_before = string.reverse(before_id)"/utf8>>,
                <<"  use #(after_lt_rev, _) <- result.try(string.split_once(reversed_before, \"<\"))"/utf8>>,
                <<"  let tag_start = \"<\" <> string.reverse(after_lt_rev)"/utf8>>,
                <<"  use #(tag_end, _) <- result.try(string.split_once(after_id, \">\"))"/utf8>>,
                <<"  Ok(tag_start <> id_attr <> tag_end <> \">\")"/utf8>>,
                <<"}"/utf8>>],
            <<<<"\n"/utf8, (gleam@string:join(Body, <<"\n"/utf8>>))/binary>>/binary,
                "\n"/utf8>>;

        _ ->
            <<""/utf8>>
    end.

-file("src/rally/generator/ssr_handler.gleam", 817).
-spec do_snake_case(list(binary()), binary(), boolean()) -> binary().
do_snake_case(Remaining, Acc, Prev_lower) ->
    case Remaining of
        [] ->
            Acc;

        [G | Rest] ->
            Lower = string:lowercase(G),
            case Lower /= G of
                true ->
                    Sep = case Prev_lower of
                        true ->
                            <<"_"/utf8>>;

                        false ->
                            <<""/utf8>>
                    end,
                    do_snake_case(
                        Rest,
                        <<<<Acc/binary, Sep/binary>>/binary, Lower/binary>>,
                        false
                    );

                false ->
                    do_snake_case(Rest, <<Acc/binary, G/binary>>, true)
            end
    end.

-file("src/rally/generator/ssr_handler.gleam", 812).
-spec to_snake_case(binary()) -> binary().
to_snake_case(Name) ->
    Graphemes = gleam@string:to_graphemes(Name),
    do_snake_case(Graphemes, <<""/utf8>>, false).

-file("src/rally/generator/ssr_handler.gleam", 808).
-spec qualified_wire_name(binary(), binary()) -> binary().
qualified_wire_name(Module_path, Type_name) ->
    <<<<(gleam@string:replace(Module_path, <<"/"/utf8>>, <<"_"/utf8>>))/binary,
            "__"/utf8>>/binary,
        (to_snake_case(Type_name))/binary>>.

-file("src/rally/generator/ssr_handler.gleam", 757).
-spec generate_wire_externals(
    gleam@option:option(binary()),
    gleam@option:option(binary()),
    boolean(),
    list({rally@types:scanned_route(), rally@types:page_contract()}),
    boolean()
) -> binary().
generate_wire_externals(
    Wire_module,
    Client_context_module,
    Has_client_context,
    Page_contracts,
    Is_json
) ->
    case {Wire_module, Is_json} of
        {none, _} ->
            <<""/utf8>>;

        {_, true} ->
            <<""/utf8>>;

        {{some, Mod}, false} ->
            Cc_ext = case {Has_client_context, Client_context_module} of
                {true, {some, Cc_mod}} ->
                    Qual = qualified_wire_name(Cc_mod, <<"ClientContext"/utf8>>),
                    <<<<<<<<<<<<"\n@external(erlang, \""/utf8, Mod/binary>>/binary,
                                        "\", \"encode_"/utf8>>/binary,
                                    Qual/binary>>/binary,
                                "\")\nfn wire_encode_"/utf8>>/binary,
                            Qual/binary>>/binary,
                        "(value: a) -> b\n"/utf8>>;

                {_, _} ->
                    <<""/utf8>>
            end,
            Model_exts = begin
                _pipe = Page_contracts,
                _pipe@1 = gleam@list:filter_map(
                    _pipe,
                    fun(Pair) ->
                        {Route, Contract} = Pair,
                        case erlang:element(4, Contract) andalso erlang:element(
                            7,
                            Contract
                        ) of
                            true ->
                                Qual@1 = qualified_wire_name(
                                    erlang:element(5, Route),
                                    <<"Model"/utf8>>
                                ),
                                {ok,
                                    <<<<<<<<<<<<"@external(erlang, \""/utf8,
                                                            Mod/binary>>/binary,
                                                        "\", \"encode_"/utf8>>/binary,
                                                    Qual@1/binary>>/binary,
                                                "\")\nfn wire_encode_"/utf8>>/binary,
                                            Qual@1/binary>>/binary,
                                        "(value: a) -> b\n"/utf8>>};

                            false ->
                                {error, nil}
                        end
                    end
                ),
                _pipe@2 = gleam@list:unique(_pipe@1),
                gleam@string:join(_pipe@2, <<"\n"/utf8>>)
            end,
            <<<<Cc_ext/binary, "\n"/utf8>>/binary, Model_exts/binary>>
    end.

-file("src/rally/generator/ssr_handler.gleam", 375).
-spec last_segment(binary()) -> binary().
last_segment(Module_path) ->
    case begin
        _pipe = gleam@string:split(Module_path, <<"/"/utf8>>),
        gleam@list:last(_pipe)
    end of
        {ok, Seg} ->
            Seg;

        {error, nil} ->
            Module_path
    end.

-file("src/rally/generator/ssr_handler.gleam", 340).
-spec import_as(binary(), binary()) -> binary().
import_as(Module_path, Alias) ->
    case last_segment(Module_path) =:= Alias of
        true ->
            <<"import "/utf8, Module_path/binary>>;

        false ->
            <<<<<<"import "/utf8, Module_path/binary>>/binary, " as "/utf8>>/binary,
                Alias/binary>>
    end.

-file("src/rally/generator/ssr_handler.gleam", 371).
-spec module_to_alias(binary()) -> binary().
module_to_alias(Module_path) ->
    gleam@string:replace(Module_path, <<"/"/utf8>>, <<"_"/utf8>>).

-file("src/rally/generator/ssr_handler.gleam", 347).
-spec generate_page_imports(
    list({rally@types:scanned_route(), rally@types:page_contract()})
) -> binary().
generate_page_imports(Page_contracts) ->
    _pipe = Page_contracts,
    _pipe@1 = gleam@list:filter_map(
        _pipe,
        fun(Pair) ->
            {Route, Contract} = Pair,
            case erlang:element(4, Contract) andalso erlang:element(7, Contract) of
                true ->
                    Alias = module_to_alias(erlang:element(5, Route)),
                    {ok,
                        <<<<<<"import "/utf8,
                                    (erlang:element(5, Route))/binary>>/binary,
                                " as "/utf8>>/binary,
                            Alias/binary>>};

                false ->
                    {error, nil}
            end
        end
    ),
    _pipe@2 = gleam@list:unique(_pipe@1),
    _pipe@3 = gleam@list:sort(_pipe@2, fun gleam@string:compare/2),
    (fun(Imports) -> case Imports of
            [] ->
                <<""/utf8>>;

            _ ->
                <<(gleam@string:join(Imports, <<"\n"/utf8>>))/binary,
                    "\n"/utf8>>
        end end)(_pipe@3).

-file("src/rally/generator/ssr_handler.gleam", 382).
-spec generate_layout_imports(
    list({rally@types:scanned_route(), rally@types:page_contract()})
) -> binary().
generate_layout_imports(Page_contracts) ->
    _pipe = Page_contracts,
    _pipe@1 = gleam@list:filter_map(
        _pipe,
        fun(Pair) ->
            {Route, Contract} = Pair,
            case {erlang:element(4, Contract) andalso erlang:element(
                    7,
                    Contract
                ),
                erlang:element(6, Route)} of
                {true, {some, Layout}} ->
                    {ok,
                        <<<<<<"import "/utf8, Layout/binary>>/binary,
                                " as "/utf8>>/binary,
                            (module_to_alias(Layout))/binary>>};

                {_, _} ->
                    {error, nil}
            end
        end
    ),
    _pipe@2 = gleam@list:unique(_pipe@1),
    _pipe@3 = gleam@list:sort(_pipe@2, fun gleam@string:compare/2),
    (fun(Imports) -> case Imports of
            [] ->
                <<""/utf8>>;

            _ ->
                <<(gleam@string:join(Imports, <<"\n"/utf8>>))/binary,
                    "\n"/utf8>>
        end end)(_pipe@3).

-file("src/rally/generator/ssr_handler.gleam", 747).
-spec route_pattern_for_load(rally@types:scanned_route()) -> binary().
route_pattern_for_load(Route) ->
    case erlang:element(4, Route) of
        [] ->
            <<""/utf8>>;

        Params ->
            Names = gleam@list:map(Params, fun(P) -> erlang:element(1, P) end),
            <<<<"("/utf8, (gleam@string:join(Names, <<", "/utf8>>))/binary>>/binary,
                ")"/utf8>>
    end.

-file("src/rally/generator/ssr_handler.gleam", 404).
-spec generate_load_arms(
    list({rally@types:scanned_route(), rally@types:page_contract()}),
    boolean(),
    boolean(),
    boolean(),
    binary(),
    gleam@option:option(binary()),
    gleam@option:option(rally@types:auth_config()),
    boolean()
) -> binary().
generate_load_arms(
    Page_contracts,
    Has_client_context,
    Use_session,
    Has_from_session,
    From_session_module,
    Wire_module,
    Auth_config,
    Is_json
) ->
    Auth_module_ref = case Auth_config of
        {some, {auth_config, Auth_module}} ->
            last_segment(Auth_module);

        none ->
            <<""/utf8>>
    end,
    _pipe = Page_contracts,
    _pipe@3 = gleam@list:filter_map(
        _pipe,
        fun(Pair) ->
            {Route, Contract} = Pair,
            case erlang:element(4, Contract) andalso erlang:element(7, Contract) of
                true ->
                    Alias = module_to_alias(erlang:element(5, Route)),
                    Pattern = route_pattern_for_load(Route),
                    Load_args = begin
                        _pipe@1 = erlang:element(4, Route),
                        _pipe@2 = gleam@list:map(
                            _pipe@1,
                            fun(P) -> erlang:element(1, P) end
                        ),
                        gleam@string:join(_pipe@2, <<", "/utf8>>)
                    end,
                    Has_params = erlang:element(4, Route) /= [],
                    Has_auth = gleam@option:is_some(Auth_config),
                    Needs_fs_for_auth = Has_from_session,
                    Identity_arg = case Has_auth of
                        true ->
                            case Has_params of
                                true ->
                                    <<", server_context, identity"/utf8>>;

                                false ->
                                    <<"server_context, identity"/utf8>>
                            end;

                        false ->
                            case Has_params of
                                true ->
                                    <<", server_context"/utf8>>;

                                false ->
                                    <<"server_context"/utf8>>
                            end
                    end,
                    From_session_call = case Has_auth of
                        true ->
                            <<From_session_module/binary,
                                ".from_session(server_context: server_context, session_id: session_id, hostname: hostname, identity: identity)"/utf8>>;

                        false ->
                            <<From_session_module/binary,
                                ".from_session(server_context: server_context, session_id: session_id, hostname: hostname)"/utf8>>
                    end,
                    Needs_from_session = Use_session orelse (Has_auth andalso Needs_fs_for_auth),
                    Ctx_init = case Needs_from_session of
                        true ->
                            case Has_client_context of
                                true ->
                                    <<<<"      let #(client_context, server_context) = "/utf8,
                                            From_session_call/binary>>/binary,
                                        "\n"/utf8>>;

                                false ->
                                    <<<<"      let #(_, server_context) = "/utf8,
                                            From_session_call/binary>>/binary,
                                        "\n"/utf8>>
                            end;

                        false ->
                            case Has_client_context of
                                true ->
                                    <<"      let #(client_context, _) = client_context.init()\n"/utf8>>;

                                false ->
                                    <<""/utf8>>
                            end
                    end,
                    View_call = case Has_client_context of
                        true ->
                            <<Alias/binary,
                                ".view(client_context, model)"/utf8>>;

                        false ->
                            <<Alias/binary, ".view(model)"/utf8>>
                    end,
                    View_expr = case {erlang:element(6, Route),
                        Has_client_context} of
                        {{some, Layout}, true} ->
                            Layout_alias = module_to_alias(Layout),
                            <<<<<<<<<<"    let page_view = element.map("/utf8,
                                                View_call/binary>>/binary,
                                            ", fn(_) { Nil })\n"/utf8>>/binary,
                                        "    let rendered = element.to_string("/utf8>>/binary,
                                    Layout_alias/binary>>/binary,
                                ".layout(client_context, fn(_) { Nil }, page_view))\n"/utf8>>;

                        {{some, Layout@1}, false} ->
                            Layout_alias@1 = module_to_alias(Layout@1),
                            <<<<<<<<<<"    let page_view = element.map("/utf8,
                                                View_call/binary>>/binary,
                                            ", fn(_) { Nil })\n"/utf8>>/binary,
                                        "    let rendered = element.to_string("/utf8>>/binary,
                                    Layout_alias@1/binary>>/binary,
                                ".layout(page_view))\n"/utf8>>;

                        {none, _} ->
                            <<<<"    let rendered = element.to_string("/utf8,
                                    View_call/binary>>/binary,
                                ")\n"/utf8>>
                    end,
                    Ctx_script = case Use_session of
                        true ->
                            <<"      let ctx_tag = context_script(client_context)\n"/utf8>>;

                        false ->
                            <<""/utf8>>
                    end,
                    Flags_target = case erlang:element(6, Contract) of
                        true ->
                            <<"data"/utf8>>;

                        false ->
                            <<"model"/utf8>>
                    end,
                    Wire_encode_flags = case {Wire_module, Is_json} of
                        {{some, _}, false} ->
                            Qual = qualified_wire_name(
                                erlang:element(5, Route),
                                <<"Model"/utf8>>
                            ),
                            <<<<<<<<<<<<"      let "/utf8, Flags_target/binary>>/binary,
                                                " = wire_encode_"/utf8>>/binary,
                                            Qual/binary>>/binary,
                                        "("/utf8>>/binary,
                                    Flags_target/binary>>/binary,
                                ")\n"/utf8>>;

                        {{some, _}, true} ->
                            Qual@1 = qualified_wire_name(
                                erlang:element(5, Route),
                                <<"Model"/utf8>>
                            ),
                            <<<<<<<<<<<<"      let "/utf8, Flags_target/binary>>/binary,
                                                " = json_codecs.json_encode_"/utf8>>/binary,
                                            Qual@1/binary>>/binary,
                                        "("/utf8>>/binary,
                                    Flags_target/binary>>/binary,
                                ")\n"/utf8>>;

                        {none, _} ->
                            <<""/utf8>>
                    end,
                    Flags_line = <<<<<<<<Wire_encode_flags/binary,
                                    "      let flags = libero_wire.encode_flags("/utf8>>/binary,
                                Flags_target/binary>>/binary,
                            ")\n"/utf8>>/binary,
                        "      let flags_tag = \"<script>window.__RALLY_FLAGS__='\" <> flags <> \"'</script>\"\n"/utf8>>,
                    Full_html_line = case Use_session of
                        true ->
                            <<"      let shell = shell_html()\n      let full_html = insert_rendered(shell, rendered)\n        <> env.browser_env_script() <> ctx_tag <> flags_tag\n"/utf8>>;

                        false ->
                            <<"      let shell = shell_html()\n      let full_html = insert_rendered(shell, rendered)\n        <> env.browser_env_script() <> flags_tag\n"/utf8>>
                    end,
                    Render_body = <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Ctx_init/binary,
                                                                                        "      let "/utf8>>/binary,
                                                                                    Flags_target/binary>>/binary,
                                                                                " = "/utf8>>/binary,
                                                                            Alias/binary>>/binary,
                                                                        ".load("/utf8>>/binary,
                                                                    Load_args/binary>>/binary,
                                                                Identity_arg/binary>>/binary,
                                                            ")\n"/utf8>>/binary,
                                                        (case {erlang:element(
                                                                6,
                                                                Contract
                                                            ),
                                                            Has_client_context} of
                                                            {true, true} ->
                                                                <<<<"      let #(model, _) = "/utf8,
                                                                        Alias/binary>>/binary,
                                                                    ".init_loaded(client_context, data)\n"/utf8>>;

                                                            {true, false} ->
                                                                <<<<"      let #(model, _) = "/utf8,
                                                                        Alias/binary>>/binary,
                                                                    ".init_loaded(data)\n"/utf8>>;

                                                            {false, _} ->
                                                                <<""/utf8>>
                                                        end)/binary>>/binary,
                                                    "      "/utf8>>/binary,
                                                View_expr/binary>>/binary,
                                            Ctx_script/binary>>/binary,
                                        Flags_line/binary>>/binary,
                                    Full_html_line/binary>>/binary,
                                "      response.new(200)\n"/utf8>>/binary,
                            "      |> response.set_header(\"content-type\", \"text/html\")\n"/utf8>>/binary,
                        "      |> response.set_body(mist.Bytes(bytes_tree.from_string(full_html)))\n"/utf8>>,
                    Arm_body = case Has_auth of
                        true ->
                            Resolve_open = <<<<<<<<<<<<"      case "/utf8,
                                                    Auth_module_ref/binary>>/binary,
                                                ".resolve(server_context, session_id) {\n"/utf8>>/binary,
                                            "        Error(Nil) ->\n"/utf8>>/binary,
                                        "          response.new(500)\n"/utf8>>/binary,
                                    "          |> response.set_body(mist.Bytes(bytes_tree.from_string(\"Auth service unavailable\")))\n"/utf8>>/binary,
                                "        Ok(identity) -> {\n"/utf8>>,
                            Is_auth_check = case erlang:element(15, Contract) of
                                true ->
                                    <<<<<<<<<<<<<<<<"          case "/utf8,
                                                                    Auth_module_ref/binary>>/binary,
                                                                ".is_authenticated(identity) {\n"/utf8>>/binary,
                                                            "            False -> response.new(302)\n"/utf8>>/binary,
                                                        "              |> response.set_header(\"location\", "/utf8>>/binary,
                                                    Auth_module_ref/binary>>/binary,
                                                ".redirect_url)\n"/utf8>>/binary,
                                            "              |> response.set_body(mist.Bytes(bytes_tree.from_string(\"\")))\n"/utf8>>/binary,
                                        "            True -> {\n"/utf8>>;

                                false ->
                                    <<""/utf8>>
                            end,
                            Authorize_check = case erlang:element(16, Contract) of
                                true ->
                                    <<<<<<<<<<"              case "/utf8,
                                                        Alias/binary>>/binary,
                                                    ".authorize(server_context, identity) {\n"/utf8>>/binary,
                                                "                False -> response.new(403)\n"/utf8>>/binary,
                                            "                  |> response.set_body(mist.Bytes(bytes_tree.from_string(\"Forbidden\")))\n"/utf8>>/binary,
                                        "                True -> {\n"/utf8>>;

                                false ->
                                    <<""/utf8>>
                            end,
                            Model_var = case erlang:element(6, Contract) of
                                true ->
                                    <<"model"/utf8>>;

                                false ->
                                    <<"data"/utf8>>
                            end,
                            View_call_auth = case Has_client_context of
                                true ->
                                    <<<<<<Alias/binary,
                                                ".view(client_context, "/utf8>>/binary,
                                            Model_var/binary>>/binary,
                                        ")"/utf8>>;

                                false ->
                                    <<<<<<Alias/binary, ".view("/utf8>>/binary,
                                            Model_var/binary>>/binary,
                                        ")"/utf8>>
                            end,
                            View_expr_auth = case {erlang:element(6, Route),
                                Has_client_context} of
                                {{some, Layout@2}, true} ->
                                    Layout_alias@2 = module_to_alias(Layout@2),
                                    <<<<<<<<<<"    let page_view = element.map("/utf8,
                                                        View_call_auth/binary>>/binary,
                                                    ", fn(_) { Nil })\n"/utf8>>/binary,
                                                "    let rendered = element.to_string("/utf8>>/binary,
                                            Layout_alias@2/binary>>/binary,
                                        ".layout(client_context, fn(_) { Nil }, page_view))\n"/utf8>>;

                                {{some, Layout@3}, false} ->
                                    Layout_alias@3 = module_to_alias(Layout@3),
                                    <<<<<<<<<<"    let page_view = element.map("/utf8,
                                                        View_call_auth/binary>>/binary,
                                                    ", fn(_) { Nil })\n"/utf8>>/binary,
                                                "    let rendered = element.to_string("/utf8>>/binary,
                                            Layout_alias@3/binary>>/binary,
                                        ".layout(page_view))\n"/utf8>>;

                                {none, _} ->
                                    <<<<"    let rendered = element.to_string("/utf8,
                                            View_call_auth/binary>>/binary,
                                        ")\n"/utf8>>
                            end,
                            Wire_encode_flags_auth = case {Wire_module, Is_json} of
                                {{some, _}, false} ->
                                    Qual@2 = qualified_wire_name(
                                        erlang:element(5, Route),
                                        <<"Model"/utf8>>
                                    ),
                                    <<<<<<<<<<<<"      let "/utf8,
                                                            Model_var/binary>>/binary,
                                                        " = wire_encode_"/utf8>>/binary,
                                                    Qual@2/binary>>/binary,
                                                "("/utf8>>/binary,
                                            Model_var/binary>>/binary,
                                        ")\n"/utf8>>;

                                {{some, _}, true} ->
                                    Qual@3 = qualified_wire_name(
                                        erlang:element(5, Route),
                                        <<"Model"/utf8>>
                                    ),
                                    <<<<<<<<<<<<"      let "/utf8,
                                                            Model_var/binary>>/binary,
                                                        " = json_codecs.json_encode_"/utf8>>/binary,
                                                    Qual@3/binary>>/binary,
                                                "("/utf8>>/binary,
                                            Model_var/binary>>/binary,
                                        ")\n"/utf8>>;

                                {none, _} ->
                                    <<""/utf8>>
                            end,
                            Flags_line_auth = <<<<<<<<Wire_encode_flags_auth/binary,
                                            "      let flags = libero_wire.encode_flags("/utf8>>/binary,
                                        Model_var/binary>>/binary,
                                    ")\n"/utf8>>/binary,
                                "      let flags_tag = \"<script>window.__RALLY_FLAGS__='\" <> flags <> \"'</script>\"\n"/utf8>>,
                            Load_result_block = <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<Ctx_init/binary,
                                                                                                                                            "                  case "/utf8>>/binary,
                                                                                                                                        Alias/binary>>/binary,
                                                                                                                                    ".load("/utf8>>/binary,
                                                                                                                                Load_args/binary>>/binary,
                                                                                                                            Identity_arg/binary>>/binary,
                                                                                                                        ") {\n"/utf8>>/binary,
                                                                                                                    "                    rally_auth.Page(data, cookies) -> {\n"/utf8>>/binary,
                                                                                                                (case {erlang:element(
                                                                                                                        6,
                                                                                                                        Contract
                                                                                                                    ),
                                                                                                                    Has_client_context} of
                                                                                                                    {true,
                                                                                                                        true} ->
                                                                                                                        <<<<"                      let #(model, _) = "/utf8,
                                                                                                                                Alias/binary>>/binary,
                                                                                                                            ".init_loaded(client_context, data)\n"/utf8>>;

                                                                                                                    {true,
                                                                                                                        false} ->
                                                                                                                        <<<<"                      let #(model, _) = "/utf8,
                                                                                                                                Alias/binary>>/binary,
                                                                                                                            ".init_loaded(data)\n"/utf8>>;

                                                                                                                    {false,
                                                                                                                        _} ->
                                                                                                                        <<""/utf8>>
                                                                                                                end)/binary>>/binary,
                                                                                                            "                      "/utf8>>/binary,
                                                                                                        View_expr_auth/binary>>/binary,
                                                                                                    Ctx_script/binary>>/binary,
                                                                                                Flags_line_auth/binary>>/binary,
                                                                                            Full_html_line/binary>>/binary,
                                                                                        "                      apply_cookies(\n"/utf8>>/binary,
                                                                                    "                        response.new(200)\n"/utf8>>/binary,
                                                                                "                        |> response.set_header(\"content-type\", \"text/html\")\n"/utf8>>/binary,
                                                                            "                        |> response.set_body(mist.Bytes(bytes_tree.from_string(full_html))),\n"/utf8>>/binary,
                                                                        "                        cookies,\n"/utf8>>/binary,
                                                                    "                      )\n"/utf8>>/binary,
                                                                "                    }\n"/utf8>>/binary,
                                                            "                    rally_auth.Redirect(url, cookies) ->\n"/utf8>>/binary,
                                                        "                      apply_cookies(\n"/utf8>>/binary,
                                                    "                        response.new(302)\n"/utf8>>/binary,
                                                "                        |> response.set_header(\"location\", url)\n"/utf8>>/binary,
                                            "                        |> response.set_body(mist.Bytes(bytes_tree.from_string(\"\"))),\n"/utf8>>/binary,
                                        "                        cookies,\n"/utf8>>/binary,
                                    "                      )\n"/utf8>>/binary,
                                "                  }\n"/utf8>>,
                            Close_authorize = case erlang:element(16, Contract) of
                                true ->
                                    <<"                }\n              }\n"/utf8>>;

                                false ->
                                    <<""/utf8>>
                            end,
                            Close_is_auth = case erlang:element(15, Contract) of
                                true ->
                                    <<"            }\n          }\n"/utf8>>;

                                false ->
                                    <<""/utf8>>
                            end,
                            Close_resolve = <<"        }\n      }\n"/utf8>>,
                            <<<<<<<<<<<<Resolve_open/binary,
                                                    Is_auth_check/binary>>/binary,
                                                Authorize_check/binary>>/binary,
                                            Load_result_block/binary>>/binary,
                                        Close_authorize/binary>>/binary,
                                    Close_is_auth/binary>>/binary,
                                Close_resolve/binary>>;

                        false ->
                            Render_body
                    end,
                    {ok,
                        <<<<<<<<<<"    router."/utf8,
                                            (erlang:element(3, Route))/binary>>/binary,
                                        Pattern/binary>>/binary,
                                    " -> {\n"/utf8>>/binary,
                                Arm_body/binary>>/binary,
                            "    }"/utf8>>};

                false ->
                    {error, nil}
            end
        end
    ),
    gleam@string:join(_pipe@3, <<"\n"/utf8>>).

-file("src/rally/generator/ssr_handler.gleam", 16).
-spec generate(
    list({rally@types:scanned_route(), rally@types:page_contract()}),
    boolean(),
    boolean(),
    binary(),
    binary(),
    binary(),
    binary(),
    gleam@option:option(binary()),
    gleam@option:option(binary()),
    gleam@option:option(rally@types:auth_config()),
    binary(),
    binary()
) -> binary().
generate(
    Page_contracts,
    Has_client_context,
    Has_from_session,
    From_session_module,
    Router_module,
    Shell_html,
    Atoms_module,
    Wire_module,
    Client_context_module,
    Auth_config,
    Wire_import_module,
    Protocol
) ->
    Use_session = Has_client_context andalso Has_from_session,
    Is_json = Protocol =:= <<"json"/utf8>>,
    Json_codecs_module = case Is_json of
        true ->
            gleam@string:replace(
                Wire_import_module,
                <<"/protocol_wire"/utf8>>,
                <<"/json_codecs"/utf8>>
            );

        false ->
            <<""/utf8>>
    end,
    From_session_ref = last_segment(From_session_module),
    Has_auth = gleam@option:is_some(Auth_config),
    Auth_module_ref = case Auth_config of
        {some, {auth_config, Auth_module}} ->
            last_segment(Auth_module);

        none ->
            <<""/utf8>>
    end,
    Load_arms = generate_load_arms(
        Page_contracts,
        Has_client_context,
        Use_session,
        Has_from_session,
        From_session_ref,
        Wire_module,
        Auth_config,
        Is_json
    ),
    Has_load_pages = Load_arms /= <<""/utf8>>,
    All_routes_have_load = gleam@list:all(
        Page_contracts,
        fun(Pair) ->
            {_, Contract} = Pair,
            erlang:element(4, Contract) andalso erlang:element(7, Contract)
        end
    ),
    Layout_imports = generate_layout_imports(Page_contracts),
    Page_imports = generate_page_imports(Page_contracts),
    Needs_server_context = Use_session orelse Has_load_pages,
    Needs_from_session_import = Use_session orelse (Has_auth andalso Has_from_session),
    Needs_codec = Use_session orelse Has_load_pages,
    Base_imports = <<<<<<<<"// Generated by Rally — do not edit.\n\nimport gleam/bytes_tree\nimport gleam/http/response\nimport mist.{type ResponseData}\n"/utf8,
                    (import_as(Router_module, <<"router"/utf8>>))/binary>>/binary,
                "\nimport rally_runtime/env\n\n@external(erlang, \""/utf8>>/binary,
            Atoms_module/binary>>/binary,
        "\", \"ensure\")\nfn ensure_atoms() -> Nil\n"/utf8>>,
    Server_imports = case Needs_server_context of
        true ->
            <<"import server_context.{type ServerContext}\n"/utf8,
                (case Needs_from_session_import andalso (From_session_module /= <<"server_context"/utf8>>) of
                    true ->
                        <<(import_as(From_session_module, From_session_ref))/binary,
                            "\n"/utf8>>;

                    false ->
                        <<""/utf8>>
                end)/binary>>;

        false ->
            <<""/utf8>>
    end,
    Client_context_imports = case {Has_client_context, Client_context_module} of
        {true, {some, Mod}} ->
            <<(import_as(Mod, <<"client_context"/utf8>>))/binary, "\n"/utf8>>;

        {_, _} ->
            <<""/utf8>>
    end,
    Codec_imports = case Needs_codec of
        true ->
            <<(import_as(Wire_import_module, <<"libero_wire"/utf8>>))/binary,
                "\n"/utf8>>;

        false ->
            <<""/utf8>>
    end,
    Json_codec_imports = case Is_json andalso Needs_codec of
        true ->
            <<(import_as(Json_codecs_module, <<"json_codecs"/utf8>>))/binary,
                "\n"/utf8>>;

        false ->
            <<""/utf8>>
    end,
    Load_page_imports = case Has_load_pages of
        true ->
            <<"import gleam/result\nimport gleam/string\nimport lustre/element\n"/utf8>>;

        false ->
            <<""/utf8>>
    end,
    Auth_imports = case Auth_config of
        {some, {auth_config, Auth_module@1}} ->
            <<(import_as(Auth_module@1, Auth_module_ref))/binary,
                (case Has_load_pages of
                    true ->
                        <<"\nimport rally_runtime/auth as rally_auth\nimport gleam/int\nimport gleam/list\n"/utf8>>;

                    false ->
                        <<"\n"/utf8>>
                end)/binary>>;

        _ ->
            <<""/utf8>>
    end,
    Wire_externals = generate_wire_externals(
        Wire_module,
        Client_context_module,
        Has_client_context,
        Page_contracts,
        Is_json
    ),
    Header = <<<<<<<<<<<<<<<<<<Base_imports/binary, Server_imports/binary>>/binary,
                                    Client_context_imports/binary>>/binary,
                                Codec_imports/binary>>/binary,
                            Json_codec_imports/binary>>/binary,
                        Load_page_imports/binary>>/binary,
                    Auth_imports/binary>>/binary,
                Layout_imports/binary>>/binary,
            Page_imports/binary>>/binary,
        Wire_externals/binary>>,
    Fn_params = case Needs_server_context of
        true ->
            <<"
pub fn handle_request(
  route route: router.Route,
  server_context server_context: ServerContext,
  session_id session_id: String,
  hostname hostname: String,
) -> response.Response(ResponseData) {"/utf8>>;

        false ->
            <<"
pub fn handle_request(
  _route: router.Route,
) -> response.Response(ResponseData) {"/utf8>>
    end,
    Cc_encode_line = case {Wire_module, Client_context_module, Is_json} of
        {{some, _}, {some, Cc_mod}, false} ->
            Qual = qualified_wire_name(Cc_mod, <<"ClientContext"/utf8>>),
            <<<<"  let client_context = wire_encode_"/utf8, Qual/binary>>/binary,
                "(client_context)\n"/utf8>>;

        {_, {some, Cc_mod@1}, true} ->
            Qual@1 = qualified_wire_name(Cc_mod@1, <<"ClientContext"/utf8>>),
            <<<<"  let client_context = json_codecs.json_encode_"/utf8,
                    Qual@1/binary>>/binary,
                "(client_context)\n"/utf8>>;

        {_, _, _} ->
            <<""/utf8>>
    end,
    Ctx_script = case Use_session of
        true ->
            <<<<"
fn context_script(client_context: client_context.ClientContext) -> String {
"/utf8,
                    Cc_encode_line/binary>>/binary,
                "  let encoded = libero_wire.encode_flags(client_context)
  \"<script>window.__RALLY_CLIENT_CONTEXT__='\" <> encoded <> \"'</script>\"
}
"/utf8>>;

        false ->
            <<""/utf8>>
    end,
    Shell_call = case Use_session of
        true ->
            <<"serve_html_shell(server_context: server_context, session_id: session_id, hostname: hostname)"/utf8>>;

        false ->
            <<"serve_html_shell()"/utf8>>
    end,
    Fn_body = case Has_load_pages of
        true ->
            Fallback = case All_routes_have_load of
                true ->
                    <<""/utf8>>;

                false ->
                    <<"\n    _ -> "/utf8, Shell_call/binary>>
            end,
            <<<<<<<<<<Fn_params/binary,
                                "
  ensure_atoms()
  case route {\n"/utf8>>/binary,
                            Load_arms/binary>>/binary,
                        "
    router.NotFound(_) ->
      response.new(404)
      |> response.set_body(mist.Bytes(bytes_tree.from_string(\"Not found\")))"/utf8>>/binary,
                    Fallback/binary>>/binary,
                "
  }
}"/utf8>>;

        false ->
            <<<<<<Fn_params/binary, "
  ensure_atoms()
  "/utf8>>/binary,
                    Shell_call/binary>>/binary,
                "
}"/utf8>>
    end,
    Dark_mode_script = <<"<script>(function(){var c=document.cookie;if(c.includes('__rally_dark_mode=1')||(!c.includes('__rally_dark_mode=')&&window.matchMedia('(prefers-color-scheme:dark)').matches)){document.documentElement.classList.add('dark')}})()</script>"/utf8>>,
    Shell_with_dark = gleam@string:replace(
        Shell_html,
        <<"</head>"/utf8>>,
        <<Dark_mode_script/binary, "</head>"/utf8>>
    ),
    Escaped_shell = begin
        _pipe = Shell_with_dark,
        _pipe@1 = gleam@string:replace(_pipe, <<"\\"/utf8>>, <<"\\\\"/utf8>>),
        _pipe@2 = gleam@string:replace(_pipe@1, <<"\""/utf8>>, <<"\\\""/utf8>>),
        gleam@string:replace(_pipe@2, <<"\n"/utf8>>, <<"\\n"/utf8>>)
    end,
    Needs_shell_fn = case Has_load_pages of
        true ->
            not All_routes_have_load;

        false ->
            true
    end,
    Shell_fn = case Needs_shell_fn of
        false ->
            <<""/utf8>>;

        true ->
            case {Use_session, Has_auth} of
                {true, true} ->
                    <<<<<<<<<<<<"
fn serve_html_shell(server_context server_context: ServerContext, session_id session_id: String, hostname hostname: String) -> response.Response(ResponseData) {
  case "/utf8,
                                            Auth_module_ref/binary>>/binary,
                                        ".resolve(server_context, session_id) {
    Error(Nil) ->
      response.new(500)
      |> response.set_body(mist.Bytes(bytes_tree.from_string(\"Auth service unavailable\")))
    Ok(identity) -> {
      let #(client_context, _) = "/utf8>>/binary,
                                    From_session_ref/binary>>/binary,
                                ".from_session(server_context: server_context, session_id: session_id, hostname: hostname, identity: identity)
      let html = \""/utf8>>/binary,
                            Escaped_shell/binary>>/binary,
                        "\" <> env.browser_env_script() <> context_script(client_context)
      response.new(200)
      |> response.set_header(\"content-type\", \"text/html\")
      |> response.set_body(mist.Bytes(bytes_tree.from_string(html)))
    }
  }
}
"/utf8>>;

                {true, false} ->
                    <<<<<<<<"
fn serve_html_shell(server_context server_context: ServerContext, session_id session_id: String, hostname hostname: String) -> response.Response(ResponseData) {
  let #(client_context, _) = "/utf8,
                                    From_session_ref/binary>>/binary,
                                ".from_session(server_context: server_context, session_id: session_id, hostname: hostname)
  let html = \""/utf8>>/binary,
                            Escaped_shell/binary>>/binary,
                        "\" <> env.browser_env_script() <> context_script(client_context)
  response.new(200)
  |> response.set_header(\"content-type\", \"text/html\")
  |> response.set_body(mist.Bytes(bytes_tree.from_string(html)))
}
"/utf8>>;

                {false, _} ->
                    <<<<"
fn serve_html_shell() -> response.Response(ResponseData) {
  let html = \""/utf8,
                            Escaped_shell/binary>>/binary,
                        "\" <> env.browser_env_script()
  response.new(200)
  |> response.set_header(\"content-type\", \"text/html\")
  |> response.set_body(mist.Bytes(bytes_tree.from_string(html)))
}
"/utf8>>
            end
    end,
    Shell_html_fn = case Has_load_pages of
        true ->
            <<<<"
fn shell_html() -> String {
  \""/utf8, Escaped_shell/binary>>/binary,
                "\"
}
"/utf8>>;

        false ->
            <<""/utf8>>
    end,
    Cookies_helper = case Has_auth andalso Has_load_pages of
        true ->
            <<"
fn apply_cookies(resp: response.Response(ResponseData), cookies: List(rally_auth.Cookie)) -> response.Response(ResponseData) {
  list.fold(cookies, resp, fn(resp, cookie) {
    case cookie {
      rally_auth.SetCookie(name, value, max_age) ->
        response.prepend_header(resp, \"set-cookie\",
          name <> \"=\" <> value <> \"; Path=/; HttpOnly; SameSite=Lax; Max-Age=\" <> int.to_string(max_age))
      rally_auth.ClearCookie(name) ->
        response.prepend_header(resp, \"set-cookie\",
          name <> \"=; Path=/; HttpOnly; Max-Age=0\")
    }
  })
}
"/utf8>>;

        false ->
            <<""/utf8>>
    end,
    <<<<<<<<<<<<Header/binary, Fn_body/binary>>/binary, Ctx_script/binary>>/binary,
                    Shell_fn/binary>>/binary,
                Shell_html_fn/binary>>/binary,
            Cookies_helper/binary>>/binary,
        (marker_helper(Has_load_pages))/binary>>.