Spaces:
Sleeping
Sleeping
| # MediaRouter Frontend Architecture v2 | |
| ## Purpose | |
| Frontend Architecture v2 is the incremental, feature-oriented foundation for | |
| MediaRouter. It preserves the existing Next.js App Router URLs, authentication | |
| and backend contracts while making the current product areas and future | |
| AI-first workspace modules independently maintainable. | |
| It is intentionally an architecture migration, not a visual redesign or a new | |
| product surface. The MediaRouter Design System remains the sole UI primitive | |
| layer; features compose it and do not recreate buttons, forms, navigation or | |
| feedback elements. | |
| ## Layering | |
| ```text | |
| app routes (thin route and metadata adapters) | |
| | | |
| feature boundary (screen, hooks, schemas, local state, feature API facade) | |
| | | |
| shared application services (auth, query, flags, capabilities, realtime) | |
| | | |
| lib/api/client.ts -> authenticated same-origin BFF -> MediaRouter backend | |
| ``` | |
| All browser requests use the shared Axios client at | |
| `frontend/lib/api/client.ts`, whose base URL is the same-origin | |
| `/api/backend` bridge. UI components must not call `fetch()` directly. | |
| `app/api/backend/[...path]/route.ts` remains the only frontend transport proxy. | |
| It owns authentication forwarding, backend authorization and safe request | |
| forwarding; no frontend API contract changed in this migration. | |
| ## Directory structure | |
| ```text | |
| frontend/ | |
| app/ | |
| (auth)/ authentication routes | |
| (platform)/ preserved workspace URLs during incremental migration | |
| (public)/ public/legal routes | |
| api/ BFF and authentication route handlers | |
| components/ | |
| ui/ layout/ navigation/ feedback/ design-system consumers only | |
| design-system/ tokens, themes, icons, motion and foundations | |
| features/ | |
| auth/ workspace/ templates/ social/ jobs/ media/ developer/ | |
| dashboard/ settings/ operations/ marketplace/ mcp/ | |
| search/ commands/ plugins/ editor/ | |
| lib/ | |
| api/ auth/ query/ capabilities/ feature-flags/ realtime/ config/ | |
| hooks/ cross-feature compatibility or presentation hooks | |
| ``` | |
| The existing `(platform)` route group intentionally remains in place. Route | |
| groups do not affect URLs, so `/dashboard`, `/templates`, `/social`, | |
| `/developer/*`, and every existing bookmark retain their current address. New | |
| workspace routes should be introduced below the agreed `(workspace)` group only | |
| when their corresponding product module is implemented; no duplicate or mock | |
| pages are created by this foundation. | |
| ## Feature boundary | |
| Each feature is the owner of its screen-level components, feature hooks, | |
| schemas/types, local stores, services and an `api/` facade. The facade imports | |
| the shared transport modules from `lib/api`; it does not instantiate a second | |
| HTTP client. This keeps authentication, error handling, timeout and BFF policy | |
| consistent while allowing a feature to control its public data interface. | |
| Current active feature boundaries include: | |
| - `auth`: session hook and protected route integration. | |
| - `workspace`: discovery bootstrap, shared discovery queries, navigation and | |
| workspace chrome state. | |
| - `templates`: browser, detail, runner, preferences store and template API | |
| facade. | |
| - `social`: social screens, query/mutation hooks and social API facade. | |
| - `jobs`: job monitor and reusable job realtime adapter. | |
| - `media`: media input, transfer state and upload service. | |
| - `developer`, `settings`, `operations`, `mcp`, `marketplace`, `dashboard`: | |
| their existing screens behind explicit feature entry points/API facades. | |
| - `search`, `commands`, `plugins`, `editor`: reusable infrastructure with no | |
| new product UI or mock data. | |
| Foundational feature declarations live in `features/manifest.ts`. They let | |
| tooling and navigation identify active, foundation and planned product areas | |
| without turning planned functionality into visible routes. | |
| ## State management | |
| State is deliberately split by lifecycle and ownership: | |
| | State | Location | Rule | | |
| | --- | --- | --- | | |
| | Global workspace chrome | `features/workspace/stores/workspace-ui-store.ts` | sidebar and command-palette visibility only | | |
| | User template preferences | `features/templates/stores/template-preferences-store.ts` | browser-local persisted preferences only | | |
| | Transfers | `features/media/stores/transfer-store.ts` | active browser transfer state only | | |
| | Server data | feature TanStack Query hooks | never copied into Zustand | | |
| | Editor state | `features/editor/index.ts` | one vanilla Zustand store per editor instance | | |
| Authentication remains session-backed through NextAuth and is exposed through | |
| `features/auth/hooks/use-auth.ts`. Workspace/project/server data remains | |
| authoritative on the backend. Do not create a catch-all Zustand store. | |
| ## API and TanStack Query policy | |
| `lib/api/errors.ts` converts transport failures into a safe `MediaRouterApiError` | |
| and normalized categories: authentication, authorization, validation, | |
| not-found, rate-limited, offline, timeout, server and unknown. | |
| `lib/query/client.ts` configures one QueryClient with bounded retry/backoff for | |
| retryable transport failures only. Query keys are stable and feature-owned; | |
| shared cross-feature keys live in `lib/query/keys.ts`. Features must: | |
| 1. Call a feature API facade or shared discovery hook, never `fetch()` from a | |
| UI component. | |
| 2. Define a stable query key. | |
| 3. Invalidate only the keys affected by a successful mutation. | |
| 4. Use optimistic updates only with a rollback snapshot. | |
| 5. Surface `normalizeApiError()` through the feedback states rather than raw | |
| server payloads. | |
| The shared feedback layer provides API, validation, permission and offline | |
| error-state building blocks, plus retry banners. Backend error details and | |
| request IDs stay safe to render, but credentials and private upstream targets | |
| never enter the browser. | |
| ## Server and client component strategy | |
| Route pages, metadata and authorization checks remain Server Components by | |
| default. A component is marked client-side only when it needs browser state, | |
| TanStack Query, event handlers, uploads, realtime, drag-and-drop, command | |
| palette interaction, a browser API or animation. | |
| Examples of client-only infrastructure are the app shell, global search, | |
| upload service consumers, job subscriptions, forms and editor stores. New | |
| features should keep route adapters thin, use `Suspense` boundaries around | |
| client data islands, and avoid pulling heavy editor/timeline modules into the | |
| workspace shell. | |
| ## Permissions, capabilities and feature flags | |
| The backend remains authoritative. `lib/auth/permissions.ts` provides the | |
| frontend UX check and the BFF separately checks every forwarded request. | |
| `lib/capabilities/index.ts` evaluates a declarative requirement containing an | |
| optional backend capability, permission and feature flag. Navigation, commands | |
| and plugins use this same resolver. Do not hardcode role checks in components. | |
| `lib/feature-flags/index.ts` is the only environment-backed flag resolver. | |
| `useFeatureFlags()` supplies the UI layer, supports future workspace, | |
| organization and user overrides, and keeps pages from reading environment | |
| variables directly. A backend-provided override can be passed as scoped | |
| metadata when the backend exposes it. | |
| ## Navigation | |
| `features/workspace/navigation/registry.ts` declares workspace, developer and | |
| footer navigation. `useWorkspaceNavigation()` combines it with the authenticated | |
| subject, backend OpenAPI discovery, feature flags and plugin registrations. | |
| `AppSidebar` only renders resolved entries, so it contains no route-specific | |
| permission policy. Dynamic badges may be supplied in a future navigation | |
| registration without changing the shell. | |
| ## Commands and plugins | |
| `features/commands/registry.ts` is an empty-by-default command-palette | |
| registry. A command has a group, shortcut, capability requirement and an | |
| optional lazy `load()` function. Infrastructure exists; this migration does not | |
| populate product commands. | |
| `features/plugins/registry.ts` is a metadata registry for future modules. | |
| Plugins can register routes, navigation, commands, settings and capability | |
| requirements. Plugins cannot bypass route authorization, feature flags, | |
| capability checks or the shared API client. The registry does not load remote | |
| code and no marketplace plugin is enabled by this foundation. | |
| ## Realtime, uploads, search and editor foundation | |
| - `lib/realtime/subscription.ts` defines a generic subscription and React hook. | |
| Jobs adapt their existing WebSocket/SSE/polling fallback to it through | |
| `features/jobs/services/realtime.ts`; other features can use the same | |
| contract. | |
| - `features/media/services/upload-service.ts` is the single feature-level | |
| entry point to the existing chunked upload protocol. It supports cancellation | |
| via `AbortController`; transport/protocol discovery remains in `lib/api`. | |
| - `features/search` maps real template, operation and MCP discovery metadata to | |
| internal routes. It has no hard-coded backend payloads or client-provided | |
| URLs. | |
| - `features/editor/index.ts` supplies isolated selection/history/undo/redo and | |
| plugin state. It intentionally does not implement an editor surface, | |
| collaboration protocol or timeline UI. | |
| ## Performance and accessibility | |
| The refactor keeps the workspace shell small, centralizes QueryClient policy, | |
| uses stable query keys and makes command/plugin work lazy-loadable. Heavy | |
| future modules (editor, timeline, media inspection, collaboration) must be | |
| loaded behind feature boundaries and `Suspense` rather than imported by the | |
| shell. Existing image handling stays under Next.js configuration. | |
| Architecture changes preserve the Design System’s WCAG AA baseline: semantic | |
| tokens, keyboard-capable Radix primitives, labelled controls, focus management, | |
| reduced motion and responsive workspace shell behavior. New capability checks | |
| hide unavailable UI but do not substitute for backend authorization. | |
| ## Testing | |
| Vitest tests are colocated with the behavior they protect. Shared fixtures and | |
| setup remain centralized in `frontend/vitest.setup.ts`. The architecture suite | |
| covers feature flag resolution, capability evaluation, navigation filtering, | |
| command and plugin registration, editor history, safe search mapping and API | |
| error classification. Existing component/auth/API tests remain unchanged. | |
| Run locally: | |
| ```sh | |
| cd frontend | |
| npm run typecheck | |
| npm run lint | |
| npm test | |
| ``` | |
| The current Android/Termux environment cannot complete native Turbopack and | |
| Storybook production builds because their native bindings are unavailable. This | |
| is an environment limitation, not a reason to bypass typecheck, lint or unit | |
| tests. | |
| ## Migration and contribution guide | |
| 1. Preserve the route URL and add/refine its feature boundary first. | |
| 2. Put requests in a feature `api/` facade that reuses `lib/api/client.ts`. | |
| 3. Put server data in TanStack Query, browser-only local interaction in a | |
| narrowly scoped store, and domain algorithms in feature services/utils. | |
| 4. Register capability-driven navigation, commands or plugin metadata instead | |
| of editing the shell directly. | |
| 5. Reuse the Design System and shared error/realtime/upload primitives. | |
| 6. Add focused tests, then run typecheck, lint and the full unit suite. | |
| Do not move existing routes solely for directory aesthetics, duplicate API | |
| clients/hooks, read environment variables in a product page, create mock data | |
| or encode backend authorization in a UI-only permission check. The migration is | |
| backward compatible by design; there are no public route, API or authentication | |
| breaking changes in v2. | |