Skip to main content

Documentation Index

Fetch the complete documentation index at: https://grantmaster.dev/llms.txt

Use this file to discover all available pages before exploring further.

Full-Stack Platform Bootstrap Specification

You are setting up a new production-grade full-stack platform. Follow this specification exactly to establish the architecture, patterns, and conventions. Replace [AppName] with the actual application name and [domain] with the business domain throughout.

1. Monorepo Structure (npm workspaces + Turbo)

[app-name]/ ├── src/ # Main React web app ├── functions/ # Firebase Cloud Functions (separate workspace) ├── packages/ │ ├── shared/ # Shared types, utilities, constants across all workspaces │ ├── domain-schema/ # Zod schemas as single source of truth for all entities │ ├── domain-client/ # Typed tRPC client hooks consumed by web + mobile │ └── tsconfig/ # Shared TypeScript configurations ├── portal/ # External-facing portal app (separate build/hosting) ├── apps/ # Future apps (mobile via Expo, etc.) ├── scripts/ # Automation, migration, validation scripts ├── tests/ # E2E test setup and auth states ├── .github/workflows/ # CI/CD pipelines ├── package.json # Root: workspaces config + orchestration scripts ├── turbo.json # Turbo build pipeline config ├── firebase.json # Firebase project config (emulators, hosting, rules) ├── .firebaserc # Environment → Firebase project mappings ├── firestore.rules # Firestore security rules ├── storage.rules # Storage security rules ├── firestore.indexes.json # Composite query indexes ├── vite.config.ts # Vite bundler config ├── vitest.config.ts # Unit test config ├── playwright.config.ts # E2E test config ├── tailwind.config.ts # Tailwind CSS config └── CLAUDE.md # AI assistant project guide

Root package.json key scripts:

{
  "workspaces": ["packages/*", "functions", "portal", "apps/*"],
  "scripts": {
    "dev": "vite --port 3000",
    "dev:start": "firebase emulators:start --import=./seed-data --export-on-exit",
    "dev:all": "concurrently \"npm run dev:start\" \"npm run dev\"",
    "dev:all:fresh": "concurrently \"npm run dev:start -- --clear\" \"npm run dev\"",
    "build": "tsc --noEmit && vite build",
    "build:staging": "vite build --mode staging",
    "test": "vitest",
    "test:coverage": "vitest run --coverage",
    "test:e2e": "playwright test",
    "lint": "eslint . --ext .ts,.tsx",
    "lint:fix": "eslint . --ext .ts,.tsx --fix"
  }
}

2. Tech Stack

LayerTechnologyPurpose
FrontendReact 19 + TypeScriptUI framework
BundlerVite 6Dev server + production builds
StylingTailwind CSS 4 + shadcn/ui (Radix primitives)Design system
State (server)@tanstack/react-query + tRPCServer state + type-safe API
State (client)React Context (3-tier provider hierarchy)App/UI state
Formsreact-hook-form + ZodValidation + form state
Tables@tanstack/react-table + react-windowVirtual scrollable tables
BackendFirebase (Auth, Firestore, Storage, Functions)BaaS
APIHono + tRPC + RESTThree-protocol API gateway
AIGoogle Gemini via GenkitAI features
i18ni18next + react-i18nextInternationalization
NotificationsInternal system (src/features/notifications/) + Postmark (email) + FCM (push)Multi-channel notifications; bell badge + toasts + email
MonitoringSentryError tracking + performance
Animationsmotion (v12+)Micro-interactions
PDF/Docs@react-pdf/renderer, exceljs, docx, jspdfDocument generation
TestingVitest (unit) + Playwright (E2E)Full test coverage
PaymentsStripeSubscriptions + billing

3. Core Architecture Patterns

3a. BaseService<T> — Abstract Service Layer

Every Firestore-backed feature uses a service that extends BaseService<T>:
// src/core/BaseService.ts
export abstract class BaseService<T extends BaseEntity> {
  protected collectionName: string;
  protected logger: Logger;
  protected eventBus: EventBus;
  
  // Built-in: tenant isolation, validation, logging, quota checks
  abstract validate(data: Partial<T>): ValidationResult;
  
  async create(data: Omit<T, 'id'>, organizationId: string): Promise<T> { /* ... */ }
  async update(id: string, data: Partial<T>, organizationId: string): Promise<T> { /* ... */ }
  async delete(id: string, organizationId: string): Promise<void> { /* ... */ }
  async getById(id: string, organizationId: string): Promise<T | null> { /* ... */ }
  async query(filters: QueryFilters, organizationId: string): Promise<T[]> { /* ... */ }
}
Rules:
  • ALL services extend BaseService — no direct Firestore calls from components
  • organizationId is ALWAYS required (enforces multi-tenancy at service layer)
  • Zod schemas validate on write, explicit read-intent on read
  • Services emit EventBus events for significant mutations

3b. EventBus — Decoupled Event System

// src/core/EventBus.ts
class EventBus {
  emit<T>(event: TypedEvent<T>): void;
  on<T>(eventType: string, handler: (payload: T) => void): Unsubscribe;
}
MUST emit events for:
  • Approval/rejection workflows
  • Entity lifecycle (created, archived, deleted)
  • Business thresholds (budget 80%/90%/100%, deadlines)
  • Compliance & audit events
  • Submissions requiring attention
Do NOT emit for: reads, trivial updates, internal optimizations.

3c. Three-Tier Provider Hierarchy

<GlobalProviders>        ← Auth, Theme, i18n, Sentry, QueryClient
  <DomainProviders>      ← Organization/Tenant, Feature flags, Permissions
    <FeatureProviders>   ← Per-feature contexts (grants, expenses, etc.)
      <App />
    </FeatureProviders>
  </DomainProviders>
</GlobalProviders>
  • GlobalProviders: Framework-level, always available
  • DomainProviders: Business-level, requires authentication
  • FeatureProviders: Feature-specific, lazy-loaded with routes

3d. Three-Protocol API Gateway (Cloud Functions)

Cloud Function (Hono) ─┬─ /api/trpc/* → tRPC routers (type-safe RPC for own apps) ├─ /api/v1/* → REST endpoints (third-party integrations) └─ /api/mcp/* → MCP protocol (AI agent tools) Three authentication paths converge to unified AuthContext:
  1. Firebase JWT → web/mobile apps
  2. API Key → third-party integrations
  3. Portal Token → external portal users

4. Feature Module Pattern

Every feature is self-contained:
src/features/[feature]/
├── components/
│   ├── pages/          # Route-level page components
│   ├── tabs/           # Tab content components
│   ├── sections/       # Page sections
│   └── cards/          # Feature-specific cards
├── hooks/
│   ├── use[Feature].ts           # Main query/mutation hook
│   └── use[Feature]Selectors.ts  # Derived/filtered data hooks
├── services/
│   └── [feature]Service.ts       # BaseService<T> extension
├── schemas/
│   └── [feature].schema.ts       # Zod schemas (source of truth for types)
├── contracts.ts                   # Enums, interfaces, state types
├── [Feature]Context.tsx           # Feature-level React Context (if needed)
└── index.ts                       # Barrel export — PUBLIC API only
Rules:
  • Features NEVER import from another feature’s internals — only via barrel export
  • Schemas are the single source of truth — types are inferred from schemas
  • Hooks handle server state (React Query), Context handles UI state
  • Services handle all Firestore operations and business logic

5. UI Component Library

Layout Components (custom)

ComponentPurpose
<PageLayout>Wraps every page — consistent padding, max-width, scroll
<PageHeader>Page title + breadcrumbs + actions — never manual <h1>
<PageTabs>Tab navigation — render content externally, not via tab prop
<SecondarySidebar>Left sidebar nav for sub-pages — items or groups prop
<PageShell>App shell with sidebar + header

shadcn/ui Base (install from shadcn CLI)

Button, Card, Input, Textarea, Select, Dialog, Sheet, Tabs, Badge, Table, Checkbox, Switch, Label, Alert, Popover, DropdownMenu, Command, Progress, Skeleton, ScrollArea, AlertDialog

Extended Components (build custom)

ComponentPurpose
<StatusBadge>Status indicators with mapDomainStatus() mapping
<CardVariants> / <StatCard>Metric display cards
<DataTable>@tanstack/react-table + virtual scrolling + bulk actions
<FormField> / <FormSection> / <FormGrid> / <FormActions>Form layout system
<KpiCard>KPI metric display
<KanbanBoard>Kanban view
<EmptyState>Empty state illustrations
<AsyncBoundary>Suspense + Error boundary combo
<RouteErrorBoundary>Route-level error handling

Skeleton Loaders (one per page type)

PageSkeleton, DashboardSkeleton, TablePageSkeleton, FormPageSkeleton, WorkspaceSkeleton

UI Rules

  • Colors: slate-* only (never gray-), primary- tokens (never blue-), emerald- for success, rose-* for error, amber-* for warning
  • Dark mode: dark:bg-{color}-500/10 for subtle tints
  • Buttons: Always <Button> component with isLoading + loadingText props
  • Icons: lucide-react only. Sizes: buttons w-4 h-4, headers w-5 h-5, stats w-8 h-8
  • Typography: h1=text-3xl font-bold, h2=text-xl font-semibold, h3=text-lg font-medium, body=text-sm text-slate-600

6. Routing System

// src/routes/lazyComponents.tsx — all route components lazy-loaded
export const DashboardPage = React.lazy(() => import('@/features/dashboard/components/pages/DashboardPage'));
export const ProjectsPage = React.lazy(() => import('@/features/projects/components/pages/ProjectsPage'));

// src/routes/routeConfig.tsx — centralized route definitions
export const routes: RouteConfig[] = [
  { path: '/dashboard', element: <DashboardPage />, label: 'Dashboard', icon: LayoutDashboard },
  { path: '/projects', element: <ProjectsPage />, label: 'Projects', icon: FolderOpen },
  // ...
];
Rules:
  • ALL route components use React.lazy() for code splitting
  • Route config is centralized — never scatter route definitions
  • Navigation config derives from route config

7. Firebase Cloud Functions

Dynamic Module Loading Pattern

// functions/src/index.ts
function registerModuleExports(modulePath: string) {
  const mod = require(modulePath);
  Object.assign(module.exports, mod);
  Object.assign(exports, mod);
}

// Production: load all modules
registerModuleExports('./stripe');
registerModuleExports('./genkit');
// ... 20+ modules

// Emulator: load selectively for fast cold starts
if (process.env.FUNCTIONS_EMULATOR) {
  registerModuleExports('./genkit'); // minimal set
}

Sentry Lazy-Loading (critical)

// functions/src/core/sentry.ts
// MUST lazy-load Sentry — @sentry/node registers OpenTelemetry hooks at import time
// which causes Firebase CLI's 10s function-discovery timeout to be exceeded
let _sentry: typeof import('@sentry/node') | null = null;
export function getSentry() {
  if (!_sentry) _sentry = require('@sentry/node');
  return _sentry;
}

Pre-Deployment Vendoring Pipeline

# scripts/prepare-functions.sh — firebase.json predeploy hook
# Step 1: Validate lockfile integrity
# Step 2: Sync shared packages → functions/shared/
# Step 3: Compile shared with esbuild
# Step 4: Vendor into functions/node_modules/ (Cloud Build can only see functions/)
# Step 5: Build functions with esbuild → lib/
# Step 6: Cold-start load budget verification (target 12s, hard limit 18s)

8. Security Model

Firestore Rules Pattern

rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// ALL collections nested under organization for tenant isolation
match /organizations/{orgId} {
// Helper functions
function belongsToOrg() { return request.auth.token.organizationId == orgId; }
function isAdmin() { return belongsToOrg() && request.auth.token.role in ['admin', 'superadmin']; }
function isManagerOrHigher() { return belongsToOrg() && request.auth.token.role in ['manager', 'admin', 'superadmin']; }
  match /[collection]/{docId} {
    allow read: if belongsToOrg();
    allow create: if isManagerOrHigher();
    allow update: if isManagerOrHigher() && !request.resource.data.diff(resource.data).affectedKeys().hasAny(['organizationId']);
    allow delete: if isAdmin();
  }
}
}
}
Rules:
  • NEVER allow cross-tenant queries without organizationId filter
  • NEVER allow writes that change organizationId
  • Role hierarchy: employee < manager < admin < superadmin
  • Portal tokens use separate auth path with limited permissions

Storage Rules

  • Path-based access: /organizations/{orgId}/[type]/{docId}/{fileName}
  • User self-managed paths (avatars)
  • Admin-only platform paths
  • Signed URL bypass for portal submissions

9. Testing Infrastructure

Unit Tests (Vitest)

// vitest.config.ts
export default defineConfig({
  test: {
    environment: 'jsdom',
    setupFiles: ['tests/setup.ts'],
    coverage: {
      provider: 'v8',
      thresholds: { lines: 30, functions: 24, branches: 20, statements: 30 }
    },
    testTimeout: 15000
  }
});

E2E Tests (Playwright) — Role-Based

// playwright.config.ts
projects: [
  { name: 'setup', testMatch: '*.setup.ts' },                    // Auth state setup
  { name: 'admin', testMatch: '*admin.spec.ts', dependencies: ['setup'] },
  { name: 'manager', testMatch: '*manager.spec.ts', dependencies: ['setup'] },
  { name: 'employee', testMatch: '*employee.spec.ts', dependencies: ['setup'] },
]
// Auth states stored in tests/.auth/{role}.json

10. CI/CD Pipeline (GitHub Actions)

Job Dependency Graph

lint (20+ checks) ─────────────────┐ unit-tests ─────────────────────────┤ ai-evals ──────────────────────────┤ e2e-tests (with Firebase emulators) ┤──→ test-summary ──→ build (staging/prod) visual-regression-tests ────────────┤ functions-build ───────────────────┤ portal-quality ────────────────────┤ security-scan (npm audit + secrets) ┘

Lint Gates (enforce architecture)

  • ESLint + TypeScript type checking
  • Bundle size limits
  • Architecture enforcement (no layer violations)
  • Feature contracts validation
  • Public API contracts
  • i18n completeness
  • Dead export detection
  • BaseService adoption enforcement
  • Firestore path validation

11. Environment Management

Firebase Projects

// .firebaserc
{
  "projects": {
    "default": "[app]-local",
    "production": "[app]-prod",
    "staging": "[app]-staging"
  }
}

Emulator Ports (hardcoded, never change)

ServicePort
Vite Dev Server3000
Firebase Emulator UI4000
Firestore8080
Auth9099
Storage9199
Functions5001

Required Environment Variables

VITE_FIREBASE_API_KEY= VITE_FIREBASE_AUTH_DOMAIN= VITE_FIREBASE_PROJECT_ID= VITE_FIREBASE_STORAGE_BUCKET= VITE_FIREBASE_MESSAGING_SENDER_ID= VITE_FIREBASE_APP_ID= VITE_GEMINI_API_KEY= VITE_SENTRY_DSN= VITE_STRIPE_PUBLISHABLE_KEY=

Note: Novu was removed — the internal notification system replaces it. No VITE_NOVU_APP_ID needed.


12. Shared Hooks Library

Build these reusable hooks from day one:
HookPurpose
useZodFormreact-hook-form + Zod validation + toast + error handling
useDataFetchGeneric data fetching with loading/error states
usePaginatedDataPagination logic
useOptimisticUpdateOptimistic UI updates
useAsyncActionAsync action with loading/error/success states
useEventBusRefreshRefresh queries when EventBus events fire
useKeyboardShortcutsKeyboard shortcut registration
useDeviceTypeResponsive device detection
useNetworkStatusOnline/offline detection
useTouchGesturesMobile gesture handling
useAccessibilitya11y utilities
useRoutePrefetchPrefetch route data on hover/focus
useTRPCtRPC client hook setup

13. Development Workflow

Getting Started

  1. Clone repo, run npm install
  2. Copy .env.example.env.local, fill in keys
  3. npm run dev:all — starts Firebase emulators + Vite dev server
  4. Navigate to localhost:3000

Adding a New Feature

  1. Create src/features/[feature]/ with standard structure
  2. Add Zod schemas in packages/domain-schema/
  3. Extend BaseService<T> for the service layer
  4. Add route to src/routes/routeConfig.tsx + lazy component
  5. Add feature context to FeatureProviders if needed
  6. Add Firestore security rules under /organizations/{orgId}/
  7. Add composite indexes to firestore.indexes.json
  8. Write unit tests colocated with source
  9. Write E2E tests in role-appropriate spec files

Adding a Cloud Function

  1. Create module in functions/src/[module].ts
  2. Register in functions/src/index.ts via registerModuleExports()
  3. Add to emulator selective load if needed for dev
  4. Add corresponding tRPC router if it serves API data

14. Bootstrap Execution Order

When setting up the new project, execute in this order:
  1. Initialize monorepo: package.json with workspaces, install Turbo
  2. Set up TypeScript configs: base configs in packages/tsconfig/
  3. Set up Vite + React: src/main.tsx, src/App.tsx, vite.config.ts
  4. Set up Tailwind + shadcn/ui: Install, configure theme tokens
  5. Set up Firebase: firebase.json, .firebaserc, emulator config
  6. Build core layer: BaseService, EventBus, Firebase client, error handling
  7. Build provider hierarchy: Global → Domain → Feature providers
  8. Build layout shell: Sidebar, Header, PageLayout, PageHeader
  9. Build routing system: lazyComponents, routeConfig, navigation
  10. Build UI component library: All shared components from Section 5
  11. Build shared hooks: All hooks from Section 12
  12. Set up Cloud Functions: Hono + tRPC + dynamic module loading
  13. Write security rules: Firestore + Storage with tenant isolation
  14. Set up testing: Vitest config + Playwright config + role-based setup
  15. Set up CI/CD: GitHub Actions with all lint gates
  16. Build first feature: Follow the feature module pattern end-to-end
  17. Set up monitoring: Sentry integration + error boundaries
  18. Set up i18n: i18next config + translation files
  19. Write CLAUDE.md: Project guide for AI assistants
Verification To verify the prompt works: Use it to bootstrap a new project (even a test one) Check that the monorepo structure matches Verify the provider hierarchy renders correctly Confirm the feature module pattern is followed for the first feature Run npm run dev:all and verify emulators + dev server start Run npm test and verify the test infrastructure works Deploy to staging and verify the pre-deployment pipeline runs