Skip to main content

forma_server/
lib.rs

1//! Axum middleware for Forma SSR.
2//!
3//! Provides page rendering ([`try_render`]), content-hashed asset serving,
4//! CSP headers with cryptographic nonces, an opt-in service worker ([`sw`]), and
5//! the Phase 1 (client mount) / Phase 2 (SSR reconcile) rendering pipeline.
6//!
7//! # Quick Start
8//!
9//! ```rust,no_run
10//! use forma_server::{try_render, PageConfig, PageResponse, RenderCtx, RenderError};
11//! ```
12//!
13//! # Serving a page
14//!
15//! [`try_render`] returns the page or a coded [`RenderError`]; the handler
16//! chooses the response. A page's HTML embeds a per-request nonce
17//! that only its policy blesses, so the HTML, the policy and
18//! `Cache-Control: no-store` travel together: a [`PageResponse`], as axum's
19//! `IntoResponse`, writes all of them and cannot be sent without them.
20//! A handler that needs a looser policy adds typed
21//! [`PolicyExtensions`] through its [`RenderCtx`]; it never edits the
22//! policy's text.
23//!
24//! ```rust,no_run
25//! use axum::http::StatusCode;
26//! use axum::response::{IntoResponse, Response};
27//! use forma_server::{try_render, PageConfig, RenderCtx};
28//!
29//! fn home(config: &PageConfig) -> Response {
30//!     match try_render(config, &RenderCtx::new()) {
31//!         Ok(page) => page.into_response(),
32//!         Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
33//!     }
34//! }
35//! ```
36//!
37//! Markup the page carries unescaped is a [`RawHtml`], made only by
38//! [`RawHtml::trusted`], so a search for that name finds every raw entry.
39//! The route's `modulepreload` links come from the build's manifest
40//! and head links have a typed field, both through [`RenderCtx`]; data goes
41//! through `config_json`.
42//!
43//! Content nobody reviewed renders in the preview frame, whose document
44//! [`PageResponse::frame`] writes ([`frame`]).
45//!
46//! A route the build declares `ssr: false` is served by
47//! [`render_browser_shell`]. At start, [`load_ir_modules_strict`] is the
48//! module loader: stop on its errors, before the port binds. [`IrModules`]
49//! loads the same way and, in a debug build, rereads a page's module when the
50//! build's watch mode rewrites it. Deprecated in
51//! 0.2.2, removed in 3.0.0: `render_page` and its Phase 1 fallback,
52//! `render_page_with_nonce`, the lenient `load_ir_modules`, [`PageOutput`]'s
53//! public fields and `headers()`, and `PageConfig::config_script`.
54
55// Makes README.md's and SECURITY.md's "Zero `unsafe` blocks" a property the
56// compiler enforces rather than a claim.
57#![forbid(unsafe_code)]
58// No `unwrap`, `expect` or `panic!` in the code that ships (DR-12, SR44): a
59// render returns its failure. Tests may. The 0.2.1 functions whose signatures
60// have no room for an error (`CspNonce::generate`, `assets::asset`, the
61// deprecated `assets::load_manifest`) keep their documented panics under a
62// scoped `allow`, each with its fallible twin; none is on the render path.
63#![cfg_attr(
64    not(test),
65    deny(clippy::unwrap_used, clippy::expect_used, clippy::panic)
66)]
67
68/// Serving content-hashed files out of the embedded `dist` directory.
69pub mod assets;
70/// Content-Security-Policy construction and nonce generation.
71pub mod csp;
72/// Doctests pinning each deprecation; built only by `cargo test --doc`.
73#[cfg(doctest)]
74mod deprecations;
75// Documented by its own `//!` docs (the preview frame, DR-14 tier (c)).
76pub mod frame;
77/// Typed homes for the page's head: manifest preloads and head links.
78pub mod head;
79/// Loading and version-checking the per-route `.ir` modules at startup.
80pub mod ir_compat;
81/// Raw HTML: the one named type for markup written unescaped.
82pub mod raw;
83/// The render that returns its failure: `try_render`, `render_browser_shell`.
84pub mod render;
85/// The page response: HTML that never leaves without its policy.
86pub mod response;
87// Documented by its own `//!` docs: an outer `///` line here would merge with
88// them and resolve their intra-doc links from the crate root.
89pub mod sw;
90/// The HTML page template and the SSR render entry points.
91pub mod template;
92/// Request/response types: page config, manifest, render mode.
93pub mod types;
94
95/// Re-exported because it appears in [`assets::asset_bytes`]'s signature.
96pub use axum::body::Bytes;
97pub use csp::{
98    build_csp_header, generate_csp_nonce, CspNonce, InvalidNonce, NonceError, PolicyError,
99    PolicyExtensions,
100};
101/// Re-exported: [`RenderCtx::with_limits`] takes it.
102pub use forma_ir::WalkLimits;
103pub use frame::{FrameError, PreviewFrame, FRAME_RUNTIME_ASSET};
104pub use head::{HeadLink, HeadLinkError, ModulePreloads};
105#[allow(deprecated)]
106pub use ir_compat::{load_ir_modules, load_ir_modules_strict, IrLoadError, IrModules};
107pub use raw::RawHtml;
108pub use render::{render_browser_shell, try_render, RenderCtx, RenderError};
109pub use response::{PageParts, PageResponse, RenderCounts};
110#[allow(deprecated)]
111pub use template::{render_page, render_page_with_nonce};
112pub use types::*;