# 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: `.spec.ts` or `.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 = new Map(); private subscribers: Set = 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 { ... } // From parser.ts export async function extractRecipe(text: string): Promise { ... } ``` ### 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; // From tandoor.ts const TandoorRecipeSchema = z.object({ // ... }); export type TandoorRecipe = z.infer; ``` ### Classes #### Class Names - **PascalCase** for class names - Descriptive suffixes: `Manager`, `Service`, `Processor`, `Handler` **Examples:** ```typescript // From QueueManager.ts export class QueueManager { private items: Map = new Map(); // ... } // From QueueProcessor.ts export class QueueProcessor { private processing: Set = new Set(); // ... } // From PushNotificationService.ts class PushNotificationService { private subscriptions: Map = 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 { 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 { ... } // Export classes export class QueueManager { ... } // Export constants export const queueConfig = { ... }; // Export types export type Recipe = z.infer; 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 { ... } // 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( url: string, options: Partial = { method: 'GET' } ): Promise<{ ok: boolean; data?: T; error?: string }> { // Implementation } ``` --- ## Svelte 5 Patterns ### Runes (Reactivity) #### $state (Reactive Variables) ```svelte ``` #### $props (Component Props) ```svelte ``` #### $derived (Computed Values) ```svelte ``` #### $effect (Side Effects) ```svelte ``` ### Component Structure ```svelte
``` --- ## 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