60 lines
1.5 KiB
Markdown
60 lines
1.5 KiB
Markdown
# Client State (Strict)
|
|
|
|
This document defines the only allowed usage of client state in `apps/website`.
|
|
|
|
Authoritative contract: [`WEBSITE_CONTRACT.md`](docs/architecture/website/WEBSITE_CONTRACT.md:1).
|
|
|
|
## 1) Core rule
|
|
|
|
Client state is allowed only for **UI concerns**.
|
|
|
|
The API remains the single source of truth.
|
|
|
|
## 2) Allowed client state
|
|
|
|
Client state MAY represent:
|
|
|
|
- selection (table rows, active tab)
|
|
- open/closed dialogs and drawers
|
|
- input values before submission
|
|
- loading flags
|
|
- best-effort optimistic flags
|
|
|
|
## 3) Forbidden client state
|
|
|
|
Client state MUST NOT be used as:
|
|
|
|
- business truth
|
|
- security truth
|
|
- permission truth
|
|
|
|
Examples of forbidden behavior:
|
|
|
|
- client code deciding that a user is allowed based on local flags
|
|
- client code persisting an authoritative list state that overrides server truth
|
|
|
|
## 4) Conflict resolution rule (hard)
|
|
|
|
If client state and API truth disagree, **API truth wins**.
|
|
|
|
Correct handling is:
|
|
|
|
- show the API result
|
|
- revalidate and reload server-rendered truth
|
|
|
|
## 5) Relationship to Blockers
|
|
|
|
Blockers exist to prevent UX mistakes.
|
|
|
|
- Blockers are not security.
|
|
- Blockers may reduce unnecessary requests.
|
|
- The API still enforces rules.
|
|
|
|
See [`docs/architecture/shared/BLOCKERS_AND_GUARDS.md`](docs/architecture/shared/BLOCKERS_AND_GUARDS.md:1) and [`docs/architecture/website/BLOCKERS.md`](docs/architecture/website/BLOCKERS.md:1).
|
|
|
|
## 6) Canonical placement in this repo
|
|
|
|
- `apps/website/lib/blockers/**`
|
|
- `apps/website/lib/hooks/**`
|
|
- `apps/website/lib/command-models/**`
|