# 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
