Development

Development workflows and contributing to LegalEase.

This guide covers development workflows, project structure, and how to contribute to LegalEase.

Repository Layout

legalease-ai/
├── frontend/          # Nuxt 4 dashboard (pnpm)
│   ├── app/
│   │   ├── components/
│   │   ├── composables/
│   │   ├── pages/
│   │   └── plugins/
│   └── nuxt.config.ts
├── functions/         # Firebase Cloud Functions (npm)
│   ├── src/
│   │   ├── flows/     # Genkit AI flows
│   │   ├── transcription/
│   │   └── index.ts
│   └── package.json
├── landing/           # Marketing + docs site (pnpm)
├── docker-compose.yml # Qdrant, Docling services
└── .mise.toml         # Task definitions

Task Runner (mise)

LegalEase uses mise for task management. View available tasks:

mise tasks

Common Tasks

# Full local development stack
mise run dev:local

# Frontend only (requires emulators running)
mise run dev

# Install all dependencies
mise run install

# Build everything
mise run build

# Build functions only
mise run build:functions

# Start Docker services
mise run services:up

# Deploy to Firebase
mise run deploy

Frontend Development

The frontend is a Nuxt 4 application with Vue 3 Composition API.

Setup

cd frontend
pnpm install

Development Server

# With emulators (recommended)
NUXT_PUBLIC_USE_EMULATORS=true pnpm dev

# With production Firebase
pnpm dev

Key Patterns

Composables (app/composables/)

Firebase-integrated composables with SSR safety:

// All composables check for server-side rendering
if (import.meta.server) return null

// Use Nuxt app for Firebase instances
const { $firestore, $storage, $functions } = useNuxtApp()
ComposablePurpose
useAuthFirebase Authentication
useCasesCase CRUD operations
useDocumentsDocument upload/management
useFirestoreDirect Firestore operations
useAIFirebase Functions calls

Firebase Plugin (app/plugins/firebase.client.ts)

Initializes Firebase and connects to emulators when NUXT_PUBLIC_USE_EMULATORS=true.

Commands

pnpm dev        # Development server
pnpm build      # Production build
pnpm lint       # ESLint check
pnpm typecheck  # TypeScript check

Functions Development

Firebase Cloud Functions use Genkit for AI flows.

Setup

cd functions
npm install

Build and Deploy

npm run build           # Compile TypeScript
firebase deploy --only functions

Key Patterns

Genkit Flows (src/flows/)

AI operations are defined as Genkit flows:

export const transcribeMediaFlow = ai.defineFlow(
  {
    name: 'transcribeMedia',
    inputSchema: TranscriptionInput,
    outputSchema: TranscriptionOutput
  },
  async (input) => {
    // Flow implementation
  }
)

Firestore Triggers (src/index.ts)

Background processing via triggers:

export const startTranscriptionJob = onDocumentWritten(
  { document: 'transcriptions/{transcriptionId}' },
  async (event) => {
    // Process when status changes to 'processing'
  }
)

Provider Pattern (src/transcription/providers/)

Pluggable AI providers:

export interface TranscriptionProvider {
  name: string
  capabilities: ProviderCapabilities
  canHandle(request: TranscriptionRequest): boolean
  transcribe(request: TranscriptionRequest): Promise<TranscriptionResult>
}

Testing Functions Locally

# Start emulators with functions
firebase emulators:start --only functions,firestore,auth,storage

# Watch function logs
firebase emulators:start | grep "function\["

Landing Site Development

The landing/docs site uses Nuxt Content.

cd landing
pnpm install
pnpm dev

Content lives in content/ as Markdown files. Changes trigger hot reload.

Adding a New Feature

1. Plan the Data Model

Define Firestore collections and types:

// frontend/app/types/myfeature.ts
export interface MyFeature {
  id?: string
  caseId: string
  // ... fields
  createdAt?: Timestamp
}

2. Create Composable

// frontend/app/composables/useMyFeature.ts
export function useMyFeature() {
  const { $firestore } = useNuxtApp()

  async function create(data: MyFeatureInput) {
    if (import.meta.server) throw new Error('Client only')
    // Firestore operations
  }

  return { create, /* ... */ }
}

3. Add Cloud Function (if needed)

// functions/src/flows/myfeature.ts
export const myFeatureFlow = ai.defineFlow(
  { name: 'myFeature', inputSchema, outputSchema },
  async (input) => {
    // AI processing
  }
)

// functions/src/index.ts
export const myFeature = onCallGenkit(
  { secrets: [googleAIApiKey] },
  myFeatureFlow
)

4. Create UI Components

<!-- frontend/app/components/MyFeature.vue -->
<script setup lang="ts">
const { create } = useMyFeature()
</script>

Code Style

TypeScript

  • Strict mode enabled
  • Prefer type over interface for simple types
  • Use import type for type-only imports

Vue

  • Composition API with <script setup>
  • SSR safety checks in composables
  • Nuxt UI components

ESLint

cd frontend
pnpm lint        # Check
pnpm lint --fix  # Auto-fix

Key rules:

  • max-lines: 500
  • max-lines-per-function: 100
  • no-console (except warn/error)

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make changes with tests
  4. Run lint and build
  5. Submit PR with description

PR Checklist

  • Code follows existing patterns
  • SSR safety for new composables
  • Documentation updated if needed
  • Lint passes (pnpm lint)
  • Build passes (mise run build)

Debugging Tips

Frontend

  • Vue DevTools for component state
  • Network tab for Firebase requests
  • console.log in composables (remove before commit)

Functions

  • Firebase Emulator UI shows function logs
  • Add console.log with prefixes: [FunctionName] message
  • Check for timeout issues in long operations

Common Issues

"X is not defined" runtime errors

  • Check import type vs import - functions need runtime imports

Emulator connection fails

  • Verify NUXT_PUBLIC_USE_EMULATORS=true is set
  • Check emulator ports are not in use

Functions timeout

  • Increase timeoutSeconds in function config
  • Check for infinite loops or hanging promises

Built with Nuxt UI • LegalEase AI © 2025