@mrck-labs/vanaheim-shared 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types/linear.ts","../src/constants/index.ts","../src/utils/formatters.ts","../src/utils/generators.ts","../src/utils/validators.ts","../src/utils/calculations.ts","../src/query/index.ts","../src/atoms/focus.ts","../src/date/index.ts"],"sourcesContent":["/**\n * Linear API Types\n *\n * Types for interacting with Linear's GraphQL API.\n */\n\n// ============================================================================\n// User Types\n// ============================================================================\n\nexport interface LinearUser {\n id: string\n name: string\n email: string\n avatarUrl: string | null\n}\n\n// ============================================================================\n// Project Types\n// ============================================================================\n\nexport interface LinearProject {\n id: string\n name: string\n color: string\n}\n\n// ============================================================================\n// State Types\n// ============================================================================\n\nexport type LinearStateType =\n | 'backlog'\n | 'unstarted'\n | 'started'\n | 'completed'\n | 'canceled'\n\nexport interface LinearState {\n id: string\n name: string\n color: string\n type: LinearStateType\n}\n\n// ============================================================================\n// Issue Types\n// ============================================================================\n\nexport type LinearPriority = 0 | 1 | 2 | 3 | 4\n\nexport const LINEAR_PRIORITY_LABELS: Record<LinearPriority, string> = {\n 0: 'No priority',\n 1: 'Urgent',\n 2: 'High',\n 3: 'Medium',\n 4: 'Low',\n}\n\nexport interface LinearIssue {\n id: string\n identifier: string\n title: string\n description: string | null\n priority: LinearPriority\n priorityLabel: string\n dueDate: string | null\n url: string\n state: LinearState | null\n project: LinearProject | null\n createdAt: string\n updatedAt: string\n}\n\n// ============================================================================\n// Query Types\n// ============================================================================\n\nexport interface IssueQueryOptions {\n projectId?: string\n search?: string\n cursor?: string\n limit?: number\n}\n\nexport interface PaginatedIssues {\n issues: LinearIssue[]\n hasNextPage: boolean\n endCursor: string | null\n totalCount: number\n}\n\n","/**\n * Shared Constants\n *\n * Common constants used across Vanaheim apps.\n */\n\n// ============================================================================\n// Currency\n// ============================================================================\n\nexport const CURRENCIES = ['CHF', 'USD', 'EUR', 'PLN'] as const\nexport type Currency = (typeof CURRENCIES)[number]\n\nexport const CURRENCY_SYMBOLS: Record<Currency, string> = {\n CHF: 'CHF',\n USD: '$',\n EUR: '€',\n PLN: 'zł',\n}\n\nexport const CURRENCY_NAMES: Record<Currency, string> = {\n CHF: 'Swiss Franc',\n USD: 'US Dollar',\n EUR: 'Euro',\n PLN: 'Polish Złoty',\n}\n\n// ============================================================================\n// Frequency\n// ============================================================================\n\nexport const FREQUENCIES = [\n 'monthly',\n 'yearly',\n '6-monthly',\n 'weekly',\n 'one-time',\n] as const\nexport type Frequency = (typeof FREQUENCIES)[number]\n\nexport const FREQUENCY_LABELS: Record<Frequency, string> = {\n monthly: 'Monthly',\n yearly: 'Yearly',\n '6-monthly': 'Every 6 Months',\n weekly: 'Weekly',\n 'one-time': 'One Time',\n}\n\nexport const FREQUENCY_MULTIPLIERS: Record<Frequency, number> = {\n monthly: 12,\n yearly: 1,\n '6-monthly': 2,\n weekly: 52,\n 'one-time': 1,\n}\n\n// ============================================================================\n// Focus\n// ============================================================================\n\nexport const DEFAULT_FOCUS_DURATIONS = [15, 25, 30, 45, 60, 90] as const\nexport type FocusDuration = (typeof DEFAULT_FOCUS_DURATIONS)[number]\n\nexport const FOCUS_STATUS = ['active', 'completed', 'abandoned'] as const\nexport type FocusStatus = (typeof FOCUS_STATUS)[number]\n\n// ============================================================================\n// Linear\n// ============================================================================\n\nexport const LINEAR_PRIORITIES = [0, 1, 2, 3, 4] as const\nexport type LinearPriorityValue = (typeof LINEAR_PRIORITIES)[number]\n\nexport const LINEAR_PRIORITY_COLORS: Record<LinearPriorityValue, string> = {\n 0: '#6b7280', // No priority - gray\n 1: '#ef4444', // Urgent - red\n 2: '#f97316', // High - orange\n 3: '#eab308', // Medium - yellow\n 4: '#3b82f6', // Low - blue\n}\n\n// ============================================================================\n// Cloud Agents\n// ============================================================================\n\nexport const CLOUD_AGENT_STATUSES = [\n 'CREATING',\n 'RUNNING',\n 'FINISHED',\n 'FAILED',\n 'CANCELLED',\n] as const\n\nexport const CLOUD_AGENT_STATUS_EMOJI: Record<string, string> = {\n CREATING: '🔨',\n RUNNING: '⏳',\n FINISHED: '✅',\n FAILED: '❌',\n CANCELLED: '🚫',\n}\n\nexport const CLOUD_AGENT_STATUS_COLORS: Record<string, string> = {\n CREATING: '#3b82f6',\n RUNNING: '#3b82f6',\n FINISHED: '#10b981',\n FAILED: '#ef4444',\n CANCELLED: '#6b7280',\n}\n\n// ============================================================================\n// API URLs\n// ============================================================================\n\nexport const API_URLS = {\n CURSOR_CLOUD: 'https://api.cursor.com',\n LINEAR_GRAPHQL: 'https://api.linear.app/graphql',\n} as const\n\n// ============================================================================\n// Settings Keys\n// ============================================================================\n\nexport const SETTING_KEYS = {\n // AI\n AI_MODEL: 'ai_model',\n AI_REASONING_ENABLED: 'ai_reasoning_enabled',\n\n // API Keys\n OPENAI_API_KEY: 'openai_api_key',\n ANTHROPIC_API_KEY: 'anthropic_api_key',\n CURSOR_API_KEY: 'cursor_api_key',\n LINEAR_API_KEY: 'linear_api_key',\n\n // Google\n GOOGLE_CLIENT_ID: 'google_client_id',\n GOOGLE_CLIENT_SECRET: 'google_client_secret',\n GOOGLE_CALENDAR_TOKENS: 'google_calendar_tokens',\n SELECTED_CALENDAR_IDS: 'selected_calendar_ids',\n} as const\n\nexport type SettingKey = (typeof SETTING_KEYS)[keyof typeof SETTING_KEYS]\n\n","/**\n * Formatting Utilities\n *\n * Pure functions for formatting data.\n */\n\n/**\n * Format seconds into MM:SS format\n * @example formatTime(125) => \"02:05\"\n */\nexport function formatTime(seconds: number): string {\n const mins = Math.floor(seconds / 60)\n const secs = seconds % 60\n return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`\n}\n\n/**\n * Format seconds into human-readable total time\n * @example formatTotalTime(3700) => \"1h 1m\"\n * @example formatTotalTime(1800) => \"30m\"\n */\nexport function formatTotalTime(seconds: number): string {\n const hours = Math.floor(seconds / 3600)\n const mins = Math.floor((seconds % 3600) / 60)\n if (hours > 0) {\n return `${hours}h ${mins}m`\n }\n return `${mins}m`\n}\n\n/**\n * Format a number as currency\n * Uses Swiss German locale for authentic Swiss formatting (apostrophe as thousands separator)\n * @example formatCurrency(1234.56, 'CHF') => \"CHF 1'234.56\"\n */\nexport function formatCurrency(\n amount: number,\n currency: string,\n locale: string = 'de-CH'\n): string {\n return new Intl.NumberFormat(locale, {\n style: 'currency',\n currency,\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(amount)\n}\n\n/**\n * Format a date as relative time (e.g., \"2h ago\", \"3d ago\")\n */\nexport function formatRelativeTime(dateStr: string): string {\n const date = new Date(dateStr)\n const now = new Date()\n const diffMs = now.getTime() - date.getTime()\n const diffMins = Math.floor(diffMs / 60000)\n const diffHours = Math.floor(diffMins / 60)\n const diffDays = Math.floor(diffHours / 24)\n\n if (diffMins < 1) return 'Just now'\n if (diffMins < 60) return `${diffMins}m ago`\n if (diffHours < 24) return `${diffHours}h ago`\n if (diffDays < 7) return `${diffDays}d ago`\n return date.toLocaleDateString()\n}\n\n/**\n * Format a date string to a readable format\n * @example formatDate('2024-01-15') => \"Jan 15, 2024\"\n */\nexport function formatDate(\n dateStr: string,\n options: Intl.DateTimeFormatOptions = {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n }\n): string {\n return new Date(dateStr).toLocaleDateString('en-US', options)\n}\n\n/**\n * Format a due date with context (Today, Tomorrow, Overdue, etc.)\n */\nexport function formatDueDate(dueDate: string | null): {\n text: string\n isOverdue: boolean\n} {\n if (!dueDate) return { text: '', isOverdue: false }\n\n const due = new Date(dueDate)\n const today = new Date()\n today.setHours(0, 0, 0, 0)\n\n const tomorrow = new Date(today)\n tomorrow.setDate(tomorrow.getDate() + 1)\n\n const dueDay = new Date(due)\n dueDay.setHours(0, 0, 0, 0)\n\n const isOverdue = dueDay < today\n\n if (dueDay.getTime() === today.getTime()) {\n return { text: 'Today', isOverdue: false }\n } else if (dueDay.getTime() === tomorrow.getTime()) {\n return { text: 'Tomorrow', isOverdue: false }\n } else if (isOverdue) {\n const daysAgo = Math.ceil(\n (today.getTime() - dueDay.getTime()) / (1000 * 60 * 60 * 24)\n )\n return { text: `${daysAgo}d overdue`, isOverdue: true }\n } else {\n return {\n text: due.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }),\n isOverdue: false,\n }\n }\n}\n\n/**\n * Truncate a string with ellipsis\n * @example truncate(\"Hello World\", 5) => \"Hello...\"\n */\nexport function truncate(str: string, maxLength: number): string {\n if (str.length <= maxLength) return str\n return str.slice(0, maxLength) + '...'\n}\n\n/**\n * Extract repo name from GitHub URL\n * @example getRepoName(\"https://github.com/owner/repo\") => \"owner/repo\"\n */\nexport function getRepoName(url: string): string {\n const match = url.match(/github\\.com\\/(.+)$/)\n return match ? match[1] : url\n}\n\n","/**\n * ID and Data Generators\n *\n * Pure functions for generating IDs and data.\n */\n\n/**\n * Generate a unique ID\n * Uses crypto.randomUUID if available, falls back to timestamp-based ID\n *\n * Note: This works in both Node.js and browser environments.\n * React Native needs the fallback since crypto.randomUUID isn't available.\n */\nexport function generateId(): string {\n // Check if crypto.randomUUID is available (Node.js 19+, modern browsers)\n if (\n typeof crypto !== 'undefined' &&\n typeof crypto.randomUUID === 'function'\n ) {\n return crypto.randomUUID()\n }\n\n // Fallback: UUID v4-like implementation\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0\n const v = c === 'x' ? r : (r & 0x3) | 0x8\n return v.toString(16)\n })\n}\n\n/**\n * Generate a short ID (timestamp + random)\n * Format: {timestamp}-{random7chars}\n * @example \"1732547123456-k8f3j2m\"\n */\nexport function generateShortId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n}\n\n/**\n * Generate a random color in hex format\n */\nexport function generateRandomColor(): string {\n const colors = [\n '#ef4444', // red\n '#f97316', // orange\n '#eab308', // yellow\n '#22c55e', // green\n '#14b8a6', // teal\n '#3b82f6', // blue\n '#8b5cf6', // violet\n '#ec4899', // pink\n ]\n return colors[Math.floor(Math.random() * colors.length)]\n}\n\n","/**\n * Validation Utilities\n *\n * Pure functions for validating data.\n */\n\n/**\n * Check if a string is a valid email\n */\nexport function isValidEmail(email: string): boolean {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n return emailRegex.test(email)\n}\n\n/**\n * Check if a string is a valid URL\n */\nexport function isValidUrl(url: string): boolean {\n try {\n new URL(url)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Check if a string is a valid ISO date (YYYY-MM-DD)\n */\nexport function isValidISODate(dateStr: string): boolean {\n const regex = /^\\d{4}-\\d{2}-\\d{2}$/\n if (!regex.test(dateStr)) return false\n\n const date = new Date(dateStr)\n return !isNaN(date.getTime())\n}\n\n/**\n * Check if a value is a valid currency code\n */\nexport function isValidCurrency(currency: string): boolean {\n const validCurrencies = ['CHF', 'USD', 'EUR', 'PLN']\n return validCurrencies.includes(currency)\n}\n\n/**\n * Check if a value is a valid frequency\n */\nexport function isValidFrequency(frequency: string): boolean {\n const validFrequencies = ['monthly', 'yearly', '6-monthly', 'weekly', 'one-time']\n return validFrequencies.includes(frequency)\n}\n\n/**\n * Validate a positive number\n */\nexport function isPositiveNumber(value: unknown): value is number {\n return typeof value === 'number' && !isNaN(value) && value > 0\n}\n\n/**\n * Validate a non-empty string\n */\nexport function isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0\n}\n\n","/**\n * Calculation Utilities\n *\n * Pure functions for business logic calculations.\n */\n\nimport type { Expense, Income } from '../types/database'\nimport { FREQUENCY_MULTIPLIERS, type Frequency } from '../constants'\n\n/**\n * Convert an amount to yearly based on frequency\n */\nexport function toYearlyAmount(amount: number, frequency: Frequency): number {\n const multiplier = FREQUENCY_MULTIPLIERS[frequency] || 1\n return amount * multiplier\n}\n\n/**\n * Convert an amount to monthly based on frequency\n */\nexport function toMonthlyAmount(amount: number, frequency: Frequency): number {\n const yearly = toYearlyAmount(amount, frequency)\n return yearly / 12\n}\n\n/**\n * Calculate total expenses (monthly)\n */\nexport function calculateMonthlyExpenses(expenses: Expense[]): number {\n return expenses\n .filter((e) => e.isActive)\n .reduce((total, expense) => {\n const monthly = toMonthlyAmount(\n expense.amount,\n expense.frequency as Frequency\n )\n // Apply share percentage\n const myShare = (monthly * expense.sharePercentage) / 100\n return total + myShare\n }, 0)\n}\n\n/**\n * Calculate total income (monthly)\n */\nexport function calculateMonthlyIncome(incomes: Income[]): number {\n return incomes\n .filter((i) => i.isActive)\n .reduce((total, income) => {\n const monthly = toMonthlyAmount(income.amount, income.frequency as Frequency)\n return total + monthly\n }, 0)\n}\n\n/**\n * Calculate savings (income - expenses)\n */\nexport function calculateMonthlySavings(\n incomes: Income[],\n expenses: Expense[]\n): number {\n return calculateMonthlyIncome(incomes) - calculateMonthlyExpenses(expenses)\n}\n\n/**\n * Calculate savings rate as percentage\n */\nexport function calculateSavingsRate(\n incomes: Income[],\n expenses: Expense[]\n): number {\n const income = calculateMonthlyIncome(incomes)\n if (income === 0) return 0\n\n const savings = calculateMonthlySavings(incomes, expenses)\n return (savings / income) * 100\n}\n\n/**\n * Calculate lieu day balance\n */\nexport function calculateLieuBalance(\n lieuDays: Array<{ type: 'earned' | 'used' }>\n): number {\n return lieuDays.reduce((balance, day) => {\n return day.type === 'earned' ? balance + 1 : balance - 1\n }, 0)\n}\n\n/**\n * Calculate focus time stats for a period\n */\nexport function calculateFocusStats(\n sessions: Array<{ actualSeconds: number; status: string }>\n): {\n totalSeconds: number\n completedCount: number\n abandonedCount: number\n completionRate: number\n} {\n const completed = sessions.filter((s) => s.status === 'completed')\n const abandoned = sessions.filter((s) => s.status === 'abandoned')\n\n const totalSeconds = completed.reduce((sum, s) => sum + s.actualSeconds, 0)\n const completionRate =\n sessions.length > 0 ? (completed.length / sessions.length) * 100 : 0\n\n return {\n totalSeconds,\n completedCount: completed.length,\n abandonedCount: abandoned.length,\n completionRate,\n }\n}\n\n","/**\n * Query Keys Factory\n *\n * Centralized query key management for TanStack Query.\n * Used by both desktop and mobile apps for consistent cache invalidation.\n *\n * @example\n * import { queryKeys } from '@mrck-labs/vanaheim-shared/query'\n *\n * // In a query hook\n * useQuery({\n * queryKey: queryKeys.focusSessions.today(),\n * queryFn: ...\n * })\n *\n * // Invalidating\n * queryClient.invalidateQueries({ queryKey: queryKeys.focusSessions.all })\n */\n\n// ============================================================================\n// Query Keys Factory\n// ============================================================================\n\nexport const queryKeys = {\n // -------------------------------------------------------------------------\n // Expenses\n // -------------------------------------------------------------------------\n expenses: {\n all: ['expenses'] as const,\n lists: () => [...queryKeys.expenses.all, 'list'] as const,\n list: (filters?: { isActive?: boolean; categoryId?: string }) =>\n [...queryKeys.expenses.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.expenses.all, 'detail', id] as const,\n },\n\n // Expense Categories\n expenseCategories: {\n all: ['expenseCategories'] as const,\n list: () => [...queryKeys.expenseCategories.all, 'list'] as const,\n },\n\n // Expense Payments\n expensePayments: {\n all: ['expensePayments'] as const,\n lists: () => [...queryKeys.expensePayments.all, 'list'] as const,\n list: (filters?: { expenseId?: string; dateFrom?: string; dateTo?: string }) =>\n [...queryKeys.expensePayments.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.expensePayments.all, 'detail', id] as const,\n },\n\n // -------------------------------------------------------------------------\n // Income\n // -------------------------------------------------------------------------\n incomes: {\n all: ['incomes'] as const,\n lists: () => [...queryKeys.incomes.all, 'list'] as const,\n list: (filters?: { isActive?: boolean; categoryId?: string }) =>\n [...queryKeys.incomes.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.incomes.all, 'detail', id] as const,\n },\n\n // Income Categories\n incomeCategories: {\n all: ['incomeCategories'] as const,\n list: () => [...queryKeys.incomeCategories.all, 'list'] as const,\n },\n\n // Income Payments\n incomePayments: {\n all: ['incomePayments'] as const,\n lists: () => [...queryKeys.incomePayments.all, 'list'] as const,\n list: (filters?: { incomeId?: string; dateFrom?: string; dateTo?: string }) =>\n [...queryKeys.incomePayments.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.incomePayments.all, 'detail', id] as const,\n },\n\n // Exchange Rates\n exchangeRates: {\n all: ['exchangeRates'] as const,\n list: () => [...queryKeys.exchangeRates.all, 'list'] as const,\n },\n\n // -------------------------------------------------------------------------\n // Focus\n // -------------------------------------------------------------------------\n focusSessions: {\n all: ['focusSessions'] as const,\n lists: () => [...queryKeys.focusSessions.all, 'list'] as const,\n list: (filters?: {\n date?: string\n categoryId?: string\n status?: string\n startedAfter?: string\n startedBefore?: string\n }) => [...queryKeys.focusSessions.lists(), filters] as const,\n today: () => [...queryKeys.focusSessions.all, 'today'] as const,\n active: () => [...queryKeys.focusSessions.all, 'active'] as const,\n detail: (id: string) => [...queryKeys.focusSessions.all, 'detail', id] as const,\n },\n\n // Focus Categories\n focusCategories: {\n all: ['focusCategories'] as const,\n list: () => [...queryKeys.focusCategories.all, 'list'] as const,\n },\n\n // -------------------------------------------------------------------------\n // Settings\n // -------------------------------------------------------------------------\n settings: {\n all: ['settings'] as const,\n detail: (key: string) => [...queryKeys.settings.all, key] as const,\n },\n\n // -------------------------------------------------------------------------\n // EF Work (Lieu Days & Links)\n // -------------------------------------------------------------------------\n lieuDays: {\n all: ['lieuDays'] as const,\n list: () => [...queryKeys.lieuDays.all, 'list'] as const,\n balance: () => [...queryKeys.lieuDays.all, 'balance'] as const,\n detail: (id: string) => [...queryKeys.lieuDays.all, 'detail', id] as const,\n },\n\n efLinks: {\n all: ['efLinks'] as const,\n list: () => [...queryKeys.efLinks.all, 'list'] as const,\n detail: (id: string) => [...queryKeys.efLinks.all, 'detail', id] as const,\n },\n\n // -------------------------------------------------------------------------\n // Banking\n // -------------------------------------------------------------------------\n bankConnections: {\n all: ['bankConnections'] as const,\n list: () => [...queryKeys.bankConnections.all, 'list'] as const,\n detail: (id: string) => [...queryKeys.bankConnections.all, 'detail', id] as const,\n },\n\n bankTransactions: {\n all: ['bankTransactions'] as const,\n lists: () => [...queryKeys.bankTransactions.all, 'list'] as const,\n list: (filters?: { connectionId?: string; dateFrom?: string; dateTo?: string }) =>\n [...queryKeys.bankTransactions.lists(), filters] as const,\n stats: (connectionId?: string) =>\n [...queryKeys.bankTransactions.all, 'stats', connectionId] as const,\n },\n\n // -------------------------------------------------------------------------\n // Health\n // -------------------------------------------------------------------------\n healthCategories: {\n all: ['healthCategories'] as const,\n list: () => [...queryKeys.healthCategories.all, 'list'] as const,\n detail: (id: string) => [...queryKeys.healthCategories.all, 'detail', id] as const,\n },\n\n healthHabits: {\n all: ['healthHabits'] as const,\n lists: () => [...queryKeys.healthHabits.all, 'list'] as const,\n list: (filters?: { categoryId?: string; isActive?: boolean }) =>\n [...queryKeys.healthHabits.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.healthHabits.all, 'detail', id] as const,\n },\n\n healthCompletions: {\n all: ['healthCompletions'] as const,\n lists: () => [...queryKeys.healthCompletions.all, 'list'] as const,\n list: (filters?: { habitId?: string; dateFrom?: string; dateTo?: string }) =>\n [...queryKeys.healthCompletions.lists(), filters] as const,\n forHabit: (habitId: string) => [...queryKeys.healthCompletions.all, 'habit', habitId] as const,\n forDate: (date: string) => [...queryKeys.healthCompletions.all, 'date', date] as const,\n },\n\n healthStats: {\n all: ['healthStats'] as const,\n forDate: (date?: string) => [...queryKeys.healthStats.all, date ?? 'today'] as const,\n },\n\n // -------------------------------------------------------------------------\n // Prompts\n // -------------------------------------------------------------------------\n promptCategories: {\n all: ['promptCategories'] as const,\n list: () => [...queryKeys.promptCategories.all, 'list'] as const,\n detail: (id: string) => [...queryKeys.promptCategories.all, 'detail', id] as const,\n },\n\n promptLabels: {\n all: ['promptLabels'] as const,\n list: () => [...queryKeys.promptLabels.all, 'list'] as const,\n detail: (id: string) => [...queryKeys.promptLabels.all, 'detail', id] as const,\n },\n\n prompts: {\n all: ['prompts'] as const,\n lists: () => [...queryKeys.prompts.all, 'list'] as const,\n list: (filters?: {\n categoryId?: string\n labelId?: string\n isFavorite?: boolean\n search?: string\n }) => [...queryKeys.prompts.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.prompts.all, 'detail', id] as const,\n },\n\n // -------------------------------------------------------------------------\n // Training\n // -------------------------------------------------------------------------\n trainingActivities: {\n all: ['trainingActivities'] as const,\n list: () => [...queryKeys.trainingActivities.all, 'list'] as const,\n detail: (id: string) => [...queryKeys.trainingActivities.all, 'detail', id] as const,\n byStravaType: (stravaType: string) =>\n [...queryKeys.trainingActivities.all, 'strava', stravaType] as const,\n },\n\n trainingSessions: {\n all: ['trainingSessions'] as const,\n lists: () => [...queryKeys.trainingSessions.all, 'list'] as const,\n list: (filters?: {\n activityId?: string\n dateFrom?: string\n dateTo?: string\n source?: 'manual' | 'strava'\n }) => [...queryKeys.trainingSessions.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.trainingSessions.all, 'detail', id] as const,\n byStravaId: (stravaActivityId: string) =>\n [...queryKeys.trainingSessions.all, 'strava', stravaActivityId] as const,\n },\n\n plannedSessions: {\n all: ['plannedSessions'] as const,\n lists: () => [...queryKeys.plannedSessions.all, 'list'] as const,\n list: (filters?: {\n activityId?: string\n dateFrom?: string\n dateTo?: string\n isCompleted?: boolean\n }) => [...queryKeys.plannedSessions.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.plannedSessions.all, 'detail', id] as const,\n upcoming: () => [...queryKeys.plannedSessions.all, 'upcoming'] as const,\n },\n\n races: {\n all: ['races'] as const,\n lists: () => [...queryKeys.races.all, 'list'] as const,\n list: (filters?: {\n type?: 'race' | 'event' | 'goal'\n status?: 'upcoming' | 'completed' | 'dns' | 'dnf'\n dateFrom?: string\n dateTo?: string\n }) => [...queryKeys.races.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.races.all, 'detail', id] as const,\n next: () => [...queryKeys.races.all, 'next'] as const,\n },\n\n trainingStats: {\n all: ['trainingStats'] as const,\n forRange: (dateFrom?: string, dateTo?: string) =>\n [...queryKeys.trainingStats.all, dateFrom, dateTo] as const,\n },\n\n // Strava\n strava: {\n all: ['strava'] as const,\n athlete: () => [...queryKeys.strava.all, 'athlete'] as const,\n connected: () => [...queryKeys.strava.all, 'connected'] as const,\n activities: (options?: { after?: number; before?: number }) =>\n [...queryKeys.strava.all, 'activities', options] as const,\n },\n\n // -------------------------------------------------------------------------\n // Journal\n // -------------------------------------------------------------------------\n journalEntries: {\n all: ['journalEntries'] as const,\n lists: () => [...queryKeys.journalEntries.all, 'list'] as const,\n list: (filters?: { dateFrom?: string; dateTo?: string }) =>\n [...queryKeys.journalEntries.lists(), filters] as const,\n forDate: (date: string) => [...queryKeys.journalEntries.all, 'date', date] as const,\n },\n\n // Journal data (aggregated)\n journalData: {\n all: ['journalData'] as const,\n forDate: (date: string) => [...queryKeys.journalData.all, date] as const,\n },\n\n // -------------------------------------------------------------------------\n // Chat\n // -------------------------------------------------------------------------\n chatConversations: {\n all: ['chatConversations'] as const,\n list: () => [...queryKeys.chatConversations.all, 'list'] as const,\n detail: (id: string) => [...queryKeys.chatConversations.all, 'detail', id] as const,\n },\n\n chatMessages: {\n all: ['chatMessages'] as const,\n forConversation: (conversationId: string) =>\n [...queryKeys.chatMessages.all, 'conversation', conversationId] as const,\n },\n\n // -------------------------------------------------------------------------\n // Google Calendar\n // -------------------------------------------------------------------------\n googleCalendar: {\n all: ['googleCalendar'] as const,\n calendars: () => [...queryKeys.googleCalendar.all, 'calendars'] as const,\n events: (calendarId: string, timeMin?: string, timeMax?: string) =>\n [...queryKeys.googleCalendar.all, 'events', calendarId, timeMin, timeMax] as const,\n },\n\n // -------------------------------------------------------------------------\n // Linear\n // -------------------------------------------------------------------------\n linear: {\n all: ['linear'] as const,\n issues: (filters?: { projectId?: string; status?: string }) =>\n [...queryKeys.linear.all, 'issues', filters] as const,\n projects: () => [...queryKeys.linear.all, 'projects'] as const,\n },\n} as const\n\n// ============================================================================\n// Type Exports\n// ============================================================================\n\nexport type QueryKeys = typeof queryKeys\n\n","/**\n * Focus Timer Atoms\n *\n * Shared Jotai atoms for focus timer state management.\n * Used by both desktop and mobile apps.\n *\n * These atoms manage in-memory timer state that syncs with\n * the database via TanStack Query.\n *\n * @example\n * import { timerStatusAtom, targetSecondsAtom } from '@mrck-labs/vanaheim-shared/atoms'\n * import { useAtom, useAtomValue } from 'jotai'\n *\n * const [status, setStatus] = useAtom(timerStatusAtom)\n * const remaining = useAtomValue(remainingSecondsAtom)\n */\n\nimport { atom } from 'jotai'\nimport { formatTime } from '../utils/formatters'\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Timer status values\n */\nexport type TimerStatus = 'idle' | 'running' | 'paused' | 'completed'\n\n/**\n * Active focus session info (in-memory during timer run)\n * This is separate from the database FocusSession type\n */\nexport interface ActiveSession {\n id: string\n title: string\n categoryId: string | null\n targetMinutes: number\n startedAt?: string // Optional - used by mobile for elapsed calculation\n}\n\n// ============================================================================\n// Core Timer Atoms\n// ============================================================================\n\n/**\n * Current timer status\n * - idle: No timer running\n * - running: Timer actively counting\n * - paused: Timer paused\n * - completed: Timer finished (target reached)\n */\nexport const timerStatusAtom = atom<TimerStatus>('idle')\n\n/**\n * Target duration in seconds\n * Default: 25 minutes (Pomodoro)\n */\nexport const targetSecondsAtom = atom<number>(25 * 60)\n\n/**\n * Elapsed seconds since timer started\n */\nexport const elapsedSecondsAtom = atom<number>(0)\n\n/**\n * Currently active session info (in-memory during timer run)\n */\nexport const activeSessionAtom = atom<ActiveSession | null>(null)\n\n// ============================================================================\n// Derived Timer Atoms (computed, read-only)\n// ============================================================================\n\n/**\n * Remaining seconds (target - elapsed)\n * Always returns 0 or positive\n */\nexport const remainingSecondsAtom = atom((get) => {\n const target = get(targetSecondsAtom)\n const elapsed = get(elapsedSecondsAtom)\n return Math.max(0, target - elapsed)\n})\n\n/**\n * Progress as a ratio (0 to 1)\n * 0 = just started, 1 = completed\n *\n * Note: Desktop previously used 0-100 (percentage).\n * If you need percentage, multiply by 100.\n */\nexport const progressAtom = atom((get) => {\n const target = get(targetSecondsAtom)\n const elapsed = get(elapsedSecondsAtom)\n if (target === 0) return 0\n return Math.min(1, elapsed / target)\n})\n\n/**\n * Formatted remaining time (MM:SS)\n * @example \"24:59\", \"00:30\"\n */\nexport const formattedRemainingAtom = atom((get) => {\n return formatTime(get(remainingSecondsAtom))\n})\n\n/**\n * Formatted elapsed time (MM:SS)\n * @example \"00:01\", \"25:00\"\n */\nexport const formattedElapsedAtom = atom((get) => {\n return formatTime(get(elapsedSecondsAtom))\n})\n\n/**\n * Progress as percentage (0 to 100)\n * Convenience atom for desktop compatibility\n */\nexport const progressPercentAtom = atom((get) => {\n return get(progressAtom) * 100\n})\n\n","/**\n * Date Utilities\n *\n * Pure functions for date manipulation and formatting.\n * Works in both Node.js and browser/React Native environments.\n *\n * @example\n * import { getTodayString, addDays, formatRelativeTime } from '@mrck-labs/vanaheim-shared/date'\n *\n * const today = getTodayString() // \"2024-12-16\"\n * const nextWeek = addDays(today, 7) // \"2024-12-23\"\n */\n\n// ============================================================================\n// Core Utilities\n// ============================================================================\n\n/**\n * Normalize a date to midnight (00:00:00.000)\n */\nexport function normalizeToMidnight(date: Date): Date {\n const d = new Date(date)\n d.setHours(0, 0, 0, 0)\n return d\n}\n\n/**\n * Get today's date normalized to midnight\n */\nexport function getTodayMidnight(): Date {\n return normalizeToMidnight(new Date())\n}\n\n/**\n * Format a Date object to YYYY-MM-DD string\n * @example formatDateString(new Date()) => \"2024-12-16\"\n */\nexport function formatDateString(date: Date): string {\n return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`\n}\n\n/**\n * Get today's local date string (YYYY-MM-DD)\n */\nexport function getTodayString(): string {\n return formatDateString(new Date())\n}\n\n/**\n * Parse a YYYY-MM-DD string to a local Date object\n * Avoids timezone issues by appending T00:00:00\n */\nexport function parseLocalDate(dateStr: string): Date {\n return new Date(dateStr + 'T00:00:00')\n}\n\n// ============================================================================\n// Date Checks\n// ============================================================================\n\n/**\n * Check if a date string is today\n */\nexport function isToday(dateStr: string | null): boolean {\n if (!dateStr) return false\n return dateStr === getTodayString()\n}\n\n/**\n * Check if a date string is in the past (before today)\n */\nexport function isOverdue(dateStr: string | null): boolean {\n if (!dateStr) return false\n return dateStr < getTodayString()\n}\n\n/**\n * Check if a date is due within a threshold of days\n * @param dateStr - Date in YYYY-MM-DD format\n * @param daysThreshold - Number of days to check (default: 7)\n */\nexport function isDueSoon(dateStr: string | null, daysThreshold: number = 7): boolean {\n if (!dateStr) return false\n if (isOverdue(dateStr)) return false\n\n const today = getTodayMidnight()\n const targetDate = parseLocalDate(dateStr)\n const diffMs = targetDate.getTime() - today.getTime()\n const diffDays = Math.ceil(diffMs / (1000 * 60 * 60 * 24))\n\n return diffDays >= 0 && diffDays <= daysThreshold\n}\n\n// ============================================================================\n// Date Arithmetic\n// ============================================================================\n\n/**\n * Add days to a date string\n * @param dateStr - Date in YYYY-MM-DD format\n * @param days - Number of days to add (can be negative)\n * @returns New date string in YYYY-MM-DD format\n */\nexport function addDays(dateStr: string, days: number): string {\n const date = parseLocalDate(dateStr)\n date.setDate(date.getDate() + days)\n return formatDateString(date)\n}\n\n/**\n * Subtract days from a date string\n */\nexport function subtractDays(dateStr: string, days: number): string {\n return addDays(dateStr, -days)\n}\n\n/**\n * Get yesterday's date string (YYYY-MM-DD)\n */\nexport function getYesterdayString(): string {\n return subtractDays(getTodayString(), 1)\n}\n\n/**\n * Get tomorrow's date string (YYYY-MM-DD)\n */\nexport function getTomorrowString(): string {\n return addDays(getTodayString(), 1)\n}\n\n// ============================================================================\n// ISO String Helpers (for database queries)\n// ============================================================================\n\n/**\n * Get start of day as ISO string\n * @param dateStr - Date in YYYY-MM-DD format\n * @returns ISO string like \"2024-12-15T00:00:00.000Z\"\n */\nexport function getStartOfDayISO(dateStr: string): string {\n const date = parseLocalDate(dateStr)\n return new Date(date.getFullYear(), date.getMonth(), date.getDate()).toISOString()\n}\n\n/**\n * Get end of day as ISO string\n * @param dateStr - Date in YYYY-MM-DD format\n * @returns ISO string like \"2024-12-15T23:59:59.999Z\"\n */\nexport function getEndOfDayISO(dateStr: string): string {\n const date = parseLocalDate(dateStr)\n return new Date(\n date.getFullYear(),\n date.getMonth(),\n date.getDate(),\n 23,\n 59,\n 59,\n 999\n ).toISOString()\n}\n\n// ============================================================================\n// Week Utilities\n// ============================================================================\n\n/**\n * Get the start of the week (Monday) for a given date\n */\nexport function getWeekStart(date: Date): Date {\n const d = new Date(date)\n const day = d.getDay()\n const diff = d.getDate() - day + (day === 0 ? -6 : 1) // Monday\n d.setDate(diff)\n d.setHours(0, 0, 0, 0)\n return d\n}\n\n/**\n * Get the end of the week (Sunday) for a given week start\n */\nexport function getWeekEnd(weekStart: Date): Date {\n const d = new Date(weekStart)\n d.setDate(d.getDate() + 6)\n return d\n}\n\n/**\n * Get the start of the previous week\n */\nexport function getPreviousWeek(weekStart: Date): Date {\n const d = new Date(weekStart)\n d.setDate(d.getDate() - 7)\n return d\n}\n\n/**\n * Get the start of the next week\n */\nexport function getNextWeek(weekStart: Date): Date {\n const d = new Date(weekStart)\n d.setDate(d.getDate() + 7)\n return d\n}\n\n/**\n * Get start of week (Monday) as date string\n */\nexport function getWeekStartString(baseDate: Date = new Date()): string {\n return formatDateString(getWeekStart(baseDate))\n}\n\n/**\n * Get end of week (Sunday) as date string\n */\nexport function getWeekEndString(baseDate: Date = new Date()): string {\n return formatDateString(getWeekEnd(getWeekStart(baseDate)))\n}\n\n/**\n * Format a week range for display\n * @example \"Dec 2 - 8, 2024\" or \"Nov 25 - Dec 1, 2024\"\n */\nexport function formatWeekRange(weekStart: Date): string {\n const weekEnd = getWeekEnd(weekStart)\n const startMonth = weekStart.toLocaleDateString('en-US', { month: 'short' })\n const endMonth = weekEnd.toLocaleDateString('en-US', { month: 'short' })\n\n if (startMonth === endMonth) {\n return `${startMonth} ${weekStart.getDate()} - ${weekEnd.getDate()}, ${weekStart.getFullYear()}`\n }\n\n return `${startMonth} ${weekStart.getDate()} - ${endMonth} ${weekEnd.getDate()}, ${weekStart.getFullYear()}`\n}\n\n// ============================================================================\n// Display Formatting\n// ============================================================================\n\n/**\n * Format a date string for display with full weekday\n * @example \"Monday, December 15, 2024\"\n */\nexport function formatFullDate(dateStr: string): string {\n const date = parseLocalDate(dateStr)\n return date.toLocaleDateString('en-US', {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n })\n}\n\n/**\n * Format date for display in long form\n * @example \"Monday, 9 December\"\n */\nexport function formatDateLong(dateStr: string): string {\n const date = parseLocalDate(dateStr)\n return date.toLocaleDateString('en-GB', {\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n })\n}\n\n/**\n * Format month and year\n * @example \"December 2024\"\n */\nexport function formatMonthYear(date: Date): string {\n return date.toLocaleDateString('en-US', {\n month: 'long',\n year: 'numeric',\n })\n}\n\n/**\n * Format a date/time to HH:MM format\n */\nexport function formatTimeHHMM(date: Date | string): string {\n const d = typeof date === 'string' ? new Date(date) : date\n return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n}\n\n/**\n * Format a date/time to HH:MM:SS format\n */\nexport function formatTimeWithSeconds(date: Date | string): string {\n const d = typeof date === 'string' ? new Date(date) : date\n return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })\n}\n\n// ============================================================================\n// Contextual Formatting\n// ============================================================================\n\n/**\n * Format a date string for display as a header\n * Returns \"Overdue\", \"Today\", \"Tomorrow\", or formatted date\n */\nexport function formatDateHeader(dateStr: string): string {\n const today = getTodayString()\n\n if (dateStr < today) {\n return 'Overdue'\n }\n\n if (dateStr === today) {\n return 'Today'\n }\n\n const tomorrowStr = getTomorrowString()\n if (dateStr === tomorrowStr) {\n return 'Tomorrow'\n }\n\n const date = parseLocalDate(dateStr)\n return date.toLocaleDateString('en-US', {\n weekday: 'long',\n month: 'short',\n day: 'numeric',\n })\n}\n\n/**\n * Format a due date for display in task items (simple string version)\n * Returns \"Overdue\", \"Today\", \"Tomorrow\", or formatted date\n *\n * Note: For version that returns { text, isOverdue }, use formatDueDate from utils/formatters\n */\nexport function formatDueDateString(dateStr: string | null): string {\n if (!dateStr) return 'No due date'\n\n const today = getTodayString()\n const tomorrowStr = getTomorrowString()\n\n if (dateStr < today) {\n const date = parseLocalDate(dateStr)\n return `Overdue • ${date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}`\n }\n\n if (dateStr === today) {\n return 'Today'\n }\n\n if (dateStr === tomorrowStr) {\n return 'Tomorrow'\n }\n\n const date = parseLocalDate(dateStr)\n const currentYear = new Date().getFullYear()\n const dateYear = date.getFullYear()\n\n return date.toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n year: dateYear !== currentYear ? 'numeric' : undefined,\n })\n}\n\n/**\n * Format a date/time as relative time string (extended version)\n * Accepts both Date objects and ISO strings\n * @example \"Just now\", \"5m ago\", \"2h ago\", \"3d ago\"\n *\n * Note: For simple string input, you can also use formatRelativeTime from utils/formatters\n */\nexport function formatRelativeTimeExtended(dateStr: string | Date): string {\n const date = typeof dateStr === 'string' ? new Date(dateStr) : dateStr\n const now = new Date()\n const diffMs = now.getTime() - date.getTime()\n const diffMins = Math.floor(diffMs / 60000)\n const diffHours = Math.floor(diffMins / 60)\n const diffDays = Math.floor(diffHours / 24)\n\n if (diffMins < 1) return 'Just now'\n if (diffMins < 60) return `${diffMins}m ago`\n if (diffHours < 24) return `${diffHours}h ago`\n if (diffDays < 7) return `${diffDays}d ago`\n return date.toLocaleDateString()\n}\n\n/**\n * Format a due date relative to today with contextual prefix\n * @example \"Due today\", \"Due tomorrow\", \"Due in 3 days\", \"2 days overdue\"\n */\nexport function formatRelativeDueDate(\n dateStr: string,\n options?: {\n duePrefix?: string\n overduePrefix?: string\n }\n): string {\n const { duePrefix = 'Due', overduePrefix = '' } = options || {}\n const date = parseLocalDate(dateStr)\n const now = normalizeToMidnight(new Date())\n\n const diffMs = date.getTime() - now.getTime()\n const diffDays = Math.round(diffMs / (1000 * 60 * 60 * 24))\n\n if (diffDays < 0) {\n const absDays = Math.abs(diffDays)\n return overduePrefix\n ? `${overduePrefix} ${absDays} day${absDays !== 1 ? 's' : ''} overdue`\n : `${absDays} day${absDays !== 1 ? 's' : ''} overdue`\n }\n if (diffDays === 0) return `${duePrefix} today`\n if (diffDays === 1) return `${duePrefix} tomorrow`\n if (diffDays <= 7) return `${duePrefix} in ${diffDays} days`\n\n return date.toLocaleDateString('en-GB', { day: 'numeric', month: 'short' })\n}\n\n/**\n * Format a pay/income date relative to today\n * @example \"Today\", \"Tomorrow\", \"In 3 days\", \"2 days ago\"\n */\nexport function formatRelativePayDate(dateStr: string): string {\n const date = parseLocalDate(dateStr)\n const now = normalizeToMidnight(new Date())\n\n const diffMs = date.getTime() - now.getTime()\n const diffDays = Math.round(diffMs / (1000 * 60 * 60 * 24))\n\n if (diffDays < 0) {\n const absDays = Math.abs(diffDays)\n return `${absDays} day${absDays !== 1 ? 's' : ''} ago`\n }\n if (diffDays === 0) return 'Today'\n if (diffDays === 1) return 'Tomorrow'\n if (diffDays <= 7) return `In ${diffDays} days`\n\n return date.toLocaleDateString('en-GB', { day: 'numeric', month: 'short' })\n}\n\n// ============================================================================\n// Localized Formatting\n// ============================================================================\n\nexport type DateFormat = 'short' | 'medium' | 'long' | 'weekday'\nexport type TimeFormat = 'short' | 'withSeconds'\n\n/**\n * Format a date with various preset formats\n */\nexport function formatDateLocalized(date: Date | string, format: DateFormat = 'medium'): string {\n const d = typeof date === 'string' ? new Date(date) : date\n\n switch (format) {\n case 'short':\n return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })\n case 'medium':\n return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })\n case 'long':\n return d.toLocaleDateString('en-US', {\n weekday: 'long',\n month: 'long',\n day: 'numeric',\n year: 'numeric',\n })\n case 'weekday':\n return d.toLocaleDateString('en-US', { weekday: 'short' })\n default:\n return d.toLocaleDateString()\n }\n}\n\n/**\n * Format a time with various preset formats\n */\nexport function formatTimeLocalized(date: Date | string, format: TimeFormat = 'short'): string {\n const d = typeof date === 'string' ? new Date(date) : date\n\n switch (format) {\n case 'short':\n return d.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: true })\n case 'withSeconds':\n return d.toLocaleTimeString('en-US', {\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: true,\n })\n default:\n return d.toLocaleTimeString()\n }\n}\n\n"],"mappings":"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","names":["isOverdue","date"]}
1
+ {"version":3,"sources":["../src/types/linear.ts","../src/constants/index.ts","../src/utils/formatters.ts","../src/utils/generators.ts","../src/utils/validators.ts","../src/utils/calculations.ts","../src/utils/health.ts","../src/query/index.ts","../src/atoms/focus.ts","../src/date/index.ts"],"sourcesContent":["/**\n * Linear API Types\n *\n * Types for interacting with Linear's GraphQL API.\n */\n\n// ============================================================================\n// User Types\n// ============================================================================\n\nexport interface LinearUser {\n id: string\n name: string\n email: string\n avatarUrl: string | null\n}\n\n// ============================================================================\n// Project Types\n// ============================================================================\n\nexport interface LinearProject {\n id: string\n name: string\n color: string\n}\n\n// ============================================================================\n// State Types\n// ============================================================================\n\nexport type LinearStateType =\n | 'backlog'\n | 'unstarted'\n | 'started'\n | 'completed'\n | 'canceled'\n\nexport interface LinearState {\n id: string\n name: string\n color: string\n type: LinearStateType\n}\n\n// ============================================================================\n// Issue Types\n// ============================================================================\n\nexport type LinearPriority = 0 | 1 | 2 | 3 | 4\n\nexport const LINEAR_PRIORITY_LABELS: Record<LinearPriority, string> = {\n 0: 'No priority',\n 1: 'Urgent',\n 2: 'High',\n 3: 'Medium',\n 4: 'Low',\n}\n\nexport interface LinearIssue {\n id: string\n identifier: string\n title: string\n description: string | null\n priority: LinearPriority\n priorityLabel: string\n dueDate: string | null\n url: string\n state: LinearState | null\n project: LinearProject | null\n createdAt: string\n updatedAt: string\n}\n\n// ============================================================================\n// Query Types\n// ============================================================================\n\nexport interface IssueQueryOptions {\n projectId?: string\n search?: string\n cursor?: string\n limit?: number\n}\n\nexport interface PaginatedIssues {\n issues: LinearIssue[]\n hasNextPage: boolean\n endCursor: string | null\n totalCount: number\n}\n\n","/**\n * Shared Constants\n *\n * Common constants used across Vanaheim apps.\n */\n\n// ============================================================================\n// Currency\n// ============================================================================\n\nexport const CURRENCIES = [\"CHF\", \"USD\", \"EUR\", \"PLN\"] as const;\nexport type Currency = (typeof CURRENCIES)[number];\n\nexport const CURRENCY_SYMBOLS: Record<Currency, string> = {\n CHF: \"CHF\",\n USD: \"$\",\n EUR: \"€\",\n PLN: \"zł\",\n};\n\nexport const CURRENCY_NAMES: Record<Currency, string> = {\n CHF: \"Swiss Franc\",\n USD: \"US Dollar\",\n EUR: \"Euro\",\n PLN: \"Polish Złoty\",\n};\n\n// ============================================================================\n// Frequency\n// ============================================================================\n\nexport const FREQUENCIES = [\n \"monthly\",\n \"yearly\",\n \"6-monthly\",\n \"weekly\",\n \"one-time\",\n] as const;\nexport type Frequency = (typeof FREQUENCIES)[number];\n\nexport const FREQUENCY_LABELS: Record<Frequency, string> = {\n monthly: \"Monthly\",\n yearly: \"Yearly\",\n \"6-monthly\": \"Every 6 Months\",\n weekly: \"Weekly\",\n \"one-time\": \"One Time\",\n};\n\nexport const FREQUENCY_MULTIPLIERS: Record<Frequency, number> = {\n monthly: 12,\n yearly: 1,\n \"6-monthly\": 2,\n weekly: 52,\n \"one-time\": 1,\n};\n\n/**\n * Frequency options for dropdowns/toggle groups\n * Format: { value: Frequency, label: string }\n */\nexport const FREQUENCY_OPTIONS = [\n { value: \"monthly\" as const, label: \"Monthly\" },\n { value: \"yearly\" as const, label: \"Yearly\" },\n { value: \"6-monthly\" as const, label: \"Every 6 Months\" },\n { value: \"weekly\" as const, label: \"Weekly\" },\n { value: \"one-time\" as const, label: \"One Time\" },\n];\n\n// ============================================================================\n// Focus\n// ============================================================================\n\nexport const DEFAULT_FOCUS_DURATIONS = [15, 25, 30, 45, 60, 90] as const;\nexport type FocusDuration = (typeof DEFAULT_FOCUS_DURATIONS)[number];\n\nexport const FOCUS_STATUS = [\"active\", \"completed\", \"abandoned\"] as const;\nexport type FocusStatus = (typeof FOCUS_STATUS)[number];\n\n// ============================================================================\n// Health & Habits\n// ============================================================================\n\nexport const HEALTH_FREQUENCY_TYPES = [\n \"daily\",\n \"specific_days\",\n \"times_per_week\",\n \"times_per_month\",\n] as const;\n\nexport const HEALTH_FREQUENCY_LABELS: Record<string, string> = {\n daily: \"Daily\",\n specific_days: \"Specific Days\",\n times_per_week: \"Times per Week\",\n times_per_month: \"Times per Month\",\n};\n\nexport const DAY_OPTIONS = [\n { value: 0, label: \"Sun\" },\n { value: 1, label: \"Mon\" },\n { value: 2, label: \"Tue\" },\n { value: 3, label: \"Wed\" },\n { value: 4, label: \"Thu\" },\n { value: 5, label: \"Fri\" },\n { value: 6, label: \"Sat\" },\n] as const;\n\n/**\n * Preset colors for categories, labels, and other UI elements\n */\nexport const PRESET_COLORS = [\n \"#10B981\", // Emerald\n \"#3B82F6\", // Blue\n \"#F59E0B\", // Amber\n \"#EF4444\", // Red\n \"#8B5CF6\", // Purple\n \"#EC4899\", // Pink\n \"#14B8A6\", // Teal\n \"#F97316\", // Orange\n \"#6366F1\", // Indigo\n \"#84CC16\", // Lime\n];\n\n/**\n * Preset icons/emojis for categories and UI elements\n */\nexport const PRESET_ICONS = [\n \"📝\", // Writing/Notes\n \"💻\", // Coding/Tech\n \"🎨\", // Design/Art\n \"📊\", // Analytics/Data\n \"🔧\", // Tools/Settings\n \"📚\", // Learning/Docs\n \"💡\", // Ideas/Lightbulb\n \"🚀\", // Launch/Start\n \"⚡\", // Fast/Energy\n \"🎯\", // Target/Goal\n \"💊\", // Health/Medicine\n \"🥗\", // Food/Diet\n \"💧\", // Water/Hydration\n \"🏃\", // Exercise/Running\n \"😴\", // Sleep/Rest\n \"🧘\", // Meditation/Mindfulness\n \"💪\", // Strength/Fitness\n \"🍎\", // Health/Food\n \"🔗\", // Links/Connections\n];\n\n// ============================================================================\n// Linear\n// ============================================================================\n\nexport const LINEAR_PRIORITIES = [0, 1, 2, 3, 4] as const;\nexport type LinearPriorityValue = (typeof LINEAR_PRIORITIES)[number];\n\nexport const LINEAR_PRIORITY_COLORS: Record<LinearPriorityValue, string> = {\n 0: \"#6b7280\", // No priority - gray\n 1: \"#ef4444\", // Urgent - red\n 2: \"#f97316\", // High - orange\n 3: \"#eab308\", // Medium - yellow\n 4: \"#3b82f6\", // Low - blue\n};\n\n// ============================================================================\n// Cloud Agents\n// ============================================================================\n\nexport const CLOUD_AGENT_STATUSES = [\n \"CREATING\",\n \"RUNNING\",\n \"FINISHED\",\n \"FAILED\",\n \"CANCELLED\",\n] as const;\n\nexport const CLOUD_AGENT_STATUS_EMOJI: Record<string, string> = {\n CREATING: \"🔨\",\n RUNNING: \"⏳\",\n FINISHED: \"✅\",\n FAILED: \"❌\",\n CANCELLED: \"🚫\",\n};\n\nexport const CLOUD_AGENT_STATUS_COLORS: Record<string, string> = {\n CREATING: \"#3b82f6\",\n RUNNING: \"#3b82f6\",\n FINISHED: \"#10b981\",\n FAILED: \"#ef4444\",\n CANCELLED: \"#6b7280\",\n};\n\n// ============================================================================\n// API URLs\n// ============================================================================\n\nexport const API_URLS = {\n CURSOR_CLOUD: \"https://api.cursor.com\",\n LINEAR_GRAPHQL: \"https://api.linear.app/graphql\",\n} as const;\n\n// ============================================================================\n// Settings Keys\n// ============================================================================\n\nexport const SETTING_KEYS = {\n // AI\n AI_MODEL: \"ai_model\",\n AI_REASONING_ENABLED: \"ai_reasoning_enabled\",\n\n // API Keys\n OPENAI_API_KEY: \"openai_api_key\",\n ANTHROPIC_API_KEY: \"anthropic_api_key\",\n CURSOR_API_KEY: \"cursor_api_key\",\n LINEAR_API_KEY: \"linear_api_key\",\n\n // Google\n GOOGLE_CLIENT_ID: \"google_client_id\",\n GOOGLE_CLIENT_SECRET: \"google_client_secret\",\n GOOGLE_CALENDAR_TOKENS: \"google_calendar_tokens\",\n SELECTED_CALENDAR_IDS: \"selected_calendar_ids\",\n} as const;\n\nexport type SettingKey = (typeof SETTING_KEYS)[keyof typeof SETTING_KEYS];\n","/**\n * Formatting Utilities\n *\n * Pure functions for formatting data.\n */\n\n/**\n * Format seconds into MM:SS format\n * @example formatTime(125) => \"02:05\"\n */\nexport function formatTime(seconds: number): string {\n const mins = Math.floor(seconds / 60)\n const secs = seconds % 60\n return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`\n}\n\n/**\n * Format seconds into human-readable total time\n * @example formatTotalTime(3700) => \"1h 1m\"\n * @example formatTotalTime(1800) => \"30m\"\n */\nexport function formatTotalTime(seconds: number): string {\n const hours = Math.floor(seconds / 3600)\n const mins = Math.floor((seconds % 3600) / 60)\n if (hours > 0) {\n return `${hours}h ${mins}m`\n }\n return `${mins}m`\n}\n\n/**\n * Format duration in seconds to human-readable format (includes seconds)\n * @example formatDuration(3661) => \"1h 1m\"\n * @example formatDuration(125) => \"2m 5s\"\n * @example formatDuration(45) => \"45s\"\n */\nexport function formatDuration(seconds: number): string {\n const hours = Math.floor(seconds / 3600)\n const minutes = Math.floor((seconds % 3600) / 60)\n const secs = seconds % 60\n\n if (hours > 0) {\n return `${hours}h ${minutes}m`\n } else if (minutes > 0) {\n return `${minutes}m${secs > 0 ? ` ${secs}s` : ''}`\n } else {\n return `${secs}s`\n }\n}\n\n/**\n * Format a number as currency\n * Uses Swiss German locale for authentic Swiss formatting (apostrophe as thousands separator)\n * @example formatCurrency(1234.56, 'CHF') => \"CHF 1'234.56\"\n * @example formatCurrency(1234.56) => \"CHF 1'234.56\" (defaults to CHF)\n */\nexport function formatCurrency(\n amount: number,\n currency: string = 'CHF',\n locale: string = 'de-CH'\n): string {\n return new Intl.NumberFormat(locale, {\n style: 'currency',\n currency,\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n }).format(amount)\n}\n\n/**\n * Format a date as relative time (e.g., \"2h ago\", \"3d ago\")\n */\nexport function formatRelativeTime(dateStr: string): string {\n const date = new Date(dateStr)\n const now = new Date()\n const diffMs = now.getTime() - date.getTime()\n const diffMins = Math.floor(diffMs / 60000)\n const diffHours = Math.floor(diffMins / 60)\n const diffDays = Math.floor(diffHours / 24)\n\n if (diffMins < 1) return 'Just now'\n if (diffMins < 60) return `${diffMins}m ago`\n if (diffHours < 24) return `${diffHours}h ago`\n if (diffDays < 7) return `${diffDays}d ago`\n return date.toLocaleDateString()\n}\n\n/**\n * Format a date string to a readable format\n * @example formatDate('2024-01-15') => \"Jan 15, 2024\"\n */\nexport function formatDate(\n dateStr: string,\n options: Intl.DateTimeFormatOptions = {\n month: 'short',\n day: 'numeric',\n year: 'numeric',\n }\n): string {\n return new Date(dateStr).toLocaleDateString('en-US', options)\n}\n\n/**\n * Format a due date with context (Today, Tomorrow, Overdue, etc.)\n */\nexport function formatDueDate(dueDate: string | null): {\n text: string\n isOverdue: boolean\n} {\n if (!dueDate) return { text: '', isOverdue: false }\n\n const due = new Date(dueDate)\n const today = new Date()\n today.setHours(0, 0, 0, 0)\n\n const tomorrow = new Date(today)\n tomorrow.setDate(tomorrow.getDate() + 1)\n\n const dueDay = new Date(due)\n dueDay.setHours(0, 0, 0, 0)\n\n const isOverdue = dueDay < today\n\n if (dueDay.getTime() === today.getTime()) {\n return { text: 'Today', isOverdue: false }\n } else if (dueDay.getTime() === tomorrow.getTime()) {\n return { text: 'Tomorrow', isOverdue: false }\n } else if (isOverdue) {\n const daysAgo = Math.ceil(\n (today.getTime() - dueDay.getTime()) / (1000 * 60 * 60 * 24)\n )\n return { text: `${daysAgo}d overdue`, isOverdue: true }\n } else {\n return {\n text: due.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }),\n isOverdue: false,\n }\n }\n}\n\n/**\n * Truncate a string with ellipsis\n * @example truncate(\"Hello World\", 5) => \"Hello...\"\n */\nexport function truncate(str: string, maxLength: number): string {\n if (str.length <= maxLength) return str\n return str.slice(0, maxLength) + '...'\n}\n\n/**\n * Extract repo name from GitHub URL\n * @example getRepoName(\"https://github.com/owner/repo\") => \"owner/repo\"\n */\nexport function getRepoName(url: string): string {\n const match = url.match(/github\\.com\\/(.+)$/)\n return match ? match[1] : url\n}\n\n","/**\n * ID and Data Generators\n *\n * Pure functions for generating IDs and data.\n */\n\n/**\n * Generate a unique ID\n * Uses crypto.randomUUID if available, falls back to timestamp-based ID\n *\n * Note: This works in both Node.js and browser environments.\n * React Native needs the fallback since crypto.randomUUID isn't available.\n */\nexport function generateId(): string {\n // Check if crypto.randomUUID is available (Node.js 19+, modern browsers)\n if (\n typeof crypto !== 'undefined' &&\n typeof crypto.randomUUID === 'function'\n ) {\n return crypto.randomUUID()\n }\n\n // Fallback: UUID v4-like implementation\n return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {\n const r = (Math.random() * 16) | 0\n const v = c === 'x' ? r : (r & 0x3) | 0x8\n return v.toString(16)\n })\n}\n\n/**\n * Generate a short ID (timestamp + random)\n * Format: {timestamp}-{random7chars}\n * @example \"1732547123456-k8f3j2m\"\n */\nexport function generateShortId(): string {\n return `${Date.now()}-${Math.random().toString(36).slice(2, 9)}`\n}\n\n/**\n * Generate a random color in hex format\n */\nexport function generateRandomColor(): string {\n const colors = [\n '#ef4444', // red\n '#f97316', // orange\n '#eab308', // yellow\n '#22c55e', // green\n '#14b8a6', // teal\n '#3b82f6', // blue\n '#8b5cf6', // violet\n '#ec4899', // pink\n ]\n return colors[Math.floor(Math.random() * colors.length)]\n}\n\n","/**\n * Validation Utilities\n *\n * Pure functions for validating data.\n */\n\n/**\n * Check if a string is a valid email\n */\nexport function isValidEmail(email: string): boolean {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n return emailRegex.test(email)\n}\n\n/**\n * Check if a string is a valid URL\n */\nexport function isValidUrl(url: string): boolean {\n try {\n new URL(url)\n return true\n } catch {\n return false\n }\n}\n\n/**\n * Check if a string is a valid ISO date (YYYY-MM-DD)\n */\nexport function isValidISODate(dateStr: string): boolean {\n const regex = /^\\d{4}-\\d{2}-\\d{2}$/\n if (!regex.test(dateStr)) return false\n\n const date = new Date(dateStr)\n return !isNaN(date.getTime())\n}\n\n/**\n * Check if a value is a valid currency code\n */\nexport function isValidCurrency(currency: string): boolean {\n const validCurrencies = ['CHF', 'USD', 'EUR', 'PLN']\n return validCurrencies.includes(currency)\n}\n\n/**\n * Check if a value is a valid frequency\n */\nexport function isValidFrequency(frequency: string): boolean {\n const validFrequencies = ['monthly', 'yearly', '6-monthly', 'weekly', 'one-time']\n return validFrequencies.includes(frequency)\n}\n\n/**\n * Validate a positive number\n */\nexport function isPositiveNumber(value: unknown): value is number {\n return typeof value === 'number' && !isNaN(value) && value > 0\n}\n\n/**\n * Validate a non-empty string\n */\nexport function isNonEmptyString(value: unknown): value is string {\n return typeof value === 'string' && value.trim().length > 0\n}\n\n","/**\n * Calculation Utilities\n *\n * Pure functions for business logic calculations.\n */\n\nimport type { Expense, Income } from '../types/database'\nimport { FREQUENCY_MULTIPLIERS, type Frequency } from '../constants'\n\n/**\n * Convert an amount to yearly based on frequency\n */\nexport function toYearlyAmount(amount: number, frequency: Frequency): number {\n const multiplier = FREQUENCY_MULTIPLIERS[frequency] || 1\n return amount * multiplier\n}\n\n/**\n * Convert an amount to monthly based on frequency\n */\nexport function toMonthlyAmount(amount: number, frequency: Frequency): number {\n const yearly = toYearlyAmount(amount, frequency)\n return yearly / 12\n}\n\n/**\n * Calculate total expenses (monthly)\n */\nexport function calculateMonthlyExpenses(expenses: Expense[]): number {\n return expenses\n .filter((e) => e.isActive)\n .reduce((total, expense) => {\n const monthly = toMonthlyAmount(\n expense.amount,\n expense.frequency as Frequency\n )\n // Apply share percentage\n const myShare = (monthly * expense.sharePercentage) / 100\n return total + myShare\n }, 0)\n}\n\n/**\n * Calculate total income (monthly)\n */\nexport function calculateMonthlyIncome(incomes: Income[]): number {\n return incomes\n .filter((i) => i.isActive)\n .reduce((total, income) => {\n const monthly = toMonthlyAmount(income.amount, income.frequency as Frequency)\n return total + monthly\n }, 0)\n}\n\n/**\n * Calculate savings (income - expenses)\n */\nexport function calculateMonthlySavings(\n incomes: Income[],\n expenses: Expense[]\n): number {\n return calculateMonthlyIncome(incomes) - calculateMonthlyExpenses(expenses)\n}\n\n/**\n * Calculate savings rate as percentage\n */\nexport function calculateSavingsRate(\n incomes: Income[],\n expenses: Expense[]\n): number {\n const income = calculateMonthlyIncome(incomes)\n if (income === 0) return 0\n\n const savings = calculateMonthlySavings(incomes, expenses)\n return (savings / income) * 100\n}\n\n/**\n * Calculate lieu day balance\n */\nexport function calculateLieuBalance(\n lieuDays: Array<{ type: 'earned' | 'used' }>\n): number {\n return lieuDays.reduce((balance, day) => {\n return day.type === 'earned' ? balance + 1 : balance - 1\n }, 0)\n}\n\n/**\n * Calculate focus time stats for a period\n */\nexport function calculateFocusStats(\n sessions: Array<{ actualSeconds: number; status: string }>\n): {\n totalSeconds: number\n completedCount: number\n abandonedCount: number\n completionRate: number\n} {\n const completed = sessions.filter((s) => s.status === 'completed')\n const abandoned = sessions.filter((s) => s.status === 'abandoned')\n\n const totalSeconds = completed.reduce((sum, s) => sum + s.actualSeconds, 0)\n const completionRate =\n sessions.length > 0 ? (completed.length / sessions.length) * 100 : 0\n\n return {\n totalSeconds,\n completedCount: completed.length,\n abandonedCount: abandoned.length,\n completionRate,\n }\n}\n\n","/**\n * Health & Habits Utilities\n *\n * Shared utility functions for the health & habits module.\n * Used by both desktop and mobile apps.\n */\n\nimport type { HealthHabit, HealthFrequencyConfig } from \"../types/database\";\n\n// ============================================================================\n// Frequency Config Helpers\n// ============================================================================\n\n/**\n * Parse frequency config from JSON string\n */\nexport function parseFrequencyConfig(\n config: string | null\n): HealthFrequencyConfig | null {\n if (!config) return null;\n try {\n return JSON.parse(config);\n } catch {\n return null;\n }\n}\n\n/**\n * Stringify frequency config to JSON\n */\nexport function stringifyFrequencyConfig(\n config: HealthFrequencyConfig | null\n): string | null {\n if (!config) return null;\n return JSON.stringify(config);\n}\n\n// ============================================================================\n// Frequency Description\n// ============================================================================\n\n/**\n * Get human-readable frequency description for a habit\n */\nexport function getFrequencyDescription(habit: HealthHabit): string {\n const config = parseFrequencyConfig(habit.frequencyConfig);\n\n switch (habit.frequencyType) {\n case \"daily\":\n return \"Every day\";\n case \"specific_days\": {\n if (!config?.days) return \"Specific days\";\n const dayNames = [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"];\n return config.days.map((d) => dayNames[d]).join(\", \");\n }\n case \"times_per_week\":\n return `${config?.target || 1}x per week`;\n case \"times_per_month\":\n return `${config?.target || 1}x per month`;\n default:\n return \"Unknown\";\n }\n}\n\n// ============================================================================\n// Schedule Helpers\n// ============================================================================\n\n/**\n * Check if habit should be done on a specific date based on frequency\n */\nexport function shouldDoOnDate(habit: HealthHabit, date: Date): boolean {\n const dayOfWeek = date.getDay(); // 0 = Sunday\n\n switch (habit.frequencyType) {\n case \"daily\":\n return true;\n case \"specific_days\": {\n const config = parseFrequencyConfig(habit.frequencyConfig);\n return config?.days?.includes(dayOfWeek) ?? false;\n }\n case \"times_per_week\":\n case \"times_per_month\":\n // These are flexible - always show as available\n return true;\n default:\n return true;\n }\n}\n\n/**\n * Check if habit should be done today\n */\nexport function shouldDoToday(habit: HealthHabit): boolean {\n return shouldDoOnDate(habit, new Date());\n}\n\n// ============================================================================\n// Weekly Progress\n// ============================================================================\n\nexport interface WeeklyProgressItem {\n habit: HealthHabit;\n target: number;\n completed: number;\n remaining: number;\n isComplete: boolean;\n}\n\n/**\n * Calculate weekly progress for a habit\n */\nexport function calculateWeeklyProgress(\n habit: HealthHabit,\n weekCompletionCount: number\n): WeeklyProgressItem {\n const config = parseFrequencyConfig(habit.frequencyConfig);\n const target = config?.target ?? 1;\n const completed = weekCompletionCount;\n\n return {\n habit,\n target,\n completed,\n remaining: Math.max(0, target - completed),\n isComplete: completed >= target,\n };\n}\n","/**\n * Query Keys Factory\n *\n * Centralized query key management for TanStack Query.\n * Used by both desktop and mobile apps for consistent cache invalidation.\n *\n * @example\n * import { queryKeys } from '@mrck-labs/vanaheim-shared/query'\n *\n * // In a query hook\n * useQuery({\n * queryKey: queryKeys.focusSessions.today(),\n * queryFn: ...\n * })\n *\n * // Invalidating\n * queryClient.invalidateQueries({ queryKey: queryKeys.focusSessions.all })\n */\n\n// ============================================================================\n// Query Keys Factory\n// ============================================================================\n\nexport const queryKeys = {\n // -------------------------------------------------------------------------\n // Expenses\n // -------------------------------------------------------------------------\n expenses: {\n all: ['expenses'] as const,\n lists: () => [...queryKeys.expenses.all, 'list'] as const,\n list: (filters?: { isActive?: boolean; categoryId?: string }) =>\n [...queryKeys.expenses.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.expenses.all, 'detail', id] as const,\n },\n\n // Expense Categories\n expenseCategories: {\n all: ['expenseCategories'] as const,\n list: () => [...queryKeys.expenseCategories.all, 'list'] as const,\n },\n\n // Expense Payments\n expensePayments: {\n all: ['expensePayments'] as const,\n lists: () => [...queryKeys.expensePayments.all, 'list'] as const,\n list: (filters?: { expenseId?: string; dateFrom?: string; dateTo?: string }) =>\n [...queryKeys.expensePayments.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.expensePayments.all, 'detail', id] as const,\n },\n\n // -------------------------------------------------------------------------\n // Income\n // -------------------------------------------------------------------------\n incomes: {\n all: ['incomes'] as const,\n lists: () => [...queryKeys.incomes.all, 'list'] as const,\n list: (filters?: { isActive?: boolean; categoryId?: string }) =>\n [...queryKeys.incomes.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.incomes.all, 'detail', id] as const,\n },\n\n // Income Categories\n incomeCategories: {\n all: ['incomeCategories'] as const,\n list: () => [...queryKeys.incomeCategories.all, 'list'] as const,\n },\n\n // Income Payments\n incomePayments: {\n all: ['incomePayments'] as const,\n lists: () => [...queryKeys.incomePayments.all, 'list'] as const,\n list: (filters?: { incomeId?: string; dateFrom?: string; dateTo?: string }) =>\n [...queryKeys.incomePayments.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.incomePayments.all, 'detail', id] as const,\n },\n\n // Exchange Rates\n exchangeRates: {\n all: ['exchangeRates'] as const,\n list: () => [...queryKeys.exchangeRates.all, 'list'] as const,\n },\n\n // -------------------------------------------------------------------------\n // Focus\n // -------------------------------------------------------------------------\n focusSessions: {\n all: ['focusSessions'] as const,\n lists: () => [...queryKeys.focusSessions.all, 'list'] as const,\n list: (filters?: {\n date?: string\n categoryId?: string\n status?: string\n startedAfter?: string\n startedBefore?: string\n }) => [...queryKeys.focusSessions.lists(), filters] as const,\n today: () => [...queryKeys.focusSessions.all, 'today'] as const,\n active: () => [...queryKeys.focusSessions.all, 'active'] as const,\n detail: (id: string) => [...queryKeys.focusSessions.all, 'detail', id] as const,\n },\n\n // Focus Categories\n focusCategories: {\n all: ['focusCategories'] as const,\n list: () => [...queryKeys.focusCategories.all, 'list'] as const,\n },\n\n // -------------------------------------------------------------------------\n // Settings\n // -------------------------------------------------------------------------\n settings: {\n all: ['settings'] as const,\n detail: (key: string) => [...queryKeys.settings.all, key] as const,\n },\n\n // -------------------------------------------------------------------------\n // EF Work (Lieu Days & Links)\n // -------------------------------------------------------------------------\n lieuDays: {\n all: ['lieuDays'] as const,\n list: () => [...queryKeys.lieuDays.all, 'list'] as const,\n balance: () => [...queryKeys.lieuDays.all, 'balance'] as const,\n detail: (id: string) => [...queryKeys.lieuDays.all, 'detail', id] as const,\n },\n\n efLinks: {\n all: ['efLinks'] as const,\n list: () => [...queryKeys.efLinks.all, 'list'] as const,\n detail: (id: string) => [...queryKeys.efLinks.all, 'detail', id] as const,\n },\n\n // -------------------------------------------------------------------------\n // Banking\n // -------------------------------------------------------------------------\n bankConnections: {\n all: ['bankConnections'] as const,\n list: () => [...queryKeys.bankConnections.all, 'list'] as const,\n detail: (id: string) => [...queryKeys.bankConnections.all, 'detail', id] as const,\n },\n\n bankTransactions: {\n all: ['bankTransactions'] as const,\n lists: () => [...queryKeys.bankTransactions.all, 'list'] as const,\n list: (filters?: { connectionId?: string; dateFrom?: string; dateTo?: string }) =>\n [...queryKeys.bankTransactions.lists(), filters] as const,\n stats: (connectionId?: string) =>\n [...queryKeys.bankTransactions.all, 'stats', connectionId] as const,\n },\n\n // -------------------------------------------------------------------------\n // Health\n // -------------------------------------------------------------------------\n healthCategories: {\n all: ['healthCategories'] as const,\n list: () => [...queryKeys.healthCategories.all, 'list'] as const,\n detail: (id: string) => [...queryKeys.healthCategories.all, 'detail', id] as const,\n },\n\n healthHabits: {\n all: ['healthHabits'] as const,\n lists: () => [...queryKeys.healthHabits.all, 'list'] as const,\n list: (filters?: { categoryId?: string; isActive?: boolean }) =>\n [...queryKeys.healthHabits.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.healthHabits.all, 'detail', id] as const,\n },\n\n healthCompletions: {\n all: ['healthCompletions'] as const,\n lists: () => [...queryKeys.healthCompletions.all, 'list'] as const,\n list: (filters?: { habitId?: string; dateFrom?: string; dateTo?: string }) =>\n [...queryKeys.healthCompletions.lists(), filters] as const,\n forHabit: (habitId: string) => [...queryKeys.healthCompletions.all, 'habit', habitId] as const,\n forDate: (date: string) => [...queryKeys.healthCompletions.all, 'date', date] as const,\n },\n\n healthStats: {\n all: ['healthStats'] as const,\n forDate: (date?: string) => [...queryKeys.healthStats.all, date ?? 'today'] as const,\n },\n\n // -------------------------------------------------------------------------\n // Prompts\n // -------------------------------------------------------------------------\n promptCategories: {\n all: ['promptCategories'] as const,\n list: () => [...queryKeys.promptCategories.all, 'list'] as const,\n detail: (id: string) => [...queryKeys.promptCategories.all, 'detail', id] as const,\n },\n\n promptLabels: {\n all: ['promptLabels'] as const,\n list: () => [...queryKeys.promptLabels.all, 'list'] as const,\n detail: (id: string) => [...queryKeys.promptLabels.all, 'detail', id] as const,\n },\n\n prompts: {\n all: ['prompts'] as const,\n lists: () => [...queryKeys.prompts.all, 'list'] as const,\n list: (filters?: {\n categoryId?: string\n labelId?: string\n isFavorite?: boolean\n search?: string\n }) => [...queryKeys.prompts.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.prompts.all, 'detail', id] as const,\n },\n\n // -------------------------------------------------------------------------\n // Training\n // -------------------------------------------------------------------------\n trainingActivities: {\n all: ['trainingActivities'] as const,\n list: () => [...queryKeys.trainingActivities.all, 'list'] as const,\n detail: (id: string) => [...queryKeys.trainingActivities.all, 'detail', id] as const,\n byStravaType: (stravaType: string) =>\n [...queryKeys.trainingActivities.all, 'strava', stravaType] as const,\n },\n\n trainingSessions: {\n all: ['trainingSessions'] as const,\n lists: () => [...queryKeys.trainingSessions.all, 'list'] as const,\n list: (filters?: {\n activityId?: string\n dateFrom?: string\n dateTo?: string\n source?: 'manual' | 'strava'\n }) => [...queryKeys.trainingSessions.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.trainingSessions.all, 'detail', id] as const,\n byStravaId: (stravaActivityId: string) =>\n [...queryKeys.trainingSessions.all, 'strava', stravaActivityId] as const,\n },\n\n plannedSessions: {\n all: ['plannedSessions'] as const,\n lists: () => [...queryKeys.plannedSessions.all, 'list'] as const,\n list: (filters?: {\n activityId?: string\n dateFrom?: string\n dateTo?: string\n isCompleted?: boolean\n }) => [...queryKeys.plannedSessions.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.plannedSessions.all, 'detail', id] as const,\n upcoming: () => [...queryKeys.plannedSessions.all, 'upcoming'] as const,\n },\n\n races: {\n all: ['races'] as const,\n lists: () => [...queryKeys.races.all, 'list'] as const,\n list: (filters?: {\n type?: 'race' | 'event' | 'goal'\n status?: 'upcoming' | 'completed' | 'dns' | 'dnf'\n dateFrom?: string\n dateTo?: string\n }) => [...queryKeys.races.lists(), filters] as const,\n detail: (id: string) => [...queryKeys.races.all, 'detail', id] as const,\n next: () => [...queryKeys.races.all, 'next'] as const,\n },\n\n trainingStats: {\n all: ['trainingStats'] as const,\n forRange: (dateFrom?: string, dateTo?: string) =>\n [...queryKeys.trainingStats.all, dateFrom, dateTo] as const,\n },\n\n // Strava\n strava: {\n all: ['strava'] as const,\n athlete: () => [...queryKeys.strava.all, 'athlete'] as const,\n connected: () => [...queryKeys.strava.all, 'connected'] as const,\n activities: (options?: { after?: number; before?: number }) =>\n [...queryKeys.strava.all, 'activities', options] as const,\n },\n\n // -------------------------------------------------------------------------\n // Journal\n // -------------------------------------------------------------------------\n journalEntries: {\n all: ['journalEntries'] as const,\n lists: () => [...queryKeys.journalEntries.all, 'list'] as const,\n list: (filters?: { dateFrom?: string; dateTo?: string }) =>\n [...queryKeys.journalEntries.lists(), filters] as const,\n forDate: (date: string) => [...queryKeys.journalEntries.all, 'date', date] as const,\n },\n\n // Journal data (aggregated)\n journalData: {\n all: ['journalData'] as const,\n forDate: (date: string) => [...queryKeys.journalData.all, date] as const,\n },\n\n // -------------------------------------------------------------------------\n // Chat\n // -------------------------------------------------------------------------\n chatConversations: {\n all: ['chatConversations'] as const,\n list: () => [...queryKeys.chatConversations.all, 'list'] as const,\n detail: (id: string) => [...queryKeys.chatConversations.all, 'detail', id] as const,\n },\n\n chatMessages: {\n all: ['chatMessages'] as const,\n forConversation: (conversationId: string) =>\n [...queryKeys.chatMessages.all, 'conversation', conversationId] as const,\n },\n\n // -------------------------------------------------------------------------\n // Google Calendar\n // -------------------------------------------------------------------------\n googleCalendar: {\n all: ['googleCalendar'] as const,\n calendars: () => [...queryKeys.googleCalendar.all, 'calendars'] as const,\n events: (calendarId: string, timeMin?: string, timeMax?: string) =>\n [...queryKeys.googleCalendar.all, 'events', calendarId, timeMin, timeMax] as const,\n },\n\n // -------------------------------------------------------------------------\n // Linear\n // -------------------------------------------------------------------------\n linear: {\n all: ['linear'] as const,\n issues: (filters?: { projectId?: string; status?: string }) =>\n [...queryKeys.linear.all, 'issues', filters] as const,\n projects: () => [...queryKeys.linear.all, 'projects'] as const,\n },\n} as const\n\n// ============================================================================\n// Type Exports\n// ============================================================================\n\nexport type QueryKeys = typeof queryKeys\n\n","/**\n * Focus Timer Atoms\n *\n * Shared Jotai atoms for focus timer state management.\n * Used by both desktop and mobile apps.\n *\n * These atoms manage in-memory timer state that syncs with\n * the database via TanStack Query.\n *\n * @example\n * import { timerStatusAtom, targetSecondsAtom } from '@mrck-labs/vanaheim-shared/atoms'\n * import { useAtom, useAtomValue } from 'jotai'\n *\n * const [status, setStatus] = useAtom(timerStatusAtom)\n * const remaining = useAtomValue(remainingSecondsAtom)\n */\n\nimport { atom } from 'jotai'\nimport { formatTime } from '../utils/formatters'\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Timer status values\n */\nexport type TimerStatus = 'idle' | 'running' | 'paused' | 'completed'\n\n/**\n * Active focus session info (in-memory during timer run)\n * This is separate from the database FocusSession type\n */\nexport interface ActiveSession {\n id: string\n title: string\n categoryId: string | null\n targetMinutes: number\n startedAt?: string // Optional - used by mobile for elapsed calculation\n}\n\n// ============================================================================\n// Core Timer Atoms\n// ============================================================================\n\n/**\n * Current timer status\n * - idle: No timer running\n * - running: Timer actively counting\n * - paused: Timer paused\n * - completed: Timer finished (target reached)\n */\nexport const timerStatusAtom = atom<TimerStatus>('idle')\n\n/**\n * Target duration in seconds\n * Default: 25 minutes (Pomodoro)\n */\nexport const targetSecondsAtom = atom<number>(25 * 60)\n\n/**\n * Elapsed seconds since timer started\n */\nexport const elapsedSecondsAtom = atom<number>(0)\n\n/**\n * Currently active session info (in-memory during timer run)\n */\nexport const activeSessionAtom = atom<ActiveSession | null>(null)\n\n// ============================================================================\n// Derived Timer Atoms (computed, read-only)\n// ============================================================================\n\n/**\n * Remaining seconds (target - elapsed)\n * Always returns 0 or positive\n */\nexport const remainingSecondsAtom = atom((get) => {\n const target = get(targetSecondsAtom)\n const elapsed = get(elapsedSecondsAtom)\n return Math.max(0, target - elapsed)\n})\n\n/**\n * Progress as a ratio (0 to 1)\n * 0 = just started, 1 = completed\n *\n * Note: Desktop previously used 0-100 (percentage).\n * If you need percentage, multiply by 100.\n */\nexport const progressAtom = atom((get) => {\n const target = get(targetSecondsAtom)\n const elapsed = get(elapsedSecondsAtom)\n if (target === 0) return 0\n return Math.min(1, elapsed / target)\n})\n\n/**\n * Formatted remaining time (MM:SS)\n * @example \"24:59\", \"00:30\"\n */\nexport const formattedRemainingAtom = atom((get) => {\n return formatTime(get(remainingSecondsAtom))\n})\n\n/**\n * Formatted elapsed time (MM:SS)\n * @example \"00:01\", \"25:00\"\n */\nexport const formattedElapsedAtom = atom((get) => {\n return formatTime(get(elapsedSecondsAtom))\n})\n\n/**\n * Progress as percentage (0 to 100)\n * Convenience atom for desktop compatibility\n */\nexport const progressPercentAtom = atom((get) => {\n return get(progressAtom) * 100\n})\n\n","/**\n * Date Utilities\n *\n * Pure functions for date manipulation and formatting.\n * Works in both Node.js and browser/React Native environments.\n *\n * @example\n * import { getTodayString, addDays, formatRelativeTime } from '@mrck-labs/vanaheim-shared/date'\n *\n * const today = getTodayString() // \"2024-12-16\"\n * const nextWeek = addDays(today, 7) // \"2024-12-23\"\n */\n\n// ============================================================================\n// Core Utilities\n// ============================================================================\n\n/**\n * Normalize a date to midnight (00:00:00.000)\n */\nexport function normalizeToMidnight(date: Date): Date {\n const d = new Date(date)\n d.setHours(0, 0, 0, 0)\n return d\n}\n\n/**\n * Get today's date normalized to midnight\n */\nexport function getTodayMidnight(): Date {\n return normalizeToMidnight(new Date())\n}\n\n/**\n * Format a Date object to YYYY-MM-DD string\n * @example formatDateString(new Date()) => \"2024-12-16\"\n */\nexport function formatDateString(date: Date): string {\n return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`\n}\n\n/**\n * Get today's local date string (YYYY-MM-DD)\n */\nexport function getTodayString(): string {\n return formatDateString(new Date())\n}\n\n/**\n * Parse a YYYY-MM-DD string to a local Date object\n * Avoids timezone issues by appending T00:00:00\n */\nexport function parseLocalDate(dateStr: string): Date {\n return new Date(dateStr + 'T00:00:00')\n}\n\n// ============================================================================\n// Date Checks\n// ============================================================================\n\n/**\n * Check if a date string is today\n */\nexport function isToday(dateStr: string | null): boolean {\n if (!dateStr) return false\n return dateStr === getTodayString()\n}\n\n/**\n * Check if a date string is in the past (before today)\n */\nexport function isOverdue(dateStr: string | null): boolean {\n if (!dateStr) return false\n return dateStr < getTodayString()\n}\n\n/**\n * Check if a date is due within a threshold of days\n * @param dateStr - Date in YYYY-MM-DD format\n * @param daysThreshold - Number of days to check (default: 7)\n */\nexport function isDueSoon(dateStr: string | null, daysThreshold: number = 7): boolean {\n if (!dateStr) return false\n if (isOverdue(dateStr)) return false\n\n const today = getTodayMidnight()\n const targetDate = parseLocalDate(dateStr)\n const diffMs = targetDate.getTime() - today.getTime()\n const diffDays = Math.ceil(diffMs / (1000 * 60 * 60 * 24))\n\n return diffDays >= 0 && diffDays <= daysThreshold\n}\n\n// ============================================================================\n// Date Arithmetic\n// ============================================================================\n\n/**\n * Add days to a date string\n * @param dateStr - Date in YYYY-MM-DD format\n * @param days - Number of days to add (can be negative)\n * @returns New date string in YYYY-MM-DD format\n */\nexport function addDays(dateStr: string, days: number): string {\n const date = parseLocalDate(dateStr)\n date.setDate(date.getDate() + days)\n return formatDateString(date)\n}\n\n/**\n * Subtract days from a date string\n */\nexport function subtractDays(dateStr: string, days: number): string {\n return addDays(dateStr, -days)\n}\n\n/**\n * Get yesterday's date string (YYYY-MM-DD)\n */\nexport function getYesterdayString(): string {\n return subtractDays(getTodayString(), 1)\n}\n\n/**\n * Get tomorrow's date string (YYYY-MM-DD)\n */\nexport function getTomorrowString(): string {\n return addDays(getTodayString(), 1)\n}\n\n// ============================================================================\n// ISO String Helpers (for database queries)\n// ============================================================================\n\n/**\n * Get start of day as ISO string\n * @param dateStr - Date in YYYY-MM-DD format\n * @returns ISO string like \"2024-12-15T00:00:00.000Z\"\n */\nexport function getStartOfDayISO(dateStr: string): string {\n const date = parseLocalDate(dateStr)\n return new Date(date.getFullYear(), date.getMonth(), date.getDate()).toISOString()\n}\n\n/**\n * Get end of day as ISO string\n * @param dateStr - Date in YYYY-MM-DD format\n * @returns ISO string like \"2024-12-15T23:59:59.999Z\"\n */\nexport function getEndOfDayISO(dateStr: string): string {\n const date = parseLocalDate(dateStr)\n return new Date(\n date.getFullYear(),\n date.getMonth(),\n date.getDate(),\n 23,\n 59,\n 59,\n 999\n ).toISOString()\n}\n\n// ============================================================================\n// Week Utilities\n// ============================================================================\n\n/**\n * Get the start of the week (Monday) for a given date\n */\nexport function getWeekStart(date: Date): Date {\n const d = new Date(date)\n const day = d.getDay()\n const diff = d.getDate() - day + (day === 0 ? -6 : 1) // Monday\n d.setDate(diff)\n d.setHours(0, 0, 0, 0)\n return d\n}\n\n/**\n * Get the end of the week (Sunday) for a given week start\n */\nexport function getWeekEnd(weekStart: Date): Date {\n const d = new Date(weekStart)\n d.setDate(d.getDate() + 6)\n return d\n}\n\n/**\n * Get the start of the previous week\n */\nexport function getPreviousWeek(weekStart: Date): Date {\n const d = new Date(weekStart)\n d.setDate(d.getDate() - 7)\n return d\n}\n\n/**\n * Get the start of the next week\n */\nexport function getNextWeek(weekStart: Date): Date {\n const d = new Date(weekStart)\n d.setDate(d.getDate() + 7)\n return d\n}\n\n/**\n * Get start of week (Monday) as date string\n */\nexport function getWeekStartString(baseDate: Date = new Date()): string {\n return formatDateString(getWeekStart(baseDate))\n}\n\n/**\n * Get end of week (Sunday) as date string\n */\nexport function getWeekEndString(baseDate: Date = new Date()): string {\n return formatDateString(getWeekEnd(getWeekStart(baseDate)))\n}\n\n/**\n * Format a week range for display\n * @example \"Dec 2 - 8, 2024\" or \"Nov 25 - Dec 1, 2024\"\n */\nexport function formatWeekRange(weekStart: Date): string {\n const weekEnd = getWeekEnd(weekStart)\n const startMonth = weekStart.toLocaleDateString('en-US', { month: 'short' })\n const endMonth = weekEnd.toLocaleDateString('en-US', { month: 'short' })\n\n if (startMonth === endMonth) {\n return `${startMonth} ${weekStart.getDate()} - ${weekEnd.getDate()}, ${weekStart.getFullYear()}`\n }\n\n return `${startMonth} ${weekStart.getDate()} - ${endMonth} ${weekEnd.getDate()}, ${weekStart.getFullYear()}`\n}\n\n// ============================================================================\n// Display Formatting\n// ============================================================================\n\n/**\n * Format a date string for display with full weekday\n * @example \"Monday, December 15, 2024\"\n */\nexport function formatFullDate(dateStr: string): string {\n const date = parseLocalDate(dateStr)\n return date.toLocaleDateString('en-US', {\n weekday: 'long',\n year: 'numeric',\n month: 'long',\n day: 'numeric',\n })\n}\n\n/**\n * Format date for display in long form\n * @example \"Monday, 9 December\"\n */\nexport function formatDateLong(dateStr: string): string {\n const date = parseLocalDate(dateStr)\n return date.toLocaleDateString('en-GB', {\n weekday: 'long',\n day: 'numeric',\n month: 'long',\n })\n}\n\n/**\n * Format month and year\n * @example \"December 2024\"\n */\nexport function formatMonthYear(date: Date): string {\n return date.toLocaleDateString('en-US', {\n month: 'long',\n year: 'numeric',\n })\n}\n\n/**\n * Format a date/time to HH:MM format\n * Use this for formatting Date objects or ISO strings to time display\n * @example formatTimeHHMM(new Date()) => \"14:30\"\n *\n * Note: For formatting seconds (e.g., timer display), use formatTime from utils\n */\nexport function formatTimeHHMM(date: Date | string): string {\n const d = typeof date === 'string' ? new Date(date) : date\n return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })\n}\n\n/**\n * Format a date/time to HH:MM:SS format\n */\nexport function formatTimeWithSeconds(date: Date | string): string {\n const d = typeof date === 'string' ? new Date(date) : date\n return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' })\n}\n\n// ============================================================================\n// Contextual Formatting\n// ============================================================================\n\n/**\n * Format a date string for display as a header\n * Returns \"Overdue\", \"Today\", \"Tomorrow\", or formatted date\n */\nexport function formatDateHeader(dateStr: string): string {\n const today = getTodayString()\n\n if (dateStr < today) {\n return 'Overdue'\n }\n\n if (dateStr === today) {\n return 'Today'\n }\n\n const tomorrowStr = getTomorrowString()\n if (dateStr === tomorrowStr) {\n return 'Tomorrow'\n }\n\n const date = parseLocalDate(dateStr)\n return date.toLocaleDateString('en-US', {\n weekday: 'long',\n month: 'short',\n day: 'numeric',\n })\n}\n\n/**\n * Format a due date for display in task items (simple string version)\n * Returns \"Overdue\", \"Today\", \"Tomorrow\", or formatted date\n *\n * Note: For version that returns { text, isOverdue }, use formatDueDate from utils/formatters\n */\nexport function formatDueDateString(dateStr: string | null): string {\n if (!dateStr) return 'No due date'\n\n const today = getTodayString()\n const tomorrowStr = getTomorrowString()\n\n if (dateStr < today) {\n const date = parseLocalDate(dateStr)\n return `Overdue • ${date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })}`\n }\n\n if (dateStr === today) {\n return 'Today'\n }\n\n if (dateStr === tomorrowStr) {\n return 'Tomorrow'\n }\n\n const date = parseLocalDate(dateStr)\n const currentYear = new Date().getFullYear()\n const dateYear = date.getFullYear()\n\n return date.toLocaleDateString('en-US', {\n month: 'short',\n day: 'numeric',\n year: dateYear !== currentYear ? 'numeric' : undefined,\n })\n}\n\n/**\n * Format a date/time as relative time string (extended version)\n * Accepts both Date objects and ISO strings\n * @example \"Just now\", \"5m ago\", \"2h ago\", \"3d ago\"\n *\n * Note: For simple string input, you can also use formatRelativeTime from utils/formatters\n */\nexport function formatRelativeTimeExtended(dateStr: string | Date): string {\n const date = typeof dateStr === 'string' ? new Date(dateStr) : dateStr\n const now = new Date()\n const diffMs = now.getTime() - date.getTime()\n const diffMins = Math.floor(diffMs / 60000)\n const diffHours = Math.floor(diffMins / 60)\n const diffDays = Math.floor(diffHours / 24)\n\n if (diffMins < 1) return 'Just now'\n if (diffMins < 60) return `${diffMins}m ago`\n if (diffHours < 24) return `${diffHours}h ago`\n if (diffDays < 7) return `${diffDays}d ago`\n return date.toLocaleDateString()\n}\n\n/**\n * Format a due date relative to today with contextual prefix\n * @example \"Due today\", \"Due tomorrow\", \"Due in 3 days\", \"2 days overdue\"\n */\nexport function formatRelativeDueDate(\n dateStr: string,\n options?: {\n duePrefix?: string\n overduePrefix?: string\n }\n): string {\n const { duePrefix = 'Due', overduePrefix = '' } = options || {}\n const date = parseLocalDate(dateStr)\n const now = normalizeToMidnight(new Date())\n\n const diffMs = date.getTime() - now.getTime()\n const diffDays = Math.round(diffMs / (1000 * 60 * 60 * 24))\n\n if (diffDays < 0) {\n const absDays = Math.abs(diffDays)\n return overduePrefix\n ? `${overduePrefix} ${absDays} day${absDays !== 1 ? 's' : ''} overdue`\n : `${absDays} day${absDays !== 1 ? 's' : ''} overdue`\n }\n if (diffDays === 0) return `${duePrefix} today`\n if (diffDays === 1) return `${duePrefix} tomorrow`\n if (diffDays <= 7) return `${duePrefix} in ${diffDays} days`\n\n return date.toLocaleDateString('en-GB', { day: 'numeric', month: 'short' })\n}\n\n/**\n * Format a pay/income date relative to today\n * @example \"Today\", \"Tomorrow\", \"In 3 days\", \"2 days ago\"\n */\nexport function formatRelativePayDate(dateStr: string): string {\n const date = parseLocalDate(dateStr)\n const now = normalizeToMidnight(new Date())\n\n const diffMs = date.getTime() - now.getTime()\n const diffDays = Math.round(diffMs / (1000 * 60 * 60 * 24))\n\n if (diffDays < 0) {\n const absDays = Math.abs(diffDays)\n return `${absDays} day${absDays !== 1 ? 's' : ''} ago`\n }\n if (diffDays === 0) return 'Today'\n if (diffDays === 1) return 'Tomorrow'\n if (diffDays <= 7) return `In ${diffDays} days`\n\n return date.toLocaleDateString('en-GB', { day: 'numeric', month: 'short' })\n}\n\n// ============================================================================\n// Localized Formatting\n// ============================================================================\n\nexport type DateFormat = 'short' | 'medium' | 'long' | 'weekday'\nexport type TimeFormat = 'short' | 'withSeconds'\n\n/**\n * Format a date with various preset formats\n */\nexport function formatDateLocalized(date: Date | string, format: DateFormat = 'medium'): string {\n const d = typeof date === 'string' ? new Date(date) : date\n\n switch (format) {\n case 'short':\n return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })\n case 'medium':\n return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })\n case 'long':\n return d.toLocaleDateString('en-US', {\n weekday: 'long',\n month: 'long',\n day: 'numeric',\n year: 'numeric',\n })\n case 'weekday':\n return d.toLocaleDateString('en-US', { weekday: 'short' })\n default:\n return d.toLocaleDateString()\n }\n}\n\n/**\n * Format a time with various preset formats\n */\nexport function formatTimeLocalized(date: Date | string, format: TimeFormat = 'short'): string {\n const d = typeof date === 'string' ? new Date(date) : date\n\n switch (format) {\n case 'short':\n return d.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: true })\n case 'withSeconds':\n return d.toLocaleTimeString('en-US', {\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: true,\n })\n default:\n return d.toLocaleTimeString()\n }\n}\n\n"],"mappings":"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","names":["isOverdue","date"]}
@@ -1,4 +1,4 @@
1
- export { s as BankConnection, B as BankConnectionStatus, t as BankTransaction, k as ChatConversation, m as ChatMessage, C as ChatMessageRole, o as EFLink, u as ExchangeRate, a as Expense, E as ExpenseCategory, F as FocusCategory, h as FocusSession, j as FocusSessionFilters, g as FocusSessionStatus, d as Income, I as IncomeCategory, q as LieuDay, L as LieuDayType, l as NewChatConversation, n as NewChatMessage, p as NewEFLink, b as NewExpense, N as NewExpenseCategory, f as NewFocusCategory, i as NewFocusSession, e as NewIncome, c as NewIncomeCategory, r as NewLieuDay, S as Setting, T as TransactionType } from '../database-BKc0Oj26.mjs';
1
+ export { s as BankConnection, B as BankConnectionStatus, t as BankTransaction, k as ChatConversation, m as ChatMessage, C as ChatMessageRole, o as EFLink, K as ExchangeRate, a as Expense, E as ExpenseCategory, F as FocusCategory, h as FocusSession, j as FocusSessionFilters, g as FocusSessionStatus, H as HealthCategory, z as HealthCompletion, G as HealthCompletionFilters, w as HealthFrequencyConfig, v as HealthFrequencyType, x as HealthHabit, D as HealthHabitFilters, J as HealthStats, d as Income, I as IncomeCategory, q as LieuDay, L as LieuDayType, l as NewChatConversation, n as NewChatMessage, p as NewEFLink, b as NewExpense, N as NewExpenseCategory, f as NewFocusCategory, i as NewFocusSession, u as NewHealthCategory, A as NewHealthCompletion, y as NewHealthHabit, e as NewIncome, c as NewIncomeCategory, r as NewLieuDay, S as Setting, T as TransactionType } from '../database-3Vv5PNDa.mjs';
2
2
 
3
3
  /**
4
4
  * Cursor Cloud API Types
@@ -1,4 +1,4 @@
1
- export { s as BankConnection, B as BankConnectionStatus, t as BankTransaction, k as ChatConversation, m as ChatMessage, C as ChatMessageRole, o as EFLink, u as ExchangeRate, a as Expense, E as ExpenseCategory, F as FocusCategory, h as FocusSession, j as FocusSessionFilters, g as FocusSessionStatus, d as Income, I as IncomeCategory, q as LieuDay, L as LieuDayType, l as NewChatConversation, n as NewChatMessage, p as NewEFLink, b as NewExpense, N as NewExpenseCategory, f as NewFocusCategory, i as NewFocusSession, e as NewIncome, c as NewIncomeCategory, r as NewLieuDay, S as Setting, T as TransactionType } from '../database-BKc0Oj26.js';
1
+ export { s as BankConnection, B as BankConnectionStatus, t as BankTransaction, k as ChatConversation, m as ChatMessage, C as ChatMessageRole, o as EFLink, K as ExchangeRate, a as Expense, E as ExpenseCategory, F as FocusCategory, h as FocusSession, j as FocusSessionFilters, g as FocusSessionStatus, H as HealthCategory, z as HealthCompletion, G as HealthCompletionFilters, w as HealthFrequencyConfig, v as HealthFrequencyType, x as HealthHabit, D as HealthHabitFilters, J as HealthStats, d as Income, I as IncomeCategory, q as LieuDay, L as LieuDayType, l as NewChatConversation, n as NewChatMessage, p as NewEFLink, b as NewExpense, N as NewExpenseCategory, f as NewFocusCategory, i as NewFocusSession, u as NewHealthCategory, A as NewHealthCompletion, y as NewHealthHabit, e as NewIncome, c as NewIncomeCategory, r as NewLieuDay, S as Setting, T as TransactionType } from '../database-3Vv5PNDa.js';
2
2
 
3
3
  /**
4
4
  * Cursor Cloud API Types
@@ -1,4 +1,4 @@
1
- import { a as Expense, d as Income } from '../database-BKc0Oj26.mjs';
1
+ import { a as Expense, d as Income, w as HealthFrequencyConfig, x as HealthHabit } from '../database-3Vv5PNDa.mjs';
2
2
  import { Frequency } from '../constants/index.mjs';
3
3
 
4
4
  /**
@@ -17,12 +17,20 @@ declare function formatTime(seconds: number): string;
17
17
  * @example formatTotalTime(1800) => "30m"
18
18
  */
19
19
  declare function formatTotalTime(seconds: number): string;
20
+ /**
21
+ * Format duration in seconds to human-readable format (includes seconds)
22
+ * @example formatDuration(3661) => "1h 1m"
23
+ * @example formatDuration(125) => "2m 5s"
24
+ * @example formatDuration(45) => "45s"
25
+ */
26
+ declare function formatDuration(seconds: number): string;
20
27
  /**
21
28
  * Format a number as currency
22
29
  * Uses Swiss German locale for authentic Swiss formatting (apostrophe as thousands separator)
23
30
  * @example formatCurrency(1234.56, 'CHF') => "CHF 1'234.56"
31
+ * @example formatCurrency(1234.56) => "CHF 1'234.56" (defaults to CHF)
24
32
  */
25
- declare function formatCurrency(amount: number, currency: string, locale?: string): string;
33
+ declare function formatCurrency(amount: number, currency?: string, locale?: string): string;
26
34
  /**
27
35
  * Format a date as relative time (e.g., "2h ago", "3d ago")
28
36
  */
@@ -157,4 +165,43 @@ declare function calculateFocusStats(sessions: Array<{
157
165
  completionRate: number;
158
166
  };
159
167
 
160
- export { calculateFocusStats, calculateLieuBalance, calculateMonthlyExpenses, calculateMonthlyIncome, calculateMonthlySavings, calculateSavingsRate, formatCurrency, formatDate, formatDueDate, formatRelativeTime, formatTime, formatTotalTime, generateId, generateRandomColor, generateShortId, getRepoName, isNonEmptyString, isPositiveNumber, isValidCurrency, isValidEmail, isValidFrequency, isValidISODate, isValidUrl, toMonthlyAmount, toYearlyAmount, truncate };
168
+ /**
169
+ * Health & Habits Utilities
170
+ *
171
+ * Shared utility functions for the health & habits module.
172
+ * Used by both desktop and mobile apps.
173
+ */
174
+
175
+ /**
176
+ * Parse frequency config from JSON string
177
+ */
178
+ declare function parseFrequencyConfig(config: string | null): HealthFrequencyConfig | null;
179
+ /**
180
+ * Stringify frequency config to JSON
181
+ */
182
+ declare function stringifyFrequencyConfig(config: HealthFrequencyConfig | null): string | null;
183
+ /**
184
+ * Get human-readable frequency description for a habit
185
+ */
186
+ declare function getFrequencyDescription(habit: HealthHabit): string;
187
+ /**
188
+ * Check if habit should be done on a specific date based on frequency
189
+ */
190
+ declare function shouldDoOnDate(habit: HealthHabit, date: Date): boolean;
191
+ /**
192
+ * Check if habit should be done today
193
+ */
194
+ declare function shouldDoToday(habit: HealthHabit): boolean;
195
+ interface WeeklyProgressItem {
196
+ habit: HealthHabit;
197
+ target: number;
198
+ completed: number;
199
+ remaining: number;
200
+ isComplete: boolean;
201
+ }
202
+ /**
203
+ * Calculate weekly progress for a habit
204
+ */
205
+ declare function calculateWeeklyProgress(habit: HealthHabit, weekCompletionCount: number): WeeklyProgressItem;
206
+
207
+ export { type WeeklyProgressItem, calculateFocusStats, calculateLieuBalance, calculateMonthlyExpenses, calculateMonthlyIncome, calculateMonthlySavings, calculateSavingsRate, calculateWeeklyProgress, formatCurrency, formatDate, formatDueDate, formatDuration, formatRelativeTime, formatTime, formatTotalTime, generateId, generateRandomColor, generateShortId, getFrequencyDescription, getRepoName, isNonEmptyString, isPositiveNumber, isValidCurrency, isValidEmail, isValidFrequency, isValidISODate, isValidUrl, parseFrequencyConfig, shouldDoOnDate, shouldDoToday, stringifyFrequencyConfig, toMonthlyAmount, toYearlyAmount, truncate };
@@ -1,4 +1,4 @@
1
- import { a as Expense, d as Income } from '../database-BKc0Oj26.js';
1
+ import { a as Expense, d as Income, w as HealthFrequencyConfig, x as HealthHabit } from '../database-3Vv5PNDa.js';
2
2
  import { Frequency } from '../constants/index.js';
3
3
 
4
4
  /**
@@ -17,12 +17,20 @@ declare function formatTime(seconds: number): string;
17
17
  * @example formatTotalTime(1800) => "30m"
18
18
  */
19
19
  declare function formatTotalTime(seconds: number): string;
20
+ /**
21
+ * Format duration in seconds to human-readable format (includes seconds)
22
+ * @example formatDuration(3661) => "1h 1m"
23
+ * @example formatDuration(125) => "2m 5s"
24
+ * @example formatDuration(45) => "45s"
25
+ */
26
+ declare function formatDuration(seconds: number): string;
20
27
  /**
21
28
  * Format a number as currency
22
29
  * Uses Swiss German locale for authentic Swiss formatting (apostrophe as thousands separator)
23
30
  * @example formatCurrency(1234.56, 'CHF') => "CHF 1'234.56"
31
+ * @example formatCurrency(1234.56) => "CHF 1'234.56" (defaults to CHF)
24
32
  */
25
- declare function formatCurrency(amount: number, currency: string, locale?: string): string;
33
+ declare function formatCurrency(amount: number, currency?: string, locale?: string): string;
26
34
  /**
27
35
  * Format a date as relative time (e.g., "2h ago", "3d ago")
28
36
  */
@@ -157,4 +165,43 @@ declare function calculateFocusStats(sessions: Array<{
157
165
  completionRate: number;
158
166
  };
159
167
 
160
- export { calculateFocusStats, calculateLieuBalance, calculateMonthlyExpenses, calculateMonthlyIncome, calculateMonthlySavings, calculateSavingsRate, formatCurrency, formatDate, formatDueDate, formatRelativeTime, formatTime, formatTotalTime, generateId, generateRandomColor, generateShortId, getRepoName, isNonEmptyString, isPositiveNumber, isValidCurrency, isValidEmail, isValidFrequency, isValidISODate, isValidUrl, toMonthlyAmount, toYearlyAmount, truncate };
168
+ /**
169
+ * Health & Habits Utilities
170
+ *
171
+ * Shared utility functions for the health & habits module.
172
+ * Used by both desktop and mobile apps.
173
+ */
174
+
175
+ /**
176
+ * Parse frequency config from JSON string
177
+ */
178
+ declare function parseFrequencyConfig(config: string | null): HealthFrequencyConfig | null;
179
+ /**
180
+ * Stringify frequency config to JSON
181
+ */
182
+ declare function stringifyFrequencyConfig(config: HealthFrequencyConfig | null): string | null;
183
+ /**
184
+ * Get human-readable frequency description for a habit
185
+ */
186
+ declare function getFrequencyDescription(habit: HealthHabit): string;
187
+ /**
188
+ * Check if habit should be done on a specific date based on frequency
189
+ */
190
+ declare function shouldDoOnDate(habit: HealthHabit, date: Date): boolean;
191
+ /**
192
+ * Check if habit should be done today
193
+ */
194
+ declare function shouldDoToday(habit: HealthHabit): boolean;
195
+ interface WeeklyProgressItem {
196
+ habit: HealthHabit;
197
+ target: number;
198
+ completed: number;
199
+ remaining: number;
200
+ isComplete: boolean;
201
+ }
202
+ /**
203
+ * Calculate weekly progress for a habit
204
+ */
205
+ declare function calculateWeeklyProgress(habit: HealthHabit, weekCompletionCount: number): WeeklyProgressItem;
206
+
207
+ export { type WeeklyProgressItem, calculateFocusStats, calculateLieuBalance, calculateMonthlyExpenses, calculateMonthlyIncome, calculateMonthlySavings, calculateSavingsRate, calculateWeeklyProgress, formatCurrency, formatDate, formatDueDate, formatDuration, formatRelativeTime, formatTime, formatTotalTime, generateId, generateRandomColor, generateShortId, getFrequencyDescription, getRepoName, isNonEmptyString, isPositiveNumber, isValidCurrency, isValidEmail, isValidFrequency, isValidISODate, isValidUrl, parseFrequencyConfig, shouldDoOnDate, shouldDoToday, stringifyFrequencyConfig, toMonthlyAmount, toYearlyAmount, truncate };