async fn get_static_page_data(
        &self,
        full_path_encoded: &str,
        has_state: bool,
        is_capsule: bool,
    ) -> Result<PageData, ServerError> {
        // Get the partial HTML content and a state to go with it (if applicable)
        let content = if !is_capsule {
            self.immutable_store
                .read(&format!("static/{}.html", full_path_encoded))
                .await?
        } else {
            String::new()
        };
        // This maps all the dependencies for any page that has a prerendered fragment
        let widget_states = if !is_capsule {
            self.immutable_store
                .read(&format!("static/{}.widgets.json", full_path_encoded))
                .await?
        } else {
            "{}".to_string()
        };
        // These are *not* fallible!
        let widget_states = match serde_json::from_str::<
            HashMap<PathMaybeWithLocale, (String, Value)>,
        >(&widget_states)
        {
            // Same processing as the server does
            Ok(widget_states) => widget_states
                .into_iter()
                .map(|(k, (_, v))| (k, Ok(v)))
                .collect::<_>(),
            Err(err) => return Err(ServerError::InvalidPageState { source: err }),
        };
        let head = if !is_capsule {
            self.immutable_store
                .read(&format!("static/{}.head.html", full_path_encoded))
                .await?
        } else {
            String::new()
        };
        let mut state = match has_state {
            true => serde_json::from_str(
                &self
                    .immutable_store
                    .read(&format!("static/{}.json", full_path_encoded))
                    .await?,
            )
            .map_err(|err| ServerError::InvalidPageState { source: err })?,
            false => TemplateState::empty().state,
        };
        // Widget states are always parsed as fallible on the browser-side
        // because initially loaded widgets actually can be. This is a server-side
        // workaround that we have to replicate here.
        if is_capsule {
            state = serde_json::to_value(Ok::<_, ()>(state)).unwrap();
        }
        // Create an instance of `PageData`
        Ok(PageData {
            content,
            state,
            head,
            widget_states,
        })
    }
This will work for capsules by just returning empty values
for the parts of `PageData` that they can't fulfill. Importantly,
capsules will be immediately converted into `PageDataPartial`s by
the caller (since initial load pages don't need to be constructed).
packages/perseus/src/turbine/export.rs