# Code Style Guide

**Last Updated:** 2026-02-15T00:00:00.000Z  
**JIRA:** RECIPE-0001

---

## Language & Version

**Primary Language:** TypeScript  
**Version:** 5.9.3  
**Framework:** SvelteKit 2.48.5 with Svelte 5.43.8  
**Node.js:** 22+ (LTS)

---

## Naming Conventions

### Files & Directories

#### SvelteKit Route Files
- Route pages: `+page.svelte`
- Route servers: `+server.ts`
- Route layouts: `+layout.svelte`
- Type definitions: `$types.ts` (auto-generated)

**Example:**
```
src/routes/api/queue/
├── [id]/
│   ├── +server.ts
│   └── retry/
│       └── +server.ts
├── stream/
│   └── +server.ts
└── +server.ts
```

#### Library Files
- **PascalCase** for classes and managers: `QueueManager.ts`, `PushNotificationService.ts`
- **kebab-case** for utilities and configs: `tandoor-config.ts`, `instagram-url.ts`
- **lowercase** for general modules: `browser.ts`, `extraction.ts`, `parser.ts`

**Examples from codebase:**
- `src/lib/server/queue/QueueManager.ts`
- `src/lib/server/tandoor-config.ts`
- `src/lib/client/PushNotificationManager.ts`

#### Test Files
Pattern: `<name>.spec.ts` or `<name>.test.ts`

**Examples:**
- `queue-manager.spec.ts`
- `instagram-url-validation.spec.ts`
- `page.svelte.spec.ts`

### Variables & Functions

#### Variables
- **camelCase** for local variables and parameters
- **SCREAMING_SNAKE_CASE** for constants

**Examples:**
```typescript
// From QueueManager.ts
private items: Map<string, QueueItem> = new Map();
private subscribers: Set<QueueUpdateCallback> = new Set();

// From parser.ts
const RECIPE_DETECTION_PROMPT = "...";
const RECIPE_EXTRACTION_PROMPT = "...";

// Local variables
const now = new Date().toISOString();
const unsubscribe = queueManager.subscribe(callback);
```

#### Functions
- **camelCase** for function names
- **Descriptive action verbs**: `enqueue`, `extractRecipe`, `uploadRecipeImage`

**Examples:**
```typescript
// From QueueManager.ts
enqueue(url: string): QueueItem { ... }
dequeue(): QueueItem | null { ... }
updateStatus(id: string, status: QueueItemStatus): void { ... }

// From extraction.ts
export async function extractTextAndThumbnail(
  url: string,
  onProgress?: ProgressCallback
): Promise<ExtractedContent> { ... }

// From parser.ts
export async function extractRecipe(text: string): Promise<Recipe> { ... }
```

### Types & Interfaces

#### Interfaces & Types
- **PascalCase** for interface names
- Prefix with `I` is **NOT** used
- Exported types use `export type` or `export interface`

**Examples:**
```typescript
// From queue/types.ts
export interface QueueItem {
  id: string;
  url: string;
  status: QueueItemStatus;
  enqueuedAt: string;
  // ...
}

export interface QueueStatusUpdate {
  type: string;
  itemId: string;
  status: QueueItemStatus;
  // ...
}

export type QueueItemStatus = 
  | 'pending' 
  | 'in_progress' 
  | 'completed' 
  | 'failed';

// From extraction.ts
export interface ExtractedContent {
  text: string;
  thumbnailUrl?: string;
}

export type ProgressCallback = (event: ProgressEvent) => void;
```

#### Zod Schemas
- **PascalCase** with `Schema` suffix
- Inferred types without suffix

**Examples:**
```typescript
// From parser.ts
const RecipeSchema = z.object({
  name: z.string(),
  description: z.string(),
  servings: z.number(),
  // ...
});

export type Recipe = z.infer<typeof RecipeSchema>;

// From tandoor.ts
const TandoorRecipeSchema = z.object({
  // ...
});

export type TandoorRecipe = z.infer<typeof TandoorRecipeSchema>;
```

### Classes

#### Class Names
- **PascalCase** for class names
- Descriptive suffixes: `Manager`, `Service`, `Processor`, `Handler`

**Examples:**
```typescript
// From QueueManager.ts
export class QueueManager {
  private items: Map<string, QueueItem> = new Map();
  // ...
}

// From QueueProcessor.ts
export class QueueProcessor {
  private processing: Set<string> = new Set();
  // ...
}

// From PushNotificationService.ts
class PushNotificationService {
  private subscriptions: Map<string, PushSubscription> = new Map();
  // ...
}
```

#### Singleton Export Pattern
```typescript
// Class definition
export class QueueManager {
  // Implementation
}

// Singleton instance export
export const queueManager = new QueueManager();
```

---

## Indentation & Formatting

### General Rules
- **Indentation:** 2 spaces (enforced by Prettier)
- **No tabs**
- **Max line length:** 100 characters (soft limit, not enforced)
- **Trailing commas:** Yes (multiline)
- **Semicolons:** Yes (always)
- **Single quotes:** Yes (enforced by Prettier)

### Code Examples

#### Function Declarations
```typescript
// From QueueManager.ts
enqueue(url: string): QueueItem {
  const now = new Date().toISOString();
  const item: QueueItem = {
    id: uuidv4(),
    url,
    status: 'pending',
    enqueuedAt: now,
    createdAt: now,
    updatedAt: now,
    phases: [
      { name: 'extraction', status: 'pending' },
      { name: 'parsing', status: 'pending' },
      { name: 'uploading', status: 'pending' }
    ],
    logs: [],
    progressEvents: [],
    retryCount: 0,
    maxRetries: 3
  };
  
  this.items.set(item.id, item);
  return item;
}
```

#### Async Functions
```typescript
// From extraction.ts
export async function extractTextAndThumbnail(
  url: string,
  onProgress?: ProgressCallback
): Promise<ExtractedContent> {
  const browser = await getBrowser();
  const context = await createBrowserContext(browser);
  const page = await context.newPage();
  
  try {
    await page.goto(url, { waitUntil: 'networkidle' });
    // ...
  } finally {
    await context.close();
  }
}
```

#### Object Destructuring
```typescript
// From route handlers
export const POST: RequestHandler = async ({ request }) => {
  const { url } = await request.json();
  // ...
};

export const GET: RequestHandler = async ({ params }) => {
  const { id } = params;
  // ...
};
```

---

## Import Patterns

### Import Order
1. External dependencies (Node.js built-ins, npm packages)
2. SvelteKit imports (`$lib`, `$app`, `$env`)
3. Relative imports (`./ `, `../`)
4. Type imports (separate from value imports when beneficial)

**Example:**
```typescript
// From QueueProcessor.ts

// External dependencies
import { v4 as uuidv4 } from 'uuid';

// SvelteKit imports
import { queueManager } from './QueueManager';
import { extractTextAndThumbnail } from '$lib/server/extraction';
import { extractRecipe } from '$lib/server/parser';
import { uploadRecipeWithIngredientsDTO, uploadRecipeImage } from '$lib/server/tandoor';
import { pushNotificationService } from '$lib/server/notifications/PushNotificationService';
import { queueConfig } from './config';

// Type imports
import type { ProgressEvent } from '$lib/server/extraction';
import type { QueueItem } from './types';
```

### Import Styles

#### Named Imports (Preferred)
```typescript
import { json } from '@sveltejs/kit';
import { queueManager } from '$lib/server/queue/QueueManager';
import { validateInstagramUrl } from '$lib/server/validation/instagram-url';
```

#### Type-Only Imports
```typescript
import type { RequestHandler } from './$types';
import type { QueueItem, QueueItemStatus } from './types';
```

#### Default Imports
```typescript
import OpenAI from 'openai';
import fs from 'fs';
import path from 'path';
```

### Export Patterns

#### Named Exports (Preferred)
```typescript
// Export functions
export async function extractRecipe(text: string): Promise<Recipe> { ... }

// Export classes
export class QueueManager { ... }

// Export constants
export const queueConfig = { ... };

// Export types
export type Recipe = z.infer<typeof RecipeSchema>;
export interface QueueItem { ... }
```

#### Singleton Pattern Export
```typescript
// Define class
export class QueueManager { ... }

// Export singleton instance
export const queueManager = new QueueManager();
```

---

## Comments & Documentation

### JSDoc Style
Used extensively for public APIs and exported functions.

**Function Documentation:**
```typescript
/**
 * Add URL to processing queue
 * 
 * @param url - Instagram URL to process
 * @returns Newly created queue item
 * 
 * @example
 * ```typescript
 * const item = queueManager.enqueue('https://instagram.com/p/abc123');
 * console.log('Queued with ID:', item.id);
 * ```
 */
enqueue(url: string): QueueItem {
  // Implementation
}
```

**Class Documentation:**
```typescript
/**
 * Singleton queue manager for processing Instagram URLs
 * 
 * Features:
 * - FIFO queue with unique IDs
 * - Status tracking and updates
 * - Progress event accumulation
 * - Retry support for failed items
 * - Pub/sub for real-time updates
 * 
 * @example
 * ```typescript
 * import { queueManager } from './QueueManager';
 * 
 * // Add item to queue
 * const item = queueManager.enqueue('https://instagram.com/p/abc123');
 * ```
 */
export class QueueManager {
  // Implementation
}
```

**Module-Level Documentation:**
```typescript
/**
 * Queue Manager - Core queue operations and event management
 * 
 * Manages an in-memory queue of Instagram URL processing jobs.
 * Provides CRUD operations and pub/sub mechanism for queue updates.
 * 
 * Architecture: Domain Layer (Hexagonal Architecture)
 * - Port: Defines queue operations interface
 * - Implementation: In-memory Map-based storage
 */
```

### Inline Comments

#### Single-line Comments
```typescript
// Set restrictive permissions
fs.chmodSync(authFile, 0o600);

// FIFO order - get oldest pending item
const pendingItems = Array.from(this.items.values())
  .filter(item => item.status === 'pending');
```

#### Block Comments (Avoided)
Single-line comments preferred. Block comments used only for large comment blocks or temporarily disabling code during development.

### TODO Comments
```typescript
// TODO: Add retry logic with exponential backoff
// FIXME: Handle race condition when multiple workers dequeue
```

---

## TypeScript Patterns

### Type Safety

#### Strict Mode Enabled
```json
// tsconfig.json
{
  "compilerOptions": {
    "strict": true,
    "forceConsistentCasingInFileNames": true
  }
}
```

#### Type Annotations
```typescript
// Explicit return types for public functions
export async function extractRecipe(text: string): Promise<Recipe> { ... }

// Explicit parameter types
function processItem(item: QueueItem, config: QueueConfig): void { ... }

// Type inference for local variables (acceptable)
const items = queueManager.getAll(); // Type inferred
```

### Union Types
```typescript
export type QueueItemStatus = 
  | 'pending' 
  | 'in_progress' 
  | 'completed' 
  | 'failed';

export type ProcessingPhase = 
  | 'extraction' 
  | 'parsing' 
  | 'uploading';

export type ProgressEventType = 
  | 'status' 
  | 'method' 
  | 'retry' 
  | 'error' 
  | 'thumbnail' 
  | 'complete';
```

### Generics
```typescript
// Generic function
async function fetchFromTandoor<T>(
  url: string,
  options: Partial<RequestInit> = { method: 'GET' }
): Promise<{ ok: boolean; data?: T; error?: string }> {
  // Implementation
}
```

---

## Svelte 5 Patterns

### Runes (Reactivity)

#### $state (Reactive Variables)
```svelte
<script lang="ts">
  let count = $state(0);
  let items = $state<QueueItem[]>([]);
</script>
```

#### $props (Component Props)
```svelte
<script lang="ts">
  let { 
    recipe = null,
    tandoorEnabled = false,
    onRetry,
    onImportToTandoor 
  } = $props<{
    recipe: Recipe | null;
    tandoorEnabled: boolean;
    onRetry: () => void;
    onImportToTandoor: () => void;
  }>();
</script>
```

#### $derived (Computed Values)
```svelte
<script lang="ts">
  let count = $state(0);
  let double = $derived(count * 2);
</script>
```

#### $effect (Side Effects)
```svelte
<script lang="ts">
  let url = $state('');
  
  $effect(() => {
    console.log('URL changed:', url);
  });
</script>
```

### Component Structure
```svelte
<script lang="ts">
  // Imports
  import { onMount } from 'svelte';
  
  // Props
  let { items } = $props<{ items: Item[] }>();
  
  // State
  let loading = $state(false);
  
  // Derived state
  let count = $derived(items.length);
  
  // Functions
  function handleClick() {
    // ...
  }
  
  // Effects
  $effect(() => {
    // Side effects
  });
</script>

<!-- Template -->
<div>
  <!-- Markup -->
</div>

<!-- Styles -->
<style>
  /* Component-scoped styles */
</style>
```

---

## Error Handling

### Custom Error Classes
```typescript
// From api/errors.ts
export class ValidationError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'ValidationError';
  }
}

export class NotFoundError extends Error {
  constructor(resource: string) {
    super(`${resource} not found`);
    this.name = 'NotFoundError';
  }
}

export class ConflictError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'ConflictError';
  }
}
```

### Try-Catch Pattern
```typescript
export const POST: RequestHandler = async ({ request }) => {
  try {
    const { url } = await request.json();
    
    if (!url) {
      throw new ValidationError('URL is required');
    }
    
    const item = queueManager.enqueue(url);
    return json(item, { status: 201 });
    
  } catch (error) {
    return handleApiError(error);
  }
};
```

---

## Linting Configuration

### ESLint
**Config:** `eslint.config.js`

- Base: `@eslint/js` recommended
- TypeScript: `typescript-eslint` recommended
- Svelte: `eslint-plugin-svelte` recommended  
- Formatting: `eslint-config-prettier`

**Rules:**
```javascript
{
  rules: {
    "no-undef": 'off'  // TypeScript handles this
  }
}
```

### Prettier
**Config:** `.prettierrc`

```json
{
  "useTabs": false,
  "singleQuote": true,
  "trailingComma": "es5",
  "printWidth": 100,
  "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"]
}
```

---

## Testing Conventions

### Test Structure
```typescript
import { describe, it, expect, beforeEach, afterEach } from 'vitest';

describe('QueueManager', () => {
  let manager: QueueManager;
  
  beforeEach(() => {
    manager = new QueueManager();
  });
  
  it('should enqueue items', () => {
    const item = manager.enqueue('https://instagram.com/p/test');
    expect(item.status).toBe('pending');
  });
  
  it('should dequeue items in FIFO order', () => {
    manager.enqueue('url1');
    manager.enqueue('url2');
    
    const first = manager.dequeue();
    expect(first?.url).toBe('url1');
  });
});
```

### Mock Pattern
```typescript
vi.mock('$lib/server/extraction', () => ({
  extractTextAndThumbnail: vi.fn().mockResolvedValue({
    text: 'Mock text',
    thumbnailUrl: 'https://example.com/thumb.jpg'
  })
}));
```

---

## File Headers

### Module Documentation Pattern
Every major module includes a header comment:

```typescript
/**
 * Module Name - Brief Description
 * 
 * Detailed description of the module's purpose and functionality.
 * 
 * Architecture: Layer Name (Hexagonal Architecture)
 * - Port: Description of port interface
 * - Implementation: Description of concrete implementation
 */
```

**Example:**
```typescript
/**
 * Queue Manager - Core queue operations and event management
 * 
 * Manages an in-memory queue of Instagram URL processing jobs.
 * Provides CRUD operations and pub/sub mechanism for queue updates.
 * 
 * Architecture: Domain Layer (Hexagonal Architecture)
 * - Port: Defines queue operations interface
 * - Implementation: In-memory Map-based storage
 */
```

---

## Additional Conventions

### Environment Variables
```typescript
import { env } from '$env/dynamic/private';

const apiKey = env.OPENAI_API_KEY;
const tandoorUrl = env.TANDOOR_URL || null;
```

### Date Handling
ISO8601 strings throughout the application:
```typescript
const now = new Date().toISOString();
// Output: "2026-02-15T12:30:45.123Z"
```

### Null vs Undefined
- `null`: Intentional absence of value
- `undefined`: Not yet initialized or optional parameters
- Prefer `null` for API responses and data structures

### Async/Await
Always preferred over Promise chains:
```typescript
// Preferred
async function fetchData() {
  const response = await fetch(url);
  const data = await response.json();
  return data;
}

// Avoid
function fetchData() {
  return fetch(url)
    .then(response => response.json())
    .then(data => data);
}
```

---

**Document Version:** 1.0  
**Enforced By:** ESLint, Prettier, TypeScript  
**Generated by:** Initializer Agent
