File
Blob: src/client/lib/workspace-route-model.ts
| 1 | import type { WorkspaceAccessMode } from "@/client/stores/workspace-replica"; |
| 2 | |
| 3 | /** |
| 4 | * Resolution state for the `/$workspaceSlug` route. Consumers branch on |
| 5 | * `phase` and read `workspaceId` / `accessMode` only where the shape below |
| 6 | * guarantees them. |
| 7 | * |
| 8 | * Route state carries identity KEYS only (`workspaceId`, slug). Mutable |
| 9 | * workspace payload lives in the replica projection; consumers read it via |
| 10 | * `useWorkspaceReplica(workspaceId)` (and siblings). Cache-vs-live is a |
| 11 | * provider-internal concern used to gate revalidation, not part of the |
| 12 | * public state. |
| 13 | */ |
| 14 | export type WorkspaceRouteState = |
| 15 | /** |
| 16 | * Resolving. `workspaceId` is set when a cached snapshot seeded the initial |
| 17 | * render and is null on a cold start until the first response lands. |
| 18 | */ |
| 19 | | { phase: "loading"; workspaceId: string | null } |
| 20 | /** Workspace resolved and usable. */ |
| 21 | | { |
| 22 | phase: "ready"; |
| 23 | workspaceId: string; |
| 24 | accessMode: WorkspaceAccessMode; |
| 25 | } |
| 26 | /** |
| 27 | * Cached workspace identity exists but live access could not be confirmed |
| 28 | * (e.g. shared-downgrade probe failed). Page routes can still render from |
| 29 | * cache via the active-page boundary; member-only routes (index, settings) are |
| 30 | * blocked. |
| 31 | */ |
| 32 | | { |
| 33 | phase: "degraded"; |
| 34 | workspaceId: string | null; |
| 35 | workspaceSlug: string; |
| 36 | reason: "stale-shared"; |
| 37 | } |
| 38 | /** Terminal failure; the workspace cannot be rendered. */ |
| 39 | | { phase: "error"; errorKind: "not-found" | "network"; message: string }; |
| 40 | |
| 41 | type ReadyState = Extract<WorkspaceRouteState, { phase: "ready" }>; |
| 42 | type WithWorkspaceId = Exclude<WorkspaceRouteState, { phase: "error" }> & { workspaceId: string }; |
| 43 | |
| 44 | export function isWorkspaceReady(state: WorkspaceRouteState): state is ReadyState { |
| 45 | return state.phase === "ready"; |
| 46 | } |
| 47 | |
| 48 | export function hasWorkspaceIdentity(state: WorkspaceRouteState): state is WithWorkspaceId { |
| 49 | return state.phase !== "error" && state.workspaceId !== null; |
| 50 | } |
| 51 | |
| 52 | /** |
| 53 | * Shell chrome is still resolving identity — render a skeleton sidebar. |
| 54 | * True for `loading` (no workspace yet) and `degraded` (stale-shared probe |
| 55 | * produced partial identity). Consumers outside this module should use this |
| 56 | * helper instead of checking `phase === "degraded"` directly so that route |
| 57 | * shape changes stay local. |
| 58 | */ |
| 59 | export function isResolvingWorkspace(state: WorkspaceRouteState): boolean { |
| 60 | return state.phase === "loading" || state.phase === "degraded"; |
| 61 | } |