2525from idom .core .types import RootComponentConstructor
2626
2727from ._asgi import serve_development_asgi
28- from .utils import CLIENT_BUILD_DIR
28+ from .utils import CLIENT_BUILD_DIR , client_build_dir_path
2929
3030
3131logger = logging .getLogger (__name__ )
3232
33- RequestContext : type [Context [request .Request | None ]] = create_context (
33+ ConnectionContext : type [Context [request .Request | None ]] = create_context (
3434 None , "RequestContext"
3535)
3636
@@ -65,9 +65,9 @@ async def serve_development_app(
6565 await serve_development_asgi (app , host , port , started )
6666
6767
68- def use_request () -> request . Request :
68+ def use_connection () -> Connection :
6969 """Get the current ``Request``"""
70- request = use_context (RequestContext )
70+ request = use_context (ConnectionContext )
7171 if request is None :
7272 raise RuntimeError ( # pragma: no cover
7373 "No request. Are you running with a Sanic server?"
@@ -77,7 +77,7 @@ def use_request() -> request.Request:
7777
7878def use_scope () -> ASGIScope :
7979 """Get the current ASGI scope"""
80- app = use_request () .app
80+ app = use_connection (). request .app
8181 try :
8282 asgi_app = app ._asgi_app
8383 except AttributeError : # pragma: no cover
@@ -112,7 +112,16 @@ def _setup_common_routes(blueprint: Blueprint, options: Options) -> None:
112112 CORS (blueprint , ** cors_params )
113113
114114 if options .serve_static_files :
115- blueprint .static ("/app" , str (CLIENT_BUILD_DIR ))
115+
116+ @blueprint .route ("/app" )
117+ @blueprint .route ("/app/<path:path>" )
118+ async def single_page_app_files (
119+ request : request .Request , path : str = ""
120+ ) -> response .HTTPResponse :
121+ return await response .file (
122+ str (CLIENT_BUILD_DIR / client_build_dir_path (path ))
123+ )
124+
116125 blueprint .static ("/modules" , str (IDOM_WEB_MODULES_DIR .current ))
117126
118127 if options .redirect_root :
@@ -121,26 +130,44 @@ def _setup_common_routes(blueprint: Blueprint, options: Options) -> None:
121130 def redirect_to_index (
122131 request : request .Request ,
123132 ) -> response .HTTPResponse :
124- return response .redirect (
125- f"{ blueprint .url_prefix } /client/index.html?{ request .query_string } "
126- )
133+ if request .query_string :
134+ path = f"{ blueprint .url_prefix } /app?{ request .query_string } "
135+ else :
136+ path = f"{ blueprint .url_prefix } /app"
137+ return response .redirect (path )
127138
128139
129140def _setup_single_view_dispatcher_route (
130141 blueprint : Blueprint , constructor : RootComponentConstructor
131142) -> None :
132- @blueprint .websocket ("/app<path:path>/_stream" ) # type: ignore
143+ @blueprint .websocket ("/app/_stream" )
144+ @blueprint .websocket ("/app/<path:path>/_stream" ) # type: ignore
133145 async def model_stream (
134- request : request .Request , socket : WebSocketCommonProtocol
146+ request : request .Request , socket : WebSocketCommonProtocol , path : str = ""
135147 ) -> None :
136148 send , recv = _make_send_recv_callbacks (socket )
149+ conn = Connection (request , socket , path )
137150 await serve_json_patch (
138- Layout (RequestContext (constructor (), value = request )),
151+ Layout (ConnectionContext (constructor (), value = conn )),
139152 send ,
140153 recv ,
141154 )
142155
143156
157+ @dataclass
158+ class Connection :
159+ """A simple wrapper for holding"""
160+
161+ request : request .Request
162+ """The current request object"""
163+
164+ socket : WebSocketCommonProtocol
165+ """A handle to the current websocket"""
166+
167+ path : str
168+ """The current path being served"""
169+
170+
144171def _make_send_recv_callbacks (
145172 socket : WebSocketCommonProtocol ,
146173) -> Tuple [SendCoroutine , RecvCoroutine ]:
0 commit comments