39948-vm/AGENTS.md
2026-07-03 16:11:24 +02:00

32 KiB

AGENTS.md

This file provides guidance to CODEX when working with code in this repository.

MAIN RULE: DON'T MADE UP ANYTHING!!! IF YOU NOT SURE - DOUBLECHECK IT VIA PROJECT DOCUMENTATION, TOOL DOCUMENTATION, APPROPRIATE MCP, WEB SEARCH OR JUST ASK FOR ME TO CLARIFY.

Project Overview

Tour Builder Platform - a web application for building and managing interactive virtual tours. Built with:

  • Frontend: Next.js 15 with React 19, TypeScript, Redux Toolkit, Tailwind CSS
  • Backend: Node.js/Express with Sequelize ORM
  • Database: PostgreSQL

System Requirements

Required Software

  • Node.js 24.x for backend runtime and TypeScript migration work
  • PostgreSQL 14+

FFmpeg

FFmpeg is bundled with the backend via ffmpeg-static and ffprobe-static npm packages. No manual installation is required.

How it works:

  • Pre-compiled binaries are downloaded during npm install
  • fluent-ffmpeg is configured to use bundled binaries in backend/src/services/videoProcessing.js
  • Works across all platforms (Linux, macOS, Windows) and Docker environments

Supported use case:

  • Reversed video generation for back navigation transitions

Important Rules

  • Never change global app configs casually - Do not modify .env or database configuration without verifying the target environment. These configs are shared across environments and incorrect changes can break production.
  • Centralize environment variables - Do not read new app/runtime environment variables directly from services, routes, components, hooks, or feature modules. Backend app env vars must be added to backend/src/utils/env-validation.ts, typed in backend/src/types/env.ts / backend/src/types/config.ts, exposed through backend/src/config.ts, and consumed via config. Frontend public env vars must be centralized in frontend/src/config.ts. Direct process.env access is acceptable only in bootstrap/config entrypoints (load-env, logger bootstrap, DB/Umzug config, Next config), scripts, migrations, seeders, and tests.
  • Backend async handlers must be wrapped with wrapAsync helper for error propagation.
  • Use Passport JWT for protected routes: passport.authenticate('jwt', { session: false }). Secure routes must read the authenticated user through getCurrentUser(req) from backend/src/utils/request-context.ts; if absent, return ForbiddenError.
  • Access model - Keep the current permission model: effective permissions are app_role.permissions + user.custom_permissions. Administrator, Platform Owner, and Account Manager are platform-wide internal roles. Other internal users currently have all-project scope by default unless a future explicit override system is implemented. Public users must not have admin API permissions and can only access public production pages plus explicitly granted private production presentations.
  • Public role hardening - Do not grant RBAC permissions or custom permissions to Public users. Do not represent private production presentation grants as READ_PROJECTS, READ_TOUR_PAGES, or other admin permissions; use production_presentation_access.
  • Centralize access decisions - New authorization logic must go through an AccessPolicy-style helper/service rather than ad hoc checks in routes/components. Keep admin API permissions separate from runtime presentation access.

Mandatory Rules For New Code

These rules are required for new code and for touched code when practical.

Backend Boundaries

  • Routes/controllers: only authenticate, read runtime context, validate request input, call a service, and map the response. Do not put business logic in routes.
  • Services/domain: own business logic, transactions, permission decisions, and orchestration.
  • DB API/repository: only data access, filters, includes, pagination, and persistence mapping.
  • Policy: all role/permission/runtime access decisions belong in policy/helper services, not scattered across routes.
  • Validation: all new external body, query, and params inputs must be validated before service calls. Joi is already available in backend and should be preferred unless there is a clear reason to use another validator.
  • ID handling: route params are canonical. For PUT/PATCH/DELETE /:id, use req.params.id; reject mismatched body ids.
  • Query safety: new list/autocomplete endpoints must define max limit, default pagination, allowed sort fields, and allowed sort directions.
  • Service contracts: new service/DB API methods should use object/options signatures, e.g. Service.update({ id, data, currentUser, transaction, runtimeContext }) and DBApi.findAll(filter, { currentUser, transaction, runtimeContext }).
  • Logging: use the project logger in runtime backend code (services, routes, middlewares, db/api, utils, app/bootstrap/config). Do not add new console.* calls outside migrations, seeders, scripts, or explicit debug-only CLI tooling.

Backend Typing

  • New backend pure helpers, validators, and policy modules should use TypeScript or JSDoc/checkJs-compatible types.
  • Define shared shapes for currentUser, runtimeContext, service options, and validation results when touching related code.
  • Do not add global Express.Request augmentation for project request fields. Use backend/src/utils/request-context.ts helpers for request-scoped currentUser, runtimeContext, logging, runtime-public flags, and permission overrides.
  • Keep active backend source in strict TypeScript/ESM.

Frontend State And API

  • Redux is for client/app state only: auth/session UI, theme/style, layout/sidebar, constructor UI state, and app preferences.
  • TanStack Query is for server state: API reads, entity lists/details, mutations, invalidation, and background refetch.
  • Do not create new entity CRUD Redux slices or new Redux thunks for server reads/mutations.
  • Centralize API access through query hooks or a shared API client. Avoid direct axios calls inside feature UI components unless there is a clear existing local pattern.
  • New feature-specific code should live near the feature/domain it belongs to. Do not put feature logic into generic components, hooks, or lib folders when a feature-local module is clearer.

Frontend TypeScript And React

  • Do not add new any without a specific reason. Prefer existing domain types or add a narrow local type.
  • Avoid new eslint-disable, @ts-ignore, and hook dependency suppressions. If unavoidable, add a short reason.
  • New hooks must follow React hook rules and should be structured so react-hooks/exhaustive-deps can be enabled.
  • Do not add new client usage of jsonwebtoken; use jwt-decode or /auth/me for client-side identity.

Database And Migrations

  • Do not add indexes speculatively. Add indexes after checking query patterns or explaining the expected query.
  • Migrations must be reversible or include an explicit rollback/backup plan.
  • Do not drop columns/tables in the same change that stops using them unless explicitly requested and production data safety is covered.
  • Do not normalize ui_schema_json into new tables unless there is a concrete current need; prefer validation/extraction helpers first.
  • Do not rewrite, rename, or reformat already applied backend migration files. Add new migrations only for new schema changes.

Disabled Features

The following features are implemented but currently disabled with false && conditions:

  • Navigation blocking while preloading - Navigation buttons can be disabled until neighbor page backgrounds are preloaded. Currently disabled to allow instant navigation response. To re-enable, remove false && in:
    • frontend/src/components/RuntimePresentation.tsx (isForwardNavDisabled, handleElementClick)
    • frontend/src/pages/constructor.tsx (handleElementClick, isNavDisabled calculation)

Documentation Workflow

  • Before each task: Research relevant documentation in documentation/ to understand existing implementations, patterns, and conventions.
  • After each task: Update affected documentation to reflect changes (API changes, new fields, modified workflows).
  • After implementing new features/modules: Create new documentation file in documentation/ following the existing format, and add reference to the Feature Documentation table in this file.

Quick Start

# Terminal 1 - Backend (port 3000)
cd backend && npm run start-dev

# Terminal 2 - Frontend (port 3001)
cd frontend && npm run dev

Common Commands

Backend (run from backend/ directory, runs on port 3000 in the default dev_stage flow)

npm install              # Install dependencies
npm run start-dev        # Start server (loads .env, migrates, seeds, watches)
npm run lint             # ESLint check
npm run typecheck        # Strict TypeScript check for migrated backend scope
npm run test             # Unit tests (Node test runner)
npm run test:integration # Integration tests; DB tests skip when Postgres is unavailable
npm run test:e2e         # E2E HTTP tests with a local listener
npm run test:all         # Unit, integration, and e2e suites
npm run build            # Compile migrated TypeScript files
npm run db:migrate       # Run migrations only
npm run db:migrate:undo  # Undo last migration
npm run db:seed          # Seed database only
npm run db:reset         # Drop, create, migrate, and seed

Creating new migrations: Add new migration files deliberately under backend/src/db/migrations/ only when a schema change is required. Keep them reversible and preserve already applied migration files unchanged.

Note: Backend .env is loaded centrally by backend/src/load-env.ts for app and DB entrypoints. If NODE_ENV is absent, it defaults to dev_stage, matching the standard VM backend flow and using the .env DB settings. Do not add NODE_ENV=production to local startup unless a task explicitly requires the production config. The backend defaults to port 3000 in dev_stage and 8080 otherwise; set PORT explicitly when a task needs a different port.

Frontend (run from frontend/ directory)

npm install               # Install dependencies
npm run dev               # Start dev server with Turbopack (port 3001)
npm run typecheck         # TypeScript check without production build
npm run verify            # Typecheck, lint, and production build
npm run build             # Production build
npm run lint              # ESLint check (.ts, .tsx files)
npm run format            # Format code with Prettier

Standard VM Environment

The standard VM port split is:

  • Frontend PM2 app frontend-dev: Next.js production server on port 3001
  • Backend PM2 app backend-dev: npm run start with NODE_ENV=dev_stage on port 3000
  • Apache: public entrypoint and reverse proxy on port 80

Direct VM backend health checks should target http://127.0.0.1:3000/api/.... A protected endpoint returning 401 Unauthorized without JWT means the backend is reachable.

Docker (run from docker/ directory)

chmod +x start-backend.sh && chmod +x wait-for-it.sh  # First time setup
docker-compose up                                       # Start all services
rm -rf data && docker-compose up                        # Start with fresh database

Architecture

Backend Structure (backend/src/)

  • routes/: Express route handlers (RESTful endpoints)
  • db/api/: Database access layer (CRUD operations per model)
  • db/models/: Sequelize model definitions
  • db/migrations/: Database migrations
  • db/seeders/: Seed data
  • auth/: Passport.js authentication (JWT, Google OAuth, Microsoft OAuth)
  • services/: Business logic services (emails, notifications, publishing, file storage)
  • factories/: Router and service generation (router.factory.js, service.factory.js)
  • middlewares/: Permission checking, runtime context handling
  • helpers/: Utility functions (e.g., wrapAsync for async error handling)

Frontend Structure (frontend/src/)

  • pages/: Next.js pages (each entity has -list, -edit, -new, -view, -table variants)
  • components/: React components (PascalCase naming)
  • stores/: Redux Toolkit slices (per entity + auth, main, style slices)
  • layouts/: Page layouts (Authenticated, Guest)
  • hooks/: Global custom hooks (usePreloadOrchestrator, useNeighborGraph, etc.)
  • lib/: Utility libraries (elementStyles, imagePreDecode, mediaDuration, parseJson)
  • types/: TypeScript type definitions (constructor.ts, runtime.ts, preload.ts)
  • helpers/: Utility functions
  • styles/: Global CSS with Tailwind (_theme.css uses @apply)

Key Domain Models

Projects, Tour Pages, Assets, Asset Variants, Permissions, Roles, Users, Publish Events, PWA Caches, Element Type Defaults, Project Element Defaults

Note: Page elements, navigation links, and transitions are stored directly in tour_pages.ui_schema_json.

Element Defaults Hierarchy

The system has a two-level defaults cascade for UI elements:

  1. element_type_defaults - Global platform-wide defaults (11 predefined types)
  2. project_element_defaults - Project-specific overrides (auto-snapshotted from global on project creation)

When creating new elements, defaults cascade: global → project. Instance-specific settings are stored in tour_pages.ui_schema_json.

Special Pages

  • constructor.tsx: Tour builder/editor with drag-drop, element positioning, page management
  • runtime.tsx: Tour playback with transitions, preloading, navigation
  • p/[slug].tsx: Public tour pages with PWA offline support

State Management

  • Redux Toolkit is the default only for client/app state (auth/session UI, style/theme, layout/sidebar, constructor UI state, app preferences)
  • TanStack Query is the default for server state (API reads, entity lists/details, mutations, invalidation, background refetch)
  • Do not create new entity CRUD Redux slices in stores/[entity]/[entity]Slice.ts.
  • Core slices: authSlice.ts (auth), mainSlice.ts (UI), styleSlice.ts (theming), constructorSlice.ts (editor)
  • Use useAppSelector and useAppDispatch hooks from stores/hooks.ts
  • Local hooks acceptable for ephemeral, component-scoped state (e.g., usePageNavigation for session-scoped page history)
  • See stores-module.md for decision tree

Code Conventions

Backend

  • ES6+ with arrow functions, const/let
  • Document endpoints with Swagger JSDoc comments
  • Lowercase filenames (e.g., auth.js, projects.js)

Frontend

  • Functional components with TypeScript
  • PascalCase for components and types, camelCase for variables/functions
  • Custom hooks prefixed with use (e.g., useAuth)
  • Tailwind CSS with theme customization in _theme.css

Styling

  • Theme customization uses @apply directive in _theme.css
  • Sidebar styles: Target #asideMenu (defined in AsideMenuLayer.tsx) for sidebar overrides
  • Use highly specific selectors when overriding Tailwind utilities to avoid conflicts
  • Themed blocks (.theme-pink, .theme-green) standardize UI appearance - ensure custom overrides integrate cleanly

Error Handling

  • Backend: Use centralized commonErrorHandler middleware for uniform error responses
  • Frontend: Use React error boundaries for runtime errors

API Patterns

Routes follow pattern: /api/[entity]/ with standard CRUD operations

  • POST /api/auth/signin/local - Login
  • GET /api/auth/me - Current user (requires JWT)
  • Self-registration is disabled. New users are created by Administrator, Platform Owner, or Account Manager through the Users flow and receive an invitation/setup link.

All entity routes support: list, findOne, create, update, destroy operations with pagination and filtering.

Backend Factory Patterns

Router Factory (factories/router.factory.js)

Generates standard CRUD routes for entities:

module.exports = createEntityRouter('assets', AssetsService, AssetsDBApi, {
  permissionEntity: 'assets',
});

Service Factory (factories/service.factory.js)

Generates service classes with transaction handling:

module.exports = createEntityService('assets', AssetsDBApi);

Base DB API (db/api/base.api.js)

All entity APIs extend GenericDBApi and override:

  • MODEL - Sequelize model reference
  • SEARCHABLE_FIELDS - Fields for text search
  • RANGE_FIELDS - Fields for range filtering
  • ENUM_FIELDS - Fields for exact match filtering
  • getFieldMapping(data) - Transform input data before save

File Storage

Controlled by FILE_STORAGE_PROVIDER env var or auto-detected from credentials:

  • s3 - AWS S3 (requires S3_BUCKET, S3_REGION, S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEY)
  • gcloud - Google Cloud Storage
  • local - Local filesystem (default)

Publishing Workflow

Three-tier content environment model (distinct from NODE_ENV server environment):

  • Dev (constructor editing) → Stage (preview) → Production (public)

Route-based environment access:

  • /p/[slug] - Production presentation
  • /p/[slug]/stage - Stage presentation
  • /constructor?projectId= - Dev editing (always dev environment)

Key endpoints:

  • POST /api/publish/save-to-stage - Dev → Stage (body: { projectId })
  • POST /api/publish - Stage → Production (body: { projectId })

Environment isolation: Pages have environment field (dev, stage, production). The X-Runtime-Environment header (set by frontend) determines which content environment to query. Both frontend (RuntimePresentation.tsx) and backend (runtime-context.js) filter by this field.

Server Environment vs Content Environment:

  • NODE_ENV controls database config (production/dev_stage/development)
  • tour_pages.environment controls content visibility (dev/stage/production)

Private Production Presentations

Production presentations are public by default at /p/[slug]. Each project can switch production runtime visibility through projects.production_presentation_visibility (public default, private optional).

  • Platform staff users with any RBAC permission can visit every private production presentation
  • Customer users with Public role can visit only private production presentations granted in production_presentation_access
  • Administrator, Platform Owner, and Account Manager can create users
  • When creating or editing a Public user, the form shows a selector for private production presentations and saves DB access grants
  • Do not use config files or env vars for customer allowlists
  • Do not grant customer viewer users broad permissions such as READ_PROJECTS or READ_TOUR_PAGES; any RBAC permission makes the user platform staff for private presentation access

See private-production-presentations.md.

PWA & Offline Support

  • Service Worker: Generated by Serwist from frontend/src/sw.tspublic/sw.js
  • Offline Caching: PWA_Caches model tracks cached assets per project
  • Runtime Mode: Middleware distinguishes public vs authenticated access for offline tours

Asset Preloading Architecture

Runtime presentations use direct S3 downloads via presigned URLs for instant page navigation:

1. Request presigned URLs (max 50 per batch, 1-hour expiry)
   POST /api/file/presign { urls: ["assets/img.jpg", ...] }

2. Download directly from S3 → Store in Cache API (< 5MB) or IndexedDB (≥ 5MB)

3. Create blob URL → Decode image → Store in readyBlobUrlsRef

4. Instant lookup during navigation (O(1))
   const blobUrl = preloadOrchestrator.getReadyBlobUrl(originalUrl);

Preload Priority: Transition videos (+150) > Images (+100) > Audio (+50) > Video (+30)

Storage Layers:

Layer Size Limit Purpose
Cache API < 5MB Fast asset storage
IndexedDB (Dexie) ≥ 5MB Large assets, offline data
Blob URLs Memory Pre-decoded for instant display

Frontend Patterns

MUI X Data Grid v7

Use new valueGetter signature:

// For value transformation
valueGetter: (value) => value?.id ?? value

// For row access
valueGetter: (_value, row) => new Date(row.created_at)

Custom Hooks

Hooks are located in three places:

  • src/hooks/: Global hooks (usePreloadOrchestrator, usePageNavigationState, useTransitionPlayback, usePageNavigation)
  • src/hooks/video/: Video playback primitives (8 composable hooks for video playback scenarios)
  • Component directories: Domain-specific hooks (components/Assets/useAssetUploader.ts)

Key runtime hooks:

Hook Purpose
usePreloadOrchestrator Stream-first asset preloading (current page + transitions only)
usePageNavigationState Unified navigation state machine (replaces 6+ hooks)
useTransitionPlayback Video transition playback coordination
usePageNavigation Page history tracking with browser-like back behavior
useCanvasScale Responsive canvas scaling with letterbox mode
useNetworkAware Network condition monitoring for adaptive transitions
useBackgroundDimensionSuggestion Detect background media dimensions for canvas size suggestions

Video Hooks (src/hooks/video/):

Hook Purpose
useVideoBlobUrl Resolve video URLs to blob URLs from preload cache
useVideoPlaybackCore Core playback logic combining multiple primitives
useVideoPlayer Complete UI video player hook

Redux Entity Pattern

Use this pattern only when maintaining an existing Redux entity flow; new server-state code should use TanStack Query hooks instead.

import { fetch, deleteItem } from '../../stores/assets/assetsSlice';
const dispatch = useAppDispatch();
dispatch(fetch({ query: '?limit=100' }));

Feature Documentation

Detailed feature documentation is available in documentation/:

Document Description
project-architecture.md Overall project structure and architecture
api-reference.md API endpoints reference
authentication-system.md JWT, OAuth (Google, Microsoft) authentication
rbac-system.md Role-based access control system
user-management.md User CRUD, roles assignment, account management
project-memberships.md Team collaboration, per-project access control
asset-upload-variants.md Asset upload pipeline and variant generation
ui-elements.md UI Elements stored in ui_schema_json (buttons, hotspots, galleries, tooltips, media players)
page-transitions.md Video transitions stored on navigation elements
project-transition-settings.md Environment-aware CSS transition settings (fade, duration, easing)
video-playback.md Video player implementation, iOS autoplay compatibility
assets-preloading.md Asset preloading and caching strategy
publishing-workflow.md Dev → Stage → Production publishing
private-production-presentations.md Private production presentation allowlist, viewer users, and runtime access flow
offline-pwa-mode.md PWA offline capabilities and caching
email-notification-service.md Nodemailer/SES integration, verification emails, invitations
search-system.md Global full-text search, permission-based filtering
deployment-vm.md Standard VM deployment topology, PM2 recovery, ports, OOM/ffmpeg diagnostics
custom-domains-apache.md Customer-owned presentation domains via Apache, DNS A records, Certbot, and host/path routing
page-links-navigation.md Page navigation using targetPageSlug in elements
access-logs-audit-trail.md Access logging, audit trail, activity tracking
db-cleanup-audit.md Non-destructive DB cleanup audit, orphan checks, legacy schema checks, and soft-delete retention policy
global-ui-controls.md Configurable fullscreen, sound, and offline system controls with global/project/page cascade
backend/database-schema.md Complete database schema - all models, fields, relationships, indexes, constraints
backend/backend-architecture.md Backend architecture - layers, design patterns, middleware, factories, file storage
backend/api-endpoints.md Complete API reference - all endpoints, request/response formats, authentication, rate limits
backend/modules/core.md Core module - index.js (entry), config.ts (configuration), helpers.js/utilities
backend/modules/auth.md Auth module - Passport.js strategies (JWT, Google, Microsoft), login, invitation setup, password reset, email verification
backend/modules/middleware.md Middleware module - rate limiting, permissions, runtime context, public access control, file uploads
backend/modules/routes.md Routes module - 22 route files, factory pattern, CRUD endpoints, Swagger docs, auth/file/search/sql routes
backend/modules/services.md Services module - business logic layer, 34 service files, factory pattern, file storage (S3/GCloud/Local), email, notifications, publishing
backend/modules/email.md Email module - Nodemailer/AWS SES, transactional emails, HTML templates, token management, email verification, password reset, invitations
backend/modules/notifications.md Notifications module - error classes (ValidationError, ForbiddenError), i18n message catalog, helper functions
backend/modules/factories.md Factories module - createEntityRouter and createEntityService functions, code generation patterns for CRUD operations, boilerplate elimination
backend/modules/db-models.md DB Models module - 16 Sequelize models, entity definitions, associations, validations, lifecycle hooks, soft delete patterns
backend/modules/db-api.md DB API module - GenericDBApi base class, 18 entity APIs, declarative configuration, query filtering, runtime context helpers
backend/modules/db-migrations.md DB Migrations module - Umzug runner, migration safety rules, schema evolution, data backfill
backend/modules/db-seeders.md DB Seeders module - Umzug seeders, RBAC setup (7 roles, 54 permissions), sample data opt-in
backend/modules/db-config.md DB Config module - typed DB config, environment validation (Joi), Umzug commands, sync/reset scripts
backend/modules/utilities.md Utilities module - error classes, Pino logging, env validation, request helpers (wrapAsync, commonErrorHandler), DB utils, i18n messages
backend/testing.md Backend test strategy - unit, integration, and e2e coverage, helpers, commands, and environment notes
frontend/hooks-reference.md React hooks reference (useFormSync, useEntityTable, useOfflineMode, etc.)
frontend/video-hooks-module.md Video playback primitive hooks (8 composable hooks for video scenarios)
frontend/constructor-page-editor.md Constructor page editor - visual tour builder with drag-drop elements
frontend/runtime-presentation.md Runtime presentation viewer - full-screen tour playback with transitions
frontend/navigation-smooth-transitions.md Navigation & smooth transitions - page switching, transition video playback, last frame preservation, online/offline modes
frontend/ui-adaptivity-system.md UI Adaptivity System - canvas units (--cu), responsive scaling, letterbox mode, element styling
frontend/ui-element-preloading-analysis.md UI element processing & neighbor preloading - deep analysis of asset extraction, preload flow, offline caching
frontend/asset-upload-preloading-pwa-analysis.md Deep analysis of asset upload, preload orchestrator, PWA/offline mode, storage layers, network awareness
frontend/frontend-architecture.md Frontend architecture - 386 files, 14 modules, factories, hooks, Redux, PWA/offline, design patterns
frontend/pages-module.md Pages module - 99 pages, _app.tsx entry, entity CRUD pattern, constructor, runtime presentations, layouts
frontend/components-module.md Components module - 183 files, entity tables, factories, constructor, element settings, runtime, PWA/offline
frontend/hooks-module.md Hooks module - 52 custom hooks, runtime/preloading, PWA/offline, constructor, tables/forms, UI utilities
frontend/stores-module.md Stores module - Redux Toolkit, 17 slices (4 core + 13 entity), createEntitySlice factory, typed hooks
frontend/lib-module.md Lib module - 19 utility files, asset URL management, element defaults/styles/effects, offline storage (Cache API + IndexedDB), download queue
frontend/types-module.md Types module - 18 TypeScript definition files, entity types, constructor/runtime types, API/Redux types, permissions enum, offline/preload types
frontend/factories-module.md Factories module - 5 factory files (~1,285 LOC), createListPage, createFormPage, createTableComponent, configBuilderFactory, createEntitySlice for 91% boilerplate reduction
frontend/schemas-module.md Schemas module - 6 Zod validation schema files (~257 LOC), form validation for User, Asset, Project, TourPage, Role entities with type inference
frontend/helpers-module.md Helpers module - 6 utility files (~304 LOC), dataFormatter (entity display), hasPermission (RBAC), notifyStateHandler (Redux), text formatters, file saver
frontend/layouts-module.md Layouts module - 2 layout files (~262 LOC), LayoutAuthenticated (JWT auth, permissions, UI chrome), LayoutGuest (public pages), getLayout pattern
frontend/config-module.md Config module - 12 config files (~523 LOC), Next.js/Tailwind/TypeScript build config, runtime settings (config.ts), menu configs, offline/preload configs, ESLint rules
frontend/context-module.md Context module - DownloadContext (~256 LOC) for PWA download progress state, DownloadEventBus integration, useDownloadContext/useDownloadContextOptional hooks
frontend/interfaces-module.md Interfaces module - ~15 type definition files (~2,200+ LOC), entity types, API contracts, Redux state shapes, permissions enum, constructor/runtime/offline/preload interfaces