feat: local resources with .await

This commit is contained in:
Greg Johnston
2024-07-04 15:39:42 -04:00
parent 87f9fa23d5
commit 0cf3113812
12 changed files with 723 additions and 130 deletions

View File

@@ -84,4 +84,12 @@ impl SharedContext for CsrSharedContext {
fn await_deferred(&self) -> Option<PinnedFuture<()>> {
None
}
#[inline(always)]
fn set_incomplete_chunk(&self, _id: SerializedDataId) {}
#[inline(always)]
fn get_incomplete_chunk(&self, _id: &SerializedDataId) -> bool {
false
}
}

View File

@@ -15,6 +15,8 @@ extern "C" {
static __RESOLVED_RESOURCES: Array;
static __SERIALIZED_ERRORS: Array;
static __INCOMPLETE_CHUNKS: Array;
}
fn serialized_errors() -> Vec<(SerializedDataId, ErrorId, Error)> {
@@ -38,6 +40,16 @@ fn serialized_errors() -> Vec<(SerializedDataId, ErrorId, Error)> {
.collect()
}
fn incomplete_chunks() -> Vec<SerializedDataId> {
__INCOMPLETE_CHUNKS
.iter()
.map(|value| {
let id = value.as_f64().unwrap() as usize;
SerializedDataId(id)
})
.collect()
}
/// An error that has been serialized across the network boundary.
#[derive(Debug, Clone)]
struct SerializedError(String);
@@ -57,6 +69,7 @@ pub struct HydrateSharedContext {
is_hydrating: AtomicBool,
during_hydration: AtomicBool,
errors: Lazy<Vec<(SerializedDataId, ErrorId, Error)>>,
incomplete: Lazy<Vec<SerializedDataId>>,
}
impl HydrateSharedContext {
@@ -67,6 +80,7 @@ impl HydrateSharedContext {
is_hydrating: AtomicBool::new(true),
during_hydration: AtomicBool::new(true),
errors: Lazy::new(serialized_errors),
incomplete: Lazy::new(incomplete_chunks),
}
}
@@ -80,6 +94,7 @@ impl HydrateSharedContext {
is_hydrating: AtomicBool::new(false),
during_hydration: AtomicBool::new(true),
errors: Lazy::new(serialized_errors),
incomplete: Lazy::new(incomplete_chunks),
}
}
}
@@ -166,4 +181,11 @@ impl SharedContext for HydrateSharedContext {
fn await_deferred(&self) -> Option<PinnedFuture<()>> {
None
}
#[inline(always)]
fn set_incomplete_chunk(&self, _id: SerializedDataId) {}
fn get_incomplete_chunk(&self, id: &SerializedDataId) -> bool {
self.incomplete.iter().any(|entry| entry == id)
}
}

View File

@@ -140,4 +140,11 @@ pub trait SharedContext: Debug {
///
/// In browser implementations, this should be a no-op.
fn await_deferred(&self) -> Option<PinnedFuture<()>>;
/// Tells the client that this chunk is being sent from the server before all its data have
/// loaded, and it may be in a fallback state.
fn set_incomplete_chunk(&self, id: SerializedDataId);
/// Checks whether this chunk is being sent from the server before all its data have loaded.
fn get_incomplete_chunk(&self, id: &SerializedDataId) -> bool;
}

View File

@@ -2,7 +2,7 @@ use super::{SerializedDataId, SharedContext};
use crate::{PinnedFuture, PinnedStream};
use futures::{
future::join_all,
stream::{self},
stream::{self, once},
Stream, StreamExt,
};
use or_poisoned::OrPoisoned;
@@ -33,6 +33,7 @@ pub struct SsrSharedContext {
errors: ErrorBuf,
sealed_error_boundaries: SealedErrors,
deferred: Mutex<Vec<PinnedFuture<()>>>,
incomplete: Arc<Mutex<Vec<SerializedDataId>>>,
}
impl SsrSharedContext {
@@ -178,8 +179,19 @@ impl SharedContext for SsrSharedContext {
sealed_error_boundaries: Arc::clone(&self.sealed_error_boundaries),
};
let stream =
stream::once(async move { initial_chunk }).chain(async_data);
let incomplete = Arc::clone(&self.incomplete);
let stream = stream::once(async move { initial_chunk })
.chain(async_data)
.chain(once(async move {
let mut script = String::new();
script.push_str("__INCOMPLETE_CHUNKS=[");
for chunk in mem::take(&mut *incomplete.lock().or_poisoned()) {
_ = write!(script, "{},", chunk.0);
}
script.push_str("];");
script
}));
Some(Box::pin(stream))
}
@@ -203,6 +215,18 @@ impl SharedContext for SsrSharedContext {
}))
}
}
fn set_incomplete_chunk(&self, id: SerializedDataId) {
self.incomplete.lock().or_poisoned().push(id);
}
fn get_incomplete_chunk(&self, id: &SerializedDataId) -> bool {
self.incomplete
.lock()
.or_poisoned()
.iter()
.any(|entry| entry == id)
}
}
struct AsyncDataStream {