-module(lightspeed@framework@endpoint). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/lightspeed/framework/endpoint.gleam"). -export([new/2, pipe/2, get_controller/3, get_controller_indexed/3, get_live/4, get_live_indexed/4, static/4, call/2, route_labels/1, static_labels/1]). -export_type([route/0, static_asset/0, endpoint/0, dispatch/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(" Endpoint + middleware pipeline with controller and live-route dispatch.\n"). -type route() :: {controller_route, lightspeed@framework@http:method(), binary(), fun((lightspeed@framework@http:conn()) -> lightspeed@framework@http:conn())} | {live_route, lightspeed@framework@http:method(), binary(), binary(), fun((lightspeed@framework@http:conn()) -> binary())}. -type static_asset() :: {static_asset, binary(), binary(), binary()}. -opaque endpoint() :: {endpoint, binary(), lightspeed@transport@contract:auth_hook(), list(fun((lightspeed@framework@http:conn()) -> lightspeed@framework@http:conn())), list(route()), list(static_asset())}. -type dispatch() :: {controller_dispatch, fun((lightspeed@framework@http:conn()) -> lightspeed@framework@http:conn()), list(lightspeed@framework@http:route_param())} | {live_dispatch, binary(), fun((lightspeed@framework@http:conn()) -> binary()), list(lightspeed@framework@http:route_param())}. -file("src/lightspeed/framework/endpoint.gleam", 58). ?DOC(" Create an endpoint.\n"). -spec new(lightspeed@transport@contract:auth_hook(), binary()) -> endpoint(). new(Auth_hook, Websocket_path) -> {endpoint, Websocket_path, Auth_hook, [], [], []}. -file("src/lightspeed/framework/endpoint.gleam", 69). ?DOC(" Register one middleware in endpoint order.\n"). -spec pipe( endpoint(), fun((lightspeed@framework@http:conn()) -> lightspeed@framework@http:conn()) ) -> endpoint(). pipe(Endpoint, Middleware) -> {endpoint, erlang:element(2, Endpoint), erlang:element(3, Endpoint), [Middleware | erlang:element(4, Endpoint)], erlang:element(5, Endpoint), erlang:element(6, Endpoint)}. -file("src/lightspeed/framework/endpoint.gleam", 74). ?DOC(" Register a GET controller route from a verified route helper.\n"). -spec get_controller( endpoint(), lightspeed@framework@verified_routes:verified_route(any()), fun((lightspeed@framework@http:conn()) -> lightspeed@framework@http:conn()) ) -> endpoint(). get_controller(Endpoint, Route, Run) -> Pattern = lightspeed@framework@verified_routes:pattern(Route), {endpoint, erlang:element(2, Endpoint), erlang:element(3, Endpoint), erlang:element(4, Endpoint), [{controller_route, get, Pattern, Run} | erlang:element(5, Endpoint)], erlang:element(6, Endpoint)}. -file("src/lightspeed/framework/endpoint.gleam", 87). ?DOC(" Register a GET controller route from one router-indexed verified route.\n"). -spec get_controller_indexed( endpoint(), lightspeed@framework@verified_routes:indexed_route(any(), lightspeed@framework@verified_routes:get_tag()), fun((lightspeed@framework@http:conn()) -> lightspeed@framework@http:conn()) ) -> endpoint(). get_controller_indexed(Endpoint, Route, Run) -> get_controller( Endpoint, lightspeed@framework@verified_routes:as_verified_route(Route), Run ). -file("src/lightspeed/framework/endpoint.gleam", 96). ?DOC(" Register a GET live route from a verified route helper.\n"). -spec get_live( endpoint(), lightspeed@framework@verified_routes:verified_route(any()), binary(), fun((lightspeed@framework@http:conn()) -> binary()) ) -> endpoint(). get_live(Endpoint, Route, View_id, Render) -> Pattern = lightspeed@framework@verified_routes:pattern(Route), {endpoint, erlang:element(2, Endpoint), erlang:element(3, Endpoint), erlang:element(4, Endpoint), [{live_route, get, Pattern, View_id, Render} | erlang:element(5, Endpoint)], erlang:element(6, Endpoint)}. -file("src/lightspeed/framework/endpoint.gleam", 115). ?DOC(" Register a GET live route from one router-indexed verified route.\n"). -spec get_live_indexed( endpoint(), lightspeed@framework@verified_routes:indexed_route(any(), lightspeed@framework@verified_routes:get_tag()), binary(), fun((lightspeed@framework@http:conn()) -> binary()) ) -> endpoint(). get_live_indexed(Endpoint, Route, View_id, Render) -> get_live( Endpoint, lightspeed@framework@verified_routes:as_verified_route(Route), View_id, Render ). -file("src/lightspeed/framework/endpoint.gleam", 420). -spec join_graphemes(list(binary())) -> binary(). join_graphemes(Graphemes) -> case Graphemes of [] -> <<""/utf8>>; [Grapheme | Rest] -> <> end. -file("src/lightspeed/framework/endpoint.gleam", 413). -spec drop_last(binary()) -> binary(). drop_last(Path) -> case lists:reverse(gleam@string:to_graphemes(Path)) of [] -> <<""/utf8>>; [_ | Rest] -> _pipe = Rest, _pipe@1 = lists:reverse(_pipe), join_graphemes(_pipe@1) end. -file("src/lightspeed/framework/endpoint.gleam", 406). -spec ends_with_slash(binary()) -> boolean(). ends_with_slash(Path) -> case lists:reverse(gleam@string:to_graphemes(Path)) of [<<"/"/utf8>> | _] -> true; _ -> false end. -file("src/lightspeed/framework/endpoint.gleam", 378). -spec trim_trailing_slashes(binary()) -> binary(). trim_trailing_slashes(Path) -> case Path of <<"/"/utf8>> -> <<"/"/utf8>>; _ -> case ends_with_slash(Path) of true -> trim_trailing_slashes(drop_last(Path)); false -> Path end end. -file("src/lightspeed/framework/endpoint.gleam", 364). -spec normalize_path(binary()) -> binary(). normalize_path(Path) -> No_query = case gleam@string:split(Path, <<"?"/utf8>>) of [First | _] -> First; [] -> Path end, Trimmed = trim_trailing_slashes(No_query), case Trimmed of <<""/utf8>> -> <<"/"/utf8>>; _ -> Trimmed end. -file("src/lightspeed/framework/endpoint.gleam", 125). ?DOC(" Register one static asset.\n"). -spec static(endpoint(), binary(), binary(), binary()) -> endpoint(). static(Endpoint, Path, Content_type, Body) -> {endpoint, erlang:element(2, Endpoint), erlang:element(3, Endpoint), erlang:element(4, Endpoint), erlang:element(5, Endpoint), [{static_asset, normalize_path(Path), Content_type, Body} | erlang:element(6, Endpoint)]}. -file("src/lightspeed/framework/endpoint.gleam", 267). -spec render_live( lightspeed@framework@http:conn(), endpoint(), binary(), fun((lightspeed@framework@http:conn()) -> binary()) ) -> lightspeed@framework@http:conn(). render_live(Conn, Endpoint, View_id, Render) -> Request = {html_request, lightspeed@framework@http:request_session_id(Conn), normalize_path(lightspeed@framework@http:request_path(Conn)), lightspeed@framework@http:request_csrf_token(Conn), lightspeed@framework@http:request_origin(Conn)}, case lightspeed@transport@wisp_html:render_initial( Request, Render(Conn), erlang:element(2, Endpoint), erlang:element(3, Endpoint) ) of {ok, Response} -> _pipe = Conn, _pipe@1 = lightspeed@framework@http:send( _pipe, lightspeed@transport@wisp_html:status(Response), <<"text/html; charset=utf-8"/utf8>>, lightspeed@transport@wisp_html:body(Response) ), lightspeed@framework@http:put_header( _pipe@1, <<"x-lightspeed-view-id"/utf8>>, View_id ); {error, Error} -> lightspeed@framework@controller:internal_error( Conn, lightspeed@transport@contract:error_to_string(Error) ) end. -file("src/lightspeed/framework/endpoint.gleam", 352). -spec route_param_name(binary()) -> gleam@option:option(binary()). route_param_name(Segment) -> case gleam@string:to_graphemes(Segment) of [<<":"/utf8>> | Rest] -> case Rest of [] -> none; _ -> {some, join_graphemes(Rest)} end; _ -> none end. -file("src/lightspeed/framework/endpoint.gleam", 325). -spec match_segments( list(binary()), list(binary()), list(lightspeed@framework@http:route_param()) ) -> gleam@option:option(list(lightspeed@framework@http:route_param())). match_segments(Pattern_segments, Path_segments, Captures_rev) -> case {Pattern_segments, Path_segments} of {[], []} -> {some, lists:reverse(Captures_rev)}; {[], _} -> none; {_, []} -> none; {[Pattern_segment | Pattern_rest], [Path_segment | Path_rest]} -> case route_param_name(Pattern_segment) of {some, Name} -> match_segments( Pattern_rest, Path_rest, [{route_param, Name, Path_segment} | Captures_rev] ); none -> case Pattern_segment =:= Path_segment of true -> match_segments( Pattern_rest, Path_rest, Captures_rev ); false -> none end end end. -file("src/lightspeed/framework/endpoint.gleam", 395). -spec filter_empty(list(binary()), list(binary())) -> list(binary()). filter_empty(Values, Kept_rev) -> case Values of [] -> lists:reverse(Kept_rev); [Value | Rest] -> case Value =:= <<""/utf8>> of true -> filter_empty(Rest, Kept_rev); false -> filter_empty(Rest, [Value | Kept_rev]) end end. -file("src/lightspeed/framework/endpoint.gleam", 389). -spec segments(binary()) -> list(binary()). segments(Path) -> _pipe = Path, _pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>), filter_empty(_pipe@1, []). -file("src/lightspeed/framework/endpoint.gleam", 316). -spec match_pattern(binary(), binary()) -> gleam@option:option(list(lightspeed@framework@http:route_param())). match_pattern(Pattern, Path) -> Pattern_segments = segments(Pattern), Path_segments = segments(Path), match_segments(Pattern_segments, Path_segments, []). -file("src/lightspeed/framework/endpoint.gleam", 312). -spec method_matches( lightspeed@framework@http:method(), lightspeed@framework@http:method() ) -> boolean(). method_matches(Expected, Actual) -> lightspeed@framework@http:method_label(Expected) =:= lightspeed@framework@http:method_label( Actual ). -file("src/lightspeed/framework/endpoint.gleam", 230). -spec find_dispatch_loop( list(route()), lightspeed@framework@http:method(), binary() ) -> gleam@option:option(dispatch()). find_dispatch_loop(Routes, Method, Path) -> case Routes of [] -> none; [Route | Rest] -> case Route of {controller_route, Route_method, Pattern, Run} -> case {method_matches(Route_method, Method), match_pattern(Pattern, Path)} of {true, {some, Params}} -> {some, {controller_dispatch, Run, Params}}; {_, _} -> find_dispatch_loop(Rest, Method, Path) end; {live_route, Route_method@1, Pattern@1, View_id, Render} -> case {method_matches(Route_method@1, Method), match_pattern(Pattern@1, Path)} of {true, {some, Params@1}} -> {some, {live_dispatch, View_id, Render, Params@1}}; {_, _} -> find_dispatch_loop(Rest, Method, Path) end end end. -file("src/lightspeed/framework/endpoint.gleam", 224). -spec find_dispatch(endpoint(), lightspeed@framework@http:conn()) -> gleam@option:option(dispatch()). find_dispatch(Endpoint, Conn) -> Method = lightspeed@framework@http:request_method(Conn), Path = normalize_path(lightspeed@framework@http:request_path(Conn)), _pipe = erlang:element(5, Endpoint), _pipe@1 = lists:reverse(_pipe), find_dispatch_loop(_pipe@1, Method, Path). -file("src/lightspeed/framework/endpoint.gleam", 210). -spec find_static(list(static_asset()), binary()) -> gleam@option:option(static_asset()). find_static(Static_assets, Path) -> case Static_assets of [] -> none; [Asset | Rest] -> case erlang:element(2, Asset) =:= Path of true -> {some, Asset}; false -> find_static(Rest, Path) end end. -file("src/lightspeed/framework/endpoint.gleam", 196). -spec serve_static(endpoint(), lightspeed@framework@http:conn()) -> gleam@option:option(lightspeed@framework@http:conn()). serve_static(Endpoint, Conn) -> case lightspeed@framework@http:request_method(Conn) of get -> Path = normalize_path(lightspeed@framework@http:request_path(Conn)), case find_static(erlang:element(6, Endpoint), Path) of {some, Asset} -> {some, lightspeed@framework@http:send( Conn, 200, erlang:element(3, Asset), erlang:element(4, Asset) )}; none -> none end; _ -> none end. -file("src/lightspeed/framework/endpoint.gleam", 185). -spec run_middleware( lightspeed@framework@http:conn(), list(fun((lightspeed@framework@http:conn()) -> lightspeed@framework@http:conn())) ) -> lightspeed@framework@http:conn(). run_middleware(Conn, Reversed) -> case Reversed of [] -> Conn; [Middleware | Rest] -> case lightspeed@framework@http:halted(Conn) of true -> Conn; false -> run_middleware(Middleware(Conn), Rest) end end. -file("src/lightspeed/framework/endpoint.gleam", 142). ?DOC(" Handle one request through middleware and dispatch tables.\n"). -spec call(endpoint(), lightspeed@framework@http:request()) -> lightspeed@framework@http:response(). call(Endpoint, Request) -> Conn = run_middleware( begin _pipe = Request, lightspeed@framework@http:from_request(_pipe) end, erlang:element(4, Endpoint) ), case lightspeed@framework@http:halted(Conn) of true -> lightspeed@framework@http:to_response(Conn); false -> case serve_static(Endpoint, Conn) of {some, Served} -> lightspeed@framework@http:to_response(Served); none -> case find_dispatch(Endpoint, Conn) of {some, {controller_dispatch, Run, Params}} -> _pipe@1 = Conn, _pipe@2 = lightspeed@framework@http:with_route_params( _pipe@1, Params ), _pipe@3 = Run(_pipe@2), lightspeed@framework@http:to_response(_pipe@3); {some, {live_dispatch, View_id, Render, Params@1}} -> _pipe@4 = Conn, _pipe@5 = lightspeed@framework@http:with_route_params( _pipe@4, Params@1 ), _pipe@6 = render_live( _pipe@5, Endpoint, View_id, Render ), lightspeed@framework@http:to_response(_pipe@6); none -> _pipe@7 = Conn, _pipe@8 = lightspeed@framework@controller:not_found( _pipe@7 ), lightspeed@framework@http:to_response(_pipe@8) end end end. -file("src/lightspeed/framework/endpoint.gleam", 303). -spec route_label(route()) -> binary(). route_label(Route) -> case Route of {controller_route, Method, Pattern, _} -> <<<<<<"controller:"/utf8, (lightspeed@framework@http:method_label(Method))/binary>>/binary, ":"/utf8>>/binary, Pattern/binary>>; {live_route, Method@1, Pattern@1, View_id, _} -> <<<<<<<<<<"live:"/utf8, (lightspeed@framework@http:method_label( Method@1 ))/binary>>/binary, ":"/utf8>>/binary, Pattern@1/binary>>/binary, ":"/utf8>>/binary, View_id/binary>> end. -file("src/lightspeed/framework/endpoint.gleam", 172). ?DOC(" Stable endpoint route table labels.\n"). -spec route_labels(endpoint()) -> list(binary()). route_labels(Endpoint) -> _pipe = erlang:element(5, Endpoint), _pipe@1 = lists:reverse(_pipe), gleam@list:map(_pipe@1, fun route_label/1). -file("src/lightspeed/framework/endpoint.gleam", 179). ?DOC(" Stable static table labels.\n"). -spec static_labels(endpoint()) -> list(binary()). static_labels(Endpoint) -> _pipe = erlang:element(6, Endpoint), _pipe@1 = lists:reverse(_pipe), gleam@list:map( _pipe@1, fun(Asset) -> <<"static:"/utf8, (erlang:element(2, Asset))/binary>> end ).