diff --git a/examples/axum_js_ssr/src/main.rs b/examples/axum_js_ssr/src/main.rs index 2c5ae4e802..3787ce4768 100644 --- a/examples/axum_js_ssr/src/main.rs +++ b/examples/axum_js_ssr/src/main.rs @@ -29,6 +29,7 @@ async fn main() { use leptos::logging::log; use leptos::prelude::*; use leptos_axum::{generate_route_list, LeptosRoutes}; + use leptos_meta::ServerMetaContext; latency::LATENCY.get_or_init(|| [0, 4, 40, 400].iter().cycle().into()); latency::ES_LATENCY.get_or_init(|| [0].iter().cycle().into()); @@ -43,7 +44,7 @@ async fn main() { let addr = conf.leptos_options.site_addr; let leptos_options = conf.leptos_options; // Generate the list of routes in your Leptos App - let routes = generate_route_list(App); + let routes = generate_route_list::(App); async fn highlight_js() -> impl IntoResponse { ( @@ -128,11 +129,11 @@ async fn main() { let app = Router::new() .route("/highlight.min.js", get(highlight_js)) - .leptos_routes(&leptos_options, routes, { + .leptos_routes::(&leptos_options, routes, { let leptos_options = leptos_options.clone(); move || shell(leptos_options.clone()) }) - .fallback(leptos_axum::file_and_error_handler(shell)) + .fallback(leptos_axum::file_and_error_handler::<_, ServerMetaContext, _>(shell)) .layer(middleware::from_fn(latency_for_highlight_js)) .with_state(leptos_options); diff --git a/integrations/axum/Cargo.toml b/integrations/axum/Cargo.toml index 3a07cb57d6..08baba3a92 100644 --- a/integrations/axum/Cargo.toml +++ b/integrations/axum/Cargo.toml @@ -19,7 +19,6 @@ futures = "0.3.30" leptos = { workspace = true, features = ["nonce", "ssr"] } server_fn = { workspace = true, features = ["axum-no-default"] } leptos_macro = { workspace = true, features = ["axum"] } -leptos_meta = { workspace = true, features = ["ssr"] } leptos_router = { workspace = true, features = ["ssr"] } leptos_integration_utils = { workspace = true } once_cell = "1" diff --git a/integrations/axum/src/lib.rs b/integrations/axum/src/lib.rs index f0450bcfb0..6ea603a5dd 100644 --- a/integrations/axum/src/lib.rs +++ b/integrations/axum/src/lib.rs @@ -57,9 +57,8 @@ use leptos::{ IntoView, }; use leptos_integration_utils::{ - BoxedFnOnce, ExtendResponse, PinnedFuture, PinnedStream, + BoxedFnOnce, ExtendResponse, PinnedFuture, PinnedStream, ServerExtension, }; -use leptos_meta::ServerMetaContext; #[cfg(feature = "default")] use leptos_router::static_routes::ResolvedStaticPath; use leptos_router::{ @@ -489,7 +488,7 @@ pub type PinnedHtmlStream = feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] -pub fn render_app_to_stream( +pub fn render_app_to_stream( app_fn: impl Fn() -> IV + Clone + Send + 'static, ) -> impl Fn( Request, @@ -498,9 +497,10 @@ pub fn render_app_to_stream( + Send + 'static where + I: ServerExtension, IV: IntoView + 'static, { - render_app_to_stream_with_context(|| {}, app_fn) + render_app_to_stream_with_context::(|| {}, app_fn) } /// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries @@ -512,7 +512,7 @@ where feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] -pub fn render_route( +pub fn render_route( paths: Vec, app_fn: impl Fn() -> IV + Clone + Send + 'static, ) -> impl Fn( @@ -523,11 +523,12 @@ pub fn render_route( + Send + 'static where + I: ServerExtension, IV: IntoView + 'static, LeptosOptions: FromRef, S: Send + 'static, { - render_route_with_context(paths, || {}, app_fn) + render_route_with_context::<_, I, _>(paths, || {}, app_fn) } /// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries @@ -584,7 +585,7 @@ where feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] -pub fn render_app_to_stream_in_order( +pub fn render_app_to_stream_in_order( app_fn: impl Fn() -> IV + Clone + Send + 'static, ) -> impl Fn( Request, @@ -593,9 +594,10 @@ pub fn render_app_to_stream_in_order( + Send + 'static where + I: ServerExtension, IV: IntoView + 'static, { - render_app_to_stream_in_order_with_context(|| {}, app_fn) + render_app_to_stream_in_order_with_context::(|| {}, app_fn) } /// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries @@ -638,7 +640,7 @@ where feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] -pub fn render_app_to_stream_with_context( +pub fn render_app_to_stream_with_context( additional_context: impl Fn() + 'static + Clone + Send, app_fn: impl Fn() -> IV + Clone + Send + 'static, ) -> impl Fn( @@ -648,9 +650,10 @@ pub fn render_app_to_stream_with_context( + Send + 'static where + I: ServerExtension, IV: IntoView + 'static, { - render_app_to_stream_with_context_and_replace_blocks( + render_app_to_stream_with_context_and_replace_blocks::( additional_context, app_fn, false, @@ -666,7 +669,7 @@ where feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] -pub fn render_route_with_context( +pub fn render_route_with_context( paths: Vec, additional_context: impl Fn() + 'static + Clone + Send, app_fn: impl Fn() -> IV + Clone + Send + 'static, @@ -682,20 +685,20 @@ where LeptosOptions: FromRef, S: Send + 'static, { - let ooo = render_app_to_stream_with_context( + let ooo = render_app_to_stream_with_context::( additional_context.clone(), app_fn.clone(), ); - let pb = render_app_to_stream_with_context_and_replace_blocks( + let pb = render_app_to_stream_with_context_and_replace_blocks::( additional_context.clone(), app_fn.clone(), true, ); - let io = render_app_to_stream_in_order_with_context( + let io = render_app_to_stream_in_order_with_context::( additional_context.clone(), app_fn.clone(), ); - let asyn = render_app_async_stream_with_context( + let asyn = render_app_async_stream_with_context::( additional_context.clone(), app_fn.clone(), ); @@ -727,7 +730,7 @@ where #[cfg(feature = "default")] { let regenerate = listing.regenerate.clone(); - handle_static_route( + handle_static_route::<_, I, _>( additional_context.clone(), app_fn.clone(), regenerate, @@ -770,7 +773,10 @@ where feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] -pub fn render_app_to_stream_with_context_and_replace_blocks( +pub fn render_app_to_stream_with_context_and_replace_blocks< + I: ServerExtension, + IV, +>( additional_context: impl Fn() + 'static + Clone + Send, app_fn: impl Fn() -> IV + Clone + Send + 'static, replace_blocks: bool, @@ -784,7 +790,7 @@ where IV: IntoView + 'static, { _ = replace_blocks; // TODO - handle_response(additional_context, app_fn, |app, chunks| { + handle_response::(additional_context, app_fn, |app, chunks| { Box::pin(async move { let app = if cfg!(feature = "dont-use-islands-router") { app.to_html_stream_out_of_order_branching() @@ -838,7 +844,7 @@ where feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] -pub fn render_app_to_stream_in_order_with_context( +pub fn render_app_to_stream_in_order_with_context( additional_context: impl Fn() + 'static + Clone + Send, app_fn: impl Fn() -> IV + Clone + Send + 'static, ) -> impl Fn( @@ -850,7 +856,7 @@ pub fn render_app_to_stream_in_order_with_context( where IV: IntoView + 'static, { - handle_response(additional_context, app_fn, |app, chunks| { + handle_response::(additional_context, app_fn, |app, chunks| { let app = if cfg!(feature = "dont-use-islands-router") { app.to_html_stream_in_order_branching() } else { @@ -862,7 +868,7 @@ where }) } -fn handle_response( +fn handle_response( additional_context: impl Fn() + 'static + Clone + Send, app_fn: impl Fn() -> IV + Clone + Send + 'static, stream_builder: fn( @@ -876,11 +882,16 @@ where move |req: Request| { let app_fn = app_fn.clone(); let additional_context = additional_context.clone(); - handle_response_inner(additional_context, app_fn, req, stream_builder) + handle_response_inner::( + additional_context, + app_fn, + req, + stream_builder, + ) } } -fn handle_response_inner( +fn handle_response_inner( additional_context: impl Fn() + 'static + Clone + Send, app_fn: impl FnOnce() -> IV + Send + 'static, req: Request, @@ -890,12 +901,13 @@ fn handle_response_inner( ) -> PinnedFuture>, ) -> PinnedFuture> where + I: ServerExtension, IV: IntoView + 'static, { Box::pin(async move { let add_context = additional_context.clone(); let res_options = ResponseOptions::default(); - let (meta_context, meta_output) = ServerMetaContext::new(); + let (meta_context, meta_output) = I::new(); let additional_context = { let meta_context = meta_context.clone(); @@ -907,9 +919,9 @@ where let full_path = format!("http://leptos.dev{path}"); let (_, req_parts) = generate_request_and_parts(req); - provide_contexts( + provide_contexts::( &full_path, - &meta_context, + meta_context, req_parts, res_options.clone(), ); @@ -917,7 +929,7 @@ where } }; - let res = AxumResponse::from_app( + let res = AxumResponse::from_app::( app_fn, meta_output, additional_context, @@ -934,14 +946,14 @@ where feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] -fn provide_contexts( +fn provide_contexts( path: &str, - meta_context: &ServerMetaContext, + injected_context: I::Context, parts: Parts, default_res_options: ResponseOptions, ) { provide_context(RequestUrl::new(path)); - provide_context(meta_context.clone()); + I::provide_context(injected_context); provide_context(parts); provide_context(default_res_options); provide_server_redirect(redirect); @@ -1002,7 +1014,7 @@ fn provide_contexts( feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] -pub fn render_app_async( +pub fn render_app_async( app_fn: impl Fn() -> IV + Clone + Send + 'static, ) -> impl Fn( Request, @@ -1013,7 +1025,7 @@ pub fn render_app_async( where IV: IntoView + 'static, { - render_app_async_with_context(|| {}, app_fn) + render_app_async_with_context::(|| {}, app_fn) } /// Returns an Axum [Handler](axum::handler::Handler) that listens for a `GET` request and tries @@ -1057,7 +1069,7 @@ where feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] -pub fn render_app_async_stream_with_context( +pub fn render_app_async_stream_with_context( additional_context: impl Fn() + 'static + Clone + Send, app_fn: impl Fn() -> IV + Clone + Send + 'static, ) -> impl Fn( @@ -1069,7 +1081,7 @@ pub fn render_app_async_stream_with_context( where IV: IntoView + 'static, { - handle_response(additional_context, app_fn, |app, chunks| { + handle_response::(additional_context, app_fn, |app, chunks| { Box::pin(async move { let app = if cfg!(feature = "dont-use-islands-router") { app.to_html_stream_in_order_branching() @@ -1125,7 +1137,7 @@ where feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] -pub fn render_app_async_with_context( +pub fn render_app_async_with_context( additional_context: impl Fn() + 'static + Clone + Send, app_fn: impl Fn() -> IV + Clone + Send + 'static, ) -> impl Fn( @@ -1137,7 +1149,7 @@ pub fn render_app_async_with_context( where IV: IntoView + 'static, { - handle_response(additional_context, app_fn, async_stream_builder) + handle_response::(additional_context, app_fn, async_stream_builder) } fn async_stream_builder( @@ -1166,13 +1178,13 @@ where feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] -pub fn generate_route_list( +pub fn generate_route_list( app_fn: impl Fn() -> IV + 'static + Clone + Send, ) -> Vec where IV: IntoView + 'static, { - generate_route_list_with_exclusions_and_ssg(app_fn, None).0 + generate_route_list_with_exclusions_and_ssg::(app_fn, None).0 } /// Generates a list of all routes defined in Leptos's Router in your app. We can then use t.clone()his to automatically @@ -1182,13 +1194,13 @@ where feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] -pub fn generate_route_list_with_ssg( +pub fn generate_route_list_with_ssg( app_fn: impl Fn() -> IV + 'static + Clone + Send, ) -> (Vec, StaticRouteGenerator) where IV: IntoView + 'static, { - generate_route_list_with_exclusions_and_ssg(app_fn, None) + generate_route_list_with_exclusions_and_ssg::(app_fn, None) } /// Generates a list of all routes defined in Leptos's Router in your app. We can then use this to automatically @@ -1199,14 +1211,15 @@ where feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] -pub fn generate_route_list_with_exclusions( +pub fn generate_route_list_with_exclusions( app_fn: impl Fn() -> IV + 'static + Clone + Send, excluded_routes: Option>, ) -> Vec where IV: IntoView + 'static, { - generate_route_list_with_exclusions_and_ssg(app_fn, excluded_routes).0 + generate_route_list_with_exclusions_and_ssg::(app_fn, excluded_routes) + .0 } /// Builds all routes that have been defined using [`StaticRoute`]. @@ -1243,14 +1256,14 @@ pub async fn build_static_routes( feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] -pub fn generate_route_list_with_exclusions_and_ssg( +pub fn generate_route_list_with_exclusions_and_ssg( app_fn: impl Fn() -> IV + 'static + Clone + Send, excluded_routes: Option>, ) -> (Vec, StaticRouteGenerator) where IV: IntoView + 'static, { - generate_route_list_with_exclusions_and_ssg_and_context( + generate_route_list_with_exclusions_and_ssg_and_context::( app_fn, excluded_routes, || {}, @@ -1339,12 +1352,13 @@ impl AxumRouteListing { feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] -pub fn generate_route_list_with_exclusions_and_ssg_and_context( +pub fn generate_route_list_with_exclusions_and_ssg_and_context( app_fn: impl Fn() -> IV + Clone + Send + 'static, excluded_routes: Option>, additional_context: impl Fn() + Clone + Send + 'static, ) -> (Vec, StaticRouteGenerator) where + I: ServerExtension, IV: IntoView + 'static, { // do some basic reactive setup @@ -1361,14 +1375,19 @@ where // stub out a path for now provide_context(RequestUrl::new("")); let (mock_parts, _) = Request::new(Body::from("")).into_parts(); - let (mock_meta, _) = ServerMetaContext::new(); - provide_contexts("", &mock_meta, mock_parts, Default::default()); + let (mock_context, _) = I::new(); + provide_contexts::( + "", + mock_context, + mock_parts, + Default::default(), + ); additional_context(); RouteList::generate(&app_fn) }) .unwrap_or_default(); - let generator = StaticRouteGenerator::new( + let generator = StaticRouteGenerator::new::( &routes, app_fn.clone(), additional_context.clone(), @@ -1409,12 +1428,14 @@ pub struct StaticRouteGenerator( impl StaticRouteGenerator { #[cfg(feature = "default")] - fn render_route( + fn render_route( path: String, app_fn: impl Fn() -> IV + Clone + Send + 'static, additional_context: impl Fn() + Clone + Send + 'static, ) -> impl Future { - let (meta_context, meta_output) = ServerMetaContext::new(); + use leptos_integration_utils::ResponseStreamInjector as _; + + let (meta_context, meta_output) = I::new(); let additional_context = { let add_context = additional_context.clone(); move || { @@ -1426,9 +1447,9 @@ impl StaticRouteGenerator { .unwrap(); let (mock_parts, _) = mock_req.into_parts(); let res_options = ResponseOptions::default(); - provide_contexts( + provide_contexts::( &full_path, - &meta_context, + meta_context, mock_parts, res_options, ); @@ -1451,7 +1472,7 @@ impl StaticRouteGenerator { } let html = meta_output - .inject_meta_context(stream) + .inject_response(stream) .await .collect::() .await; @@ -1460,12 +1481,13 @@ impl StaticRouteGenerator { } /// Creates a new static route generator from the given list of route definitions. - pub fn new( + pub fn new( routes: &RouteList, app_fn: impl Fn() -> IV + Clone + Send + 'static, additional_context: impl Fn() + Clone + Send + 'static, ) -> Self where + I: ServerExtension, IV: IntoView + 'static, { #[cfg(feature = "default")] @@ -1479,7 +1501,7 @@ impl StaticRouteGenerator { Box::pin(routes.generate_static_files( move |path: &ResolvedStaticPath| { - Self::render_route( + Self::render_route::( path.to_string(), app_fn.clone(), additional_context.clone(), @@ -1578,7 +1600,7 @@ async fn write_static_route( } #[cfg(feature = "default")] -fn handle_static_route( +fn handle_static_route( additional_context: impl Fn() + 'static + Clone + Send, app_fn: impl Fn() -> IV + Clone + Send + 'static, regenerate: Vec, @@ -1592,6 +1614,7 @@ fn handle_static_route( where LeptosOptions: FromRef, S: Send + 'static, + I: ServerExtension, IV: IntoView + 'static, { use tower_http::services::ServeFile; @@ -1613,7 +1636,7 @@ where let (owner, html) = path .build( move |path: &ResolvedStaticPath| { - StaticRouteGenerator::render_route( + StaticRouteGenerator::render_route::( path.to_string(), app_fn.clone(), additional_context.clone(), @@ -1678,16 +1701,17 @@ where S: Clone + Send + Sync + 'static, LeptosOptions: FromRef, { - fn leptos_routes( + fn leptos_routes( self, options: &S, paths: Vec, app_fn: impl Fn() -> IV + Clone + Send + 'static, ) -> Self where - IV: IntoView + 'static; + IV: IntoView + 'static, + I: ServerExtension; - fn leptos_routes_with_context( + fn leptos_routes_with_context( self, options: &S, paths: Vec, @@ -1695,7 +1719,8 @@ where app_fn: impl Fn() -> IV + Clone + Send + 'static, ) -> Self where - IV: IntoView + 'static; + IV: IntoView + 'static, + I: ServerExtension; fn leptos_routes_with_handler( self, @@ -1756,23 +1781,24 @@ where feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] - fn leptos_routes( + fn leptos_routes( self, state: &S, paths: Vec, app_fn: impl Fn() -> IV + Clone + Send + 'static, ) -> Self where + I: ServerExtension, IV: IntoView + 'static, { - self.leptos_routes_with_context(state, paths, || {}, app_fn) + self.leptos_routes_with_context::(state, paths, || {}, app_fn) } #[cfg_attr( feature = "tracing", tracing::instrument(level = "trace", fields(error), skip_all) )] - fn leptos_routes_with_context( + fn leptos_routes_with_context( self, state: &S, paths: Vec, @@ -1833,7 +1859,7 @@ where { router.route( path, - get(handle_static_route( + get(handle_static_route::<_, I, _>( cx_with_state_and_method.clone(), app_fn.clone(), listing.regenerate.clone(), @@ -1852,7 +1878,7 @@ where path, match listing.mode() { SsrMode::OutOfOrder => { - let s = render_app_to_stream_with_context( + let s = render_app_to_stream_with_context::( cx_with_state_and_method.clone(), app_fn.clone(), ); @@ -1865,7 +1891,7 @@ where } } SsrMode::PartiallyBlocked => { - let s = render_app_to_stream_with_context_and_replace_blocks( + let s = render_app_to_stream_with_context_and_replace_blocks::( cx_with_state_and_method.clone(), app_fn.clone(), true @@ -1879,7 +1905,7 @@ where } } SsrMode::InOrder => { - let s = render_app_to_stream_in_order_with_context( + let s = render_app_to_stream_in_order_with_context::( cx_with_state_and_method.clone(), app_fn.clone(), ); @@ -1892,7 +1918,7 @@ where } } SsrMode::Async => { - let s = render_app_async_with_context( + let s = render_app_async_with_context::( cx_with_state_and_method.clone(), app_fn.clone(), ); @@ -2001,7 +2027,7 @@ where } #[cfg(feature = "default")] -pub fn file_and_error_handler( +pub fn file_and_error_handler( shell: fn(LeptosOptions) -> IV, ) -> impl Fn( Uri, @@ -2012,6 +2038,7 @@ pub fn file_and_error_handler( + Send + 'static where + I: ServerExtension, IV: IntoView + 'static, S: Send + 'static, LeptosOptions: FromRef, @@ -2025,7 +2052,7 @@ where if res.status() == StatusCode::OK { res.into_response() } else { - let mut res = handle_response_inner( + let mut res = handle_response_inner::( || {}, move || shell(options), req, diff --git a/integrations/utils/src/lib.rs b/integrations/utils/src/lib.rs index ab0d199a35..16e09e7415 100644 --- a/integrations/utils/src/lib.rs +++ b/integrations/utils/src/lib.rs @@ -2,17 +2,99 @@ use futures::{stream::once, Stream, StreamExt}; use hydration_context::{SharedContext, SsrSharedContext}; use leptos::{ nonce::use_nonce, + prelude::provide_context, reactive::owner::{Owner, Sandboxed}, IntoView, }; use leptos_config::LeptosOptions; -use leptos_meta::ServerMetaContextOutput; +use leptos_meta::{ServerMetaContext, ServerMetaContextOutput}; use std::{future::Future, pin::Pin, sync::Arc}; pub type PinnedStream = Pin + Send>>; pub type PinnedFuture = Pin + Send>>; pub type BoxedFnOnce = Box T + Send>; +pub trait ResponseStreamInjector { + fn inject_response( + self, + stream: impl Stream + Send + Unpin, + ) -> impl Future + Send> + Send; +} + +pub trait ServerExtension: 'static { + type Context: Send + Sync + Clone + 'static; + type Data: Send + ResponseStreamInjector + 'static; + fn new() -> (Self::Context, Self::Data); + fn provide_context(context: Self::Context) { + provide_context(context); + } +} + +macro_rules! impl_tuples { + ($($cur:ident)*) => { + #[allow(non_snake_case)] + impl<$($cur: ResponseStreamInjector + Send,)*> ResponseStreamInjector for ($($cur,)*) { + async fn inject_response( + self, + stream: impl Stream + Send + Unpin, + ) -> impl Stream + Send { + let ($($cur,)*) = self; + // Probably it might be useful sometimes to inject + // response context in reverse order + $( + let stream = Box::pin($cur.inject_response(stream).await); + )* + stream + } + } + #[allow(non_snake_case)] + impl<$($cur: ServerExtension,)*> ServerExtension for ($($cur,)*) { + type Context = ($($cur::Context,)*); + type Data = ($($cur::Data,)*); + fn new() -> (Self::Context, Self::Data) { + let ($($cur,)*) = ($($cur::new(),)*); + ( + ($($cur.0,)*), + ($($cur.1,)*), + ) + } + fn provide_context(context: Self::Context) { + let ($($cur,)*) = context; + $( + $cur::provide_context($cur); + )* + } + } + }; + ($($cur:ident)* @ $c:ident $($rest:ident)*) => { + impl_tuples!($($cur)*); + impl_tuples!($($cur)* $c @ $($rest)*); + }; + ($($cur:ident)* @) => { + impl_tuples!($($cur)*); + }; +} + +impl_tuples!(@ A B C D E F); + +impl ResponseStreamInjector for ServerMetaContextOutput { + async fn inject_response( + self, + stream: impl Stream + Send + Unpin, + ) -> impl Stream + Send { + self.inject_meta_context(stream).await + } +} + +impl ServerExtension for ServerMetaContext { + type Context = ServerMetaContext; + type Data = ServerMetaContextOutput; + + fn new() -> (Self::Context, Self::Data) { + Self::new() + } +} + pub trait ExtendResponse: Sized { type ResponseOptions: Send; @@ -23,9 +105,9 @@ pub trait ExtendResponse: Sized { fn set_default_content_type(&mut self, content_type: &str); - fn from_app( + fn from_app( app_fn: impl FnOnce() -> IV + Send + 'static, - meta_context: ServerMetaContextOutput, + meta_context: I::Data, additional_context: impl FnOnce() + Send + 'static, res_options: Self::ResponseOptions, stream_builder: fn( @@ -48,7 +130,7 @@ pub trait ExtendResponse: Sized { } let mut stream = - Box::pin(meta_context.inject_meta_context(stream).await); + Box::pin(meta_context.inject_response(stream).await); // wait for the first chunk of the stream, then set the status and headers let first_chunk = stream.next().await.unwrap_or_default();