1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
//! [`ClientData`] and [`PageProps`]: the browser-exposure traits that are
//! the Client Exposure Firewall.
use PropsSchema;
/// A type explicitly certified as safe to send to the browser.
///
/// `Serialize` alone does **not** make a type browser-safe. Only types that
/// implement `ClientData` may cross the browser boundary through
/// [`Inertia::render_page`](crate::inertia::Inertia::render_page). This is a
/// manual, opt-in declaration: an internal domain model that merely derives
/// `Serialize` cannot accidentally reach the browser, because `render_page`
/// requires `P: ClientData`.
///
/// The `#[page]` and `#[resource]` macros implement this trait from a
/// struct's named fields. The exposure schema they build is the same
/// metadata graph the cross-stack linker and the generated TypeScript
/// contract consume -- one source of truth. Nested exposed values
/// must themselves satisfy this contract; use
/// [`PropsSchema::nested`](super::PropsSchema::nested) (and `nested_array`,
/// `nested_optional`) so the `T: ClientData` bound is checked at the call
/// site.
///
/// # Security note
///
/// The boundary is the explicit `impl ClientData for T` opt-in plus the
/// `render_page` type bound. The exposure schema carries enough to lint
/// field names for secret-bearing words (`password`, `token`, ...), but no
/// command performs that audit -- nothing will catch a secret you certify
/// by hand.
/// Typed metadata for props intentionally associated with an Inertia page.
///
/// Metadata stays explicit: a runtime serialized value never infers it.
/// Every [`ClientData`] type satisfies the [`PageProps`] metadata seam, so
/// the cross-stack linker and the generated TypeScript contract consume
/// one metadata graph rather than two competing systems.