@classytic/payroll 2.7.5 → 2.8.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.
- package/README.md +333 -323
- package/dist/attendance.calculator-BZcv2iii.d.ts +336 -0
- package/dist/calculators/index.d.ts +3 -299
- package/dist/calculators/index.js +154 -19
- package/dist/calculators/index.js.map +1 -1
- package/dist/core/index.d.ts +321 -0
- package/dist/core/index.js +1962 -0
- package/dist/core/index.js.map +1 -0
- package/dist/{employee-identity-Cq2wo9-2.d.ts → error-helpers-Bm6lMny2.d.ts} +257 -7
- package/dist/{index-DjB72l6e.d.ts → index-BKLkuSAs.d.ts} +248 -132
- package/dist/index.d.ts +418 -658
- package/dist/index.js +1179 -373
- package/dist/index.js.map +1 -1
- package/dist/payroll-states-DBt0XVm-.d.ts +598 -0
- package/dist/{prorating.calculator-C7sdFiG2.d.ts → prorating.calculator-C33fWBQf.d.ts} +2 -2
- package/dist/schemas/index.d.ts +2 -2
- package/dist/schemas/index.js +95 -75
- package/dist/schemas/index.js.map +1 -1
- package/dist/{types-BVDjiVGS.d.ts → types-bZdAJueH.d.ts} +427 -12
- package/dist/utils/index.d.ts +17 -5
- package/dist/utils/index.js +185 -25
- package/dist/utils/index.js.map +1 -1
- package/package.json +5 -1
package/dist/utils/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utils/logger.ts","../../src/utils/date.ts","../../src/utils/money.ts","../../src/utils/calculation.ts","../../src/core/config.ts","../../src/calculators/prorating.calculator.ts","../../src/enums.ts","../../src/utils/validation.ts","../../src/utils/query-builders.ts","../../src/utils/leave.ts","../../src/errors/index.ts","../../src/utils/employee-lookup.ts","../../src/utils/org-resolution.ts","../../src/utils/employee-identity.ts"],"names":["logger","periodWorkingDays","employee","minValue","maxValue","min","max","diffInDays","sum","operationName"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @classytic/payroll - Logger\n *\n * Pluggable logger abstraction\n * Defaults to console, can be replaced with pino, winston, etc.\n */\n\nimport type { Logger } from '../types.js';\n\n// ============================================================================\n// Default Logger Implementation\n// ============================================================================\n\nconst createConsoleLogger = (): Logger => ({\n info: (message: string, meta?: Record<string, unknown>) => {\n if (meta) {\n console.log(`[Payroll] INFO: ${message}`, meta);\n } else {\n console.log(`[Payroll] INFO: ${message}`);\n }\n },\n error: (message: string, meta?: Record<string, unknown>) => {\n if (meta) {\n console.error(`[Payroll] ERROR: ${message}`, meta);\n } else {\n console.error(`[Payroll] ERROR: ${message}`);\n }\n },\n warn: (message: string, meta?: Record<string, unknown>) => {\n if (meta) {\n console.warn(`[Payroll] WARN: ${message}`, meta);\n } else {\n console.warn(`[Payroll] WARN: ${message}`);\n }\n },\n debug: (message: string, meta?: Record<string, unknown>) => {\n if (process.env.NODE_ENV !== 'production') {\n if (meta) {\n console.log(`[Payroll] DEBUG: ${message}`, meta);\n } else {\n console.log(`[Payroll] DEBUG: ${message}`);\n }\n }\n },\n});\n\n// ============================================================================\n// Logger State\n// ============================================================================\n\nlet currentLogger: Logger = createConsoleLogger();\nlet loggingEnabled = true;\n\n// ============================================================================\n// Logger Functions\n// ============================================================================\n\n/**\n * Get the current logger instance (respects loggingEnabled flag)\n */\nexport function getLogger(): Logger {\n return {\n info: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.info(message, meta);\n },\n error: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.error(message, meta);\n },\n warn: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.warn(message, meta);\n },\n debug: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.debug(message, meta);\n },\n };\n}\n\n/**\n * Set a custom logger instance\n */\nexport function setLogger(logger: Logger): void {\n currentLogger = logger;\n}\n\n/**\n * Reset to default console logger\n */\nexport function resetLogger(): void {\n currentLogger = createConsoleLogger();\n}\n\n/**\n * Create a child logger with prefix (respects loggingEnabled flag)\n */\nexport function createChildLogger(prefix: string): Logger {\n const parent = currentLogger;\n return {\n info: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) parent.info(`[${prefix}] ${message}`, meta);\n },\n error: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) parent.error(`[${prefix}] ${message}`, meta);\n },\n warn: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) parent.warn(`[${prefix}] ${message}`, meta);\n },\n debug: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) parent.debug(`[${prefix}] ${message}`, meta);\n },\n };\n}\n\n/**\n * Create a silent logger (for testing)\n */\nexport function createSilentLogger(): Logger {\n return {\n info: () => {},\n error: () => {},\n warn: () => {},\n debug: () => {},\n };\n}\n\n/**\n * Enable logging globally\n */\nexport function enableLogging(): void {\n loggingEnabled = true;\n}\n\n/**\n * Disable logging globally (useful for production)\n */\nexport function disableLogging(): void {\n loggingEnabled = false;\n}\n\n/**\n * Check if logging is enabled\n */\nexport function isLoggingEnabled(): boolean {\n return loggingEnabled;\n}\n\n// ============================================================================\n// Logger Proxy Object\n// ============================================================================\n\n/**\n * Logger proxy that always delegates to currentLogger\n * Respects global logging enabled/disabled state\n */\nexport const logger: Logger = {\n info: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.info(message, meta);\n },\n error: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.error(message, meta);\n },\n warn: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.warn(message, meta);\n },\n debug: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.debug(message, meta);\n },\n};\n\nexport default logger;\n\n","/**\n * @classytic/payroll - Date Utilities\n *\n * Pure, composable, testable date operations\n * No side effects, no mutations\n */\n\nimport type { PayPeriodInfo, PaymentFrequency } from '../types.js';\n\n// ============================================================================\n// Date Arithmetic\n// ============================================================================\n\n/**\n * Add days to a date\n */\nexport function addDays(date: Date, days: number): Date {\n const result = new Date(date);\n result.setDate(result.getDate() + days);\n return result;\n}\n\n/**\n * Add months to a date\n */\nexport function addMonths(date: Date, months: number): Date {\n const result = new Date(date);\n result.setMonth(result.getMonth() + months);\n return result;\n}\n\n/**\n * Add years to a date\n */\nexport function addYears(date: Date, years: number): Date {\n const result = new Date(date);\n result.setFullYear(result.getFullYear() + years);\n return result;\n}\n\n/**\n * Subtract days from a date\n */\nexport function subDays(date: Date, days: number): Date {\n return addDays(date, -days);\n}\n\n/**\n * Subtract months from a date\n */\nexport function subMonths(date: Date, months: number): Date {\n return addMonths(date, -months);\n}\n\n// ============================================================================\n// Date Boundaries\n// ============================================================================\n\n/**\n * Get the start of a month\n */\nexport function startOfMonth(date: Date): Date {\n const result = new Date(date);\n result.setDate(1);\n result.setHours(0, 0, 0, 0);\n return result;\n}\n\n/**\n * Get the end of a month\n */\nexport function endOfMonth(date: Date): Date {\n const result = new Date(date);\n result.setMonth(result.getMonth() + 1, 0);\n result.setHours(23, 59, 59, 999);\n return result;\n}\n\n/**\n * Get the start of a year\n */\nexport function startOfYear(date: Date): Date {\n const result = new Date(date);\n result.setMonth(0, 1);\n result.setHours(0, 0, 0, 0);\n return result;\n}\n\n/**\n * Get the end of a year\n */\nexport function endOfYear(date: Date): Date {\n const result = new Date(date);\n result.setMonth(11, 31);\n result.setHours(23, 59, 59, 999);\n return result;\n}\n\n/**\n * Get the start of a day\n */\nexport function startOfDay(date: Date): Date {\n const result = new Date(date);\n result.setHours(0, 0, 0, 0);\n return result;\n}\n\n/**\n * Get the end of a day\n */\nexport function endOfDay(date: Date): Date {\n const result = new Date(date);\n result.setHours(23, 59, 59, 999);\n return result;\n}\n\n// ============================================================================\n// Date Differences\n// ============================================================================\n\n/**\n * Calculate difference in days between two dates\n */\nexport function diffInDays(start: Date, end: Date): number {\n return Math.ceil(\n (new Date(end).getTime() - new Date(start).getTime()) / (1000 * 60 * 60 * 24)\n );\n}\n\n/**\n * Calculate difference in months between two dates\n */\nexport function diffInMonths(start: Date, end: Date): number {\n const startDate = new Date(start);\n const endDate = new Date(end);\n return (\n (endDate.getFullYear() - startDate.getFullYear()) * 12 +\n (endDate.getMonth() - startDate.getMonth())\n );\n}\n\n/**\n * Calculate difference in years between two dates\n */\nexport function diffInYears(start: Date, end: Date): number {\n return Math.floor(diffInMonths(start, end) / 12);\n}\n\n// Aliases for backwards compatibility\nexport const daysBetween = diffInDays;\nexport const monthsBetween = diffInMonths;\n\n// ============================================================================\n// Day Type Checks\n// ============================================================================\n\n/**\n * Check if date is a weekday (Mon-Fri)\n */\nexport function isWeekday(date: Date): boolean {\n const day = new Date(date).getDay();\n return day >= 1 && day <= 5;\n}\n\n/**\n * Check if date is a weekend (Sat-Sun)\n */\nexport function isWeekend(date: Date): boolean {\n const day = new Date(date).getDay();\n return day === 0 || day === 6;\n}\n\n/**\n * Get day of week (0=Sunday, 6=Saturday)\n */\nexport function getDayOfWeek(date: Date): number {\n return new Date(date).getDay();\n}\n\n/**\n * Get day name\n */\nexport function getDayName(date: Date): string {\n const days = [\n 'Sunday',\n 'Monday',\n 'Tuesday',\n 'Wednesday',\n 'Thursday',\n 'Friday',\n 'Saturday',\n ];\n return days[getDayOfWeek(date)];\n}\n\n// ============================================================================\n// Pay Period Functions\n// ============================================================================\n\n/**\n * Get pay period for a given month and year\n */\nexport function getPayPeriod(month: number, year: number): PayPeriodInfo {\n const startDate = new Date(year, month - 1, 1);\n return {\n month,\n year,\n startDate: startOfMonth(startDate),\n endDate: endOfMonth(startDate),\n };\n}\n\n/**\n * Get current pay period\n */\nexport function getCurrentPeriod(date = new Date()): { year: number; month: number } {\n const d = new Date(date);\n return {\n year: d.getFullYear(),\n month: d.getMonth() + 1,\n };\n}\n\n/**\n * Get working days in a month\n */\nexport function getWorkingDaysInMonth(year: number, month: number): number {\n const start = new Date(year, month - 1, 1);\n const end = endOfMonth(start);\n let count = 0;\n \n const current = new Date(start);\n while (current <= end) {\n if (isWeekday(current)) {\n count++;\n }\n current.setDate(current.getDate() + 1);\n }\n \n return count;\n}\n\n/**\n * Get total days in a month\n */\nexport function getDaysInMonth(year: number, month: number): number {\n return new Date(year, month, 0).getDate();\n}\n\n// ============================================================================\n// Employment Date Functions\n// ============================================================================\n\n/**\n * Calculate probation end date\n */\nexport function calculateProbationEnd(\n hireDate: Date,\n probationMonths: number\n): Date | null {\n if (!probationMonths || probationMonths <= 0) return null;\n return addMonths(hireDate, probationMonths);\n}\n\n/**\n * Check if employee is on probation\n */\nexport function isOnProbation(\n probationEndDate: Date | null | undefined,\n now = new Date()\n): boolean {\n if (!probationEndDate) return false;\n return now < new Date(probationEndDate);\n}\n\n/**\n * Calculate years of service\n */\nexport function calculateYearsOfService(\n hireDate: Date,\n terminationDate?: Date | null\n): number {\n const end = terminationDate || new Date();\n const days = diffInDays(hireDate, end);\n return Math.max(0, Math.floor((days / 365.25) * 10) / 10);\n}\n\n// ============================================================================\n// Range Functions\n// ============================================================================\n\n/**\n * Check if a date is within a range\n */\nexport function isDateInRange(date: Date, start: Date, end: Date): boolean {\n const checkDate = new Date(date);\n return checkDate >= new Date(start) && checkDate <= new Date(end);\n}\n\n/**\n * Check if an item with effectiveFrom/effectiveTo dates is effective for a given period.\n *\n * Used for filtering allowances, deductions, and other time-bounded compensation items.\n * An item is considered effective if its date range overlaps with the period.\n *\n * @param item - Object with optional effectiveFrom and effectiveTo dates\n * @param periodStart - Start of the period to check\n * @param periodEnd - End of the period to check\n * @returns true if the item is effective during any part of the period\n *\n * @example\n * ```typescript\n * const allowance = { effectiveFrom: new Date('2024-01-01'), effectiveTo: null };\n * const periodStart = new Date('2024-03-01');\n * const periodEnd = new Date('2024-03-31');\n *\n * isEffectiveForPeriod(allowance, periodStart, periodEnd); // true\n * ```\n */\nexport function isEffectiveForPeriod(\n item: { effectiveFrom?: Date | null; effectiveTo?: Date | null },\n periodStart: Date,\n periodEnd: Date\n): boolean {\n const effectiveFrom = item.effectiveFrom ? new Date(item.effectiveFrom) : new Date(0);\n const effectiveTo = item.effectiveTo ? new Date(item.effectiveTo) : new Date('2099-12-31');\n\n // Item is effective if its range overlaps with the period\n return effectiveFrom <= periodEnd && effectiveTo >= periodStart;\n}\n\n/**\n * Get date range for a pay period\n */\nexport function getPayPeriodDateRange(\n month: number,\n year: number\n): { start: Date; end: Date } {\n const period = getPayPeriod(month, year);\n return { start: period.startDate, end: period.endDate };\n}\n\n// ============================================================================\n// Formatting Functions\n// ============================================================================\n\n/**\n * Format date for database storage\n */\nexport function formatDateForDB(date: Date): string {\n if (!date) return '';\n return new Date(date).toISOString();\n}\n\n/**\n * Parse date from database\n */\nexport function parseDBDate(dateString: string): Date | null {\n if (!dateString) return null;\n return new Date(dateString);\n}\n\n/**\n * Format period as string (e.g., \"01/2025\")\n */\nexport function formatPeriod({ month, year }: { month: number; year: number }): string {\n return `${String(month).padStart(2, '0')}/${year}`;\n}\n\n/**\n * Parse period string back to object\n */\nexport function parsePeriod(periodString: string): { month: number; year: number } {\n const [month, year] = periodString.split('/').map(Number);\n return { month, year };\n}\n\n/**\n * Format month name\n */\nexport function getMonthName(month: number): string {\n const months = [\n 'January', 'February', 'March', 'April', 'May', 'June',\n 'July', 'August', 'September', 'October', 'November', 'December',\n ];\n return months[month - 1] || '';\n}\n\n/**\n * Format short month name\n */\nexport function getShortMonthName(month: number): string {\n const months = [\n 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',\n ];\n return months[month - 1] || '';\n}\n\n// ============================================================================\n// Default Export\n// ============================================================================\n\nexport default {\n addDays,\n addMonths,\n addYears,\n subDays,\n subMonths,\n startOfMonth,\n endOfMonth,\n startOfYear,\n endOfYear,\n startOfDay,\n endOfDay,\n diffInDays,\n diffInMonths,\n diffInYears,\n daysBetween,\n monthsBetween,\n isWeekday,\n isWeekend,\n getDayOfWeek,\n getDayName,\n getPayPeriod,\n getCurrentPeriod,\n getWorkingDaysInMonth,\n getDaysInMonth,\n calculateProbationEnd,\n isOnProbation,\n calculateYearsOfService,\n isDateInRange,\n isEffectiveForPeriod,\n getPayPeriodDateRange,\n formatDateForDB,\n parseDBDate,\n formatPeriod,\n parsePeriod,\n getMonthName,\n getShortMonthName,\n};\n\n","/**\n * Money calculation utilities for payroll compliance\n *\n * Uses banker's rounding (round half to even) to prevent systematic bias\n * in financial calculations, as recommended for payroll systems.\n *\n * PRECISION: All functions default to 2 decimal places (cents/paise precision)\n * to maintain accuracy in payroll calculations. Amounts are stored as floating\n * point numbers with decimal precision (e.g., 1000.50 for $1,000.50).\n *\n * ## Design Decision: Floating Point Storage\n *\n * This module uses floating-point numbers (IEEE 754 double precision) for money\n * storage rather than integer minor units (cents) or MongoDB Decimal128.\n *\n * ### Tradeoffs:\n *\n * **Why floating point:**\n * - Simpler API (developers think in dollars, not cents)\n * - More intuitive JSON serialization (100.50 vs 10050)\n * - Sufficient precision for payroll (15 significant digits)\n * - Banker's rounding mitigates cumulative drift\n *\n * **Why NOT integer cents:**\n * - Would require breaking schema changes\n * - All consumer code needs conversion (amount / 100)\n * - More verbose for reporting/display\n *\n * **Why NOT Decimal128:**\n * - MongoDB-specific, reduces portability\n * - Requires special BSON handling in application code\n * - Overkill for typical payroll amounts (< $10M/employee/year)\n *\n * ### Mitigation:\n * - All calculations go through roundMoney() with banker's rounding\n * - Final amounts always rounded to 2 decimal places before storage\n * - Aggregations use MongoDB $round operator for consistency\n *\n * @see https://en.wikipedia.org/wiki/Banker%27s_rounding\n */\n\n/**\n * Banker's rounding (round half to even) with decimal precision\n *\n * When rounding 0.5, rounds to the nearest even number:\n * - 2.5 → 2 (even)\n * - 3.5 → 4 (even)\n * - 4.5 → 4 (even)\n *\n * This prevents systematic bias in cumulative rounding that occurs\n * with standard rounding (always up), which is critical for payroll compliance.\n *\n * @param value - The number to round\n * @param decimals - Number of decimal places (default: 2 for cent precision)\n * @returns The rounded value\n */\nexport function roundMoney(value: number, decimals = 2): number {\n const multiplier = Math.pow(10, decimals);\n const scaled = value * multiplier;\n const fraction = scaled - Math.floor(scaled);\n\n // If exactly 0.5 (within tolerance), round to nearest even\n // Use 1e-10 tolerance to handle floating-point imprecision\n if (Math.abs(fraction - 0.5) < 1e-10) {\n const floor = Math.floor(scaled);\n const rounded = floor % 2 === 0 ? floor : floor + 1;\n return rounded / multiplier;\n }\n\n // Otherwise use standard rounding\n return Math.round(scaled) / multiplier;\n}\n\n/**\n * Round money with validation for negative values\n *\n * @param value - The number to round\n * @param decimals - Number of decimal places (default: 2 for cent precision)\n * @returns The rounded value (never negative for deductions)\n */\nexport function roundMoneyPositive(value: number, decimals = 2): number {\n return Math.max(0, roundMoney(value, decimals));\n}\n\n/**\n * Calculate percentage of an amount with banker's rounding\n *\n * @param amount - Base amount\n * @param percentage - Percentage (e.g., 10 for 10%)\n * @param decimals - Decimal places to round to (default: 2 for cent precision)\n * @returns Rounded percentage amount\n */\nexport function percentageOf(amount: number, percentage: number, decimals = 2): number {\n return roundMoney((amount * percentage) / 100, decimals);\n}\n\n/**\n * Prorate an amount by a ratio with banker's rounding\n *\n * @param amount - Base amount\n * @param ratio - Proration ratio (0 to 1)\n * @param decimals - Decimal places (default: 2 for cent precision)\n * @returns Prorated amount\n */\nexport function prorateAmount(amount: number, ratio: number, decimals = 2): number {\n return roundMoney(amount * ratio, decimals);\n}\n","/**\n * @classytic/payroll - Calculation Utilities\n *\n * Pure, functional, composable financial calculations\n * No side effects, highly testable\n */\n\nimport type {\n Allowance,\n Deduction,\n Compensation,\n TaxCalculationResult,\n CompensationBreakdownResult,\n} from '../types.js';\nimport { roundMoney } from './money.js';\n\n// ============================================================================\n// Basic Math Operations\n// ============================================================================\n\n/**\n * Sum array of numbers\n */\nexport function sum(numbers: number[]): number {\n return numbers.reduce((total, n) => total + n, 0);\n}\n\n/**\n * Sum by property\n */\nexport function sumBy<T>(items: T[], getter: (item: T) => number): number {\n return items.reduce((total, item) => total + getter(item), 0);\n}\n\n/**\n * Sum allowances\n */\nexport function sumAllowances(allowances: Array<{ amount: number }>): number {\n return sumBy(allowances, (a) => a.amount);\n}\n\n/**\n * Sum deductions\n */\nexport function sumDeductions(deductions: Array<{ amount: number }>): number {\n return sumBy(deductions, (d) => d.amount);\n}\n\n/**\n * ROUNDING POLICY FOR FINANCIAL CALCULATIONS\n *\n * Monetary amounts are stored as floating point numbers in major units with\n * decimal precision (e.g., 1000.50 for $1,000.50 or ₹1,000.50).\n *\n * PRECISION: All calculations preserve 2 decimal places (cent/paise precision)\n * to maintain accuracy required for payroll compliance.\n *\n * Rounding Rules:\n * 1. Banker's Rounding (Round Half to Even): Used for fair rounding over many transactions\n * 2. All intermediate calculations maintain full precision\n * 3. Final amounts rounded to 2 decimals using banker's rounding\n * 4. Tax calculations use banker's rounding for compliance\n *\n * Example:\n * Input: 1000.50 base + 15% tax\n * Calculation: 1000.50 * 0.15 = 150.075 → rounds to 150.08 (banker's rounding to 2 decimals)\n * Result: Tax = 150.08\n *\n * @see https://en.wikipedia.org/wiki/Rounding#Round_half_to_even\n */\n\n/**\n * Banker's Rounding (Round Half to Even) - Integer precision\n *\n * Rounds to the nearest integer using banker's rounding (round half to even).\n * This prevents systematic bias in rounding over many transactions.\n *\n * Uses epsilon check for safe floating-point comparison.\n *\n * Examples:\n * 0.5 → 0 (even)\n * 1.5 → 2 (even)\n * 2.5 → 2 (even)\n * 3.5 → 4 (even)\n *\n * @param value - The number to round\n * @returns Rounded integer\n * @note For money calculations with decimal precision, use `roundMoney()` instead\n */\nexport function bankersRound(value: number): number {\n const floor = Math.floor(value);\n const fraction = value - floor;\n\n // Use epsilon check for safer floating-point comparison\n if (Math.abs(fraction - 0.5) < Number.EPSILON) {\n // If halfway, round to even\n return floor % 2 === 0 ? floor : floor + 1;\n }\n\n // Otherwise use standard rounding\n return Math.round(value);\n}\n\n/**\n * Apply percentage to amount with banker's rounding (2 decimal precision)\n *\n * @param amount - Amount in major units (e.g., dollars, rupees)\n * @param percentage - Percentage to apply (e.g., 15 for 15%)\n * @param decimals - Decimal places for precision (default: 2 for cent precision)\n * @returns Result in major units, properly rounded to 2 decimals\n * @note Uses banker's rounding (round half to even) to preserve cent precision.\n * Equivalent to percentageOf() from money.ts.\n */\nexport function applyPercentage(amount: number, percentage: number, decimals = 2): number {\n // Use centralized roundMoney for consistent banker's rounding across codebase\n const result = (amount * percentage) / 100;\n return roundMoney(result, decimals);\n}\n\n/**\n * Calculate percentage of total\n */\nexport function calculatePercentage(part: number, total: number): number {\n return total > 0 ? bankersRound((part / total) * 100) : 0;\n}\n\n/**\n * Round to decimal places using banker's rounding\n */\nexport function roundTo(value: number, decimals = 2): number {\n const factor = Math.pow(10, decimals);\n return bankersRound(value * factor) / factor;\n}\n\n// ============================================================================\n// Salary Calculations\n// ============================================================================\n\n/**\n * Calculate gross salary from base and allowances\n */\nexport function calculateGross(\n baseAmount: number,\n allowances: Array<{ amount: number }>\n): number {\n return baseAmount + sumAllowances(allowances);\n}\n\n/**\n * Calculate net salary from gross and deductions\n */\nexport function calculateNet(\n gross: number,\n deductions: Array<{ amount: number }>\n): number {\n return Math.max(0, gross - sumDeductions(deductions));\n}\n\n/**\n * Calculate total compensation\n */\nexport function calculateTotalCompensation(\n baseAmount: number,\n allowances: Array<{ amount: number }>,\n deductions: Array<{ amount: number }>\n): { gross: number; net: number; deductions: number } {\n const gross = calculateGross(baseAmount, allowances);\n const totalDeductions = sumDeductions(deductions);\n const net = calculateNet(gross, deductions);\n return { gross, net, deductions: totalDeductions };\n}\n\n// ============================================================================\n// Allowance & Deduction Calculation\n// ============================================================================\n\n/**\n * Calculate allowance amount (handles percentage-based)\n */\nexport function calculateAllowanceAmount(\n allowance: Pick<Allowance, 'amount' | 'isPercentage' | 'value'>,\n baseAmount: number\n): number {\n if (allowance.isPercentage && allowance.value !== undefined) {\n return applyPercentage(baseAmount, allowance.value);\n }\n return allowance.amount;\n}\n\n/**\n * Calculate deduction amount (handles percentage-based)\n */\nexport function calculateDeductionAmount(\n deduction: Pick<Deduction, 'amount' | 'isPercentage' | 'value'>,\n baseAmount: number\n): number {\n if (deduction.isPercentage && deduction.value !== undefined) {\n return applyPercentage(baseAmount, deduction.value);\n }\n return deduction.amount;\n}\n\n/**\n * Calculate all allowances with their actual amounts\n */\nexport function calculateAllowances(\n allowances: Allowance[],\n baseAmount: number\n): Array<Allowance & { calculatedAmount: number }> {\n return allowances.map((allowance) => ({\n ...allowance,\n calculatedAmount: calculateAllowanceAmount(allowance, baseAmount),\n }));\n}\n\n/**\n * Calculate all deductions with their actual amounts\n */\nexport function calculateDeductions(\n deductions: Deduction[],\n baseAmount: number\n): Array<Deduction & { calculatedAmount: number }> {\n return deductions.map((deduction) => ({\n ...deduction,\n calculatedAmount: calculateDeductionAmount(deduction, baseAmount),\n }));\n}\n\n// ============================================================================\n// Compensation Breakdown\n// ============================================================================\n\n/**\n * Calculate full compensation breakdown\n */\nexport function calculateCompensationBreakdown(\n compensation: Pick<Compensation, 'baseAmount' | 'allowances' | 'deductions'>\n): CompensationBreakdownResult {\n const { baseAmount, allowances = [], deductions = [] } = compensation;\n\n const calculatedAllowances = calculateAllowances(allowances, baseAmount);\n const calculatedDeductions = calculateDeductions(deductions, baseAmount);\n\n const grossAmount =\n baseAmount + sumBy(calculatedAllowances, (a) => a.calculatedAmount);\n const netAmount =\n grossAmount - sumBy(calculatedDeductions, (d) => d.calculatedAmount);\n\n return {\n baseAmount,\n allowances: calculatedAllowances,\n deductions: calculatedDeductions,\n grossAmount,\n netAmount: Math.max(0, netAmount),\n };\n}\n\n// ============================================================================\n// Tax Calculations\n// ============================================================================\n\n/**\n * Apply tax brackets to calculate tax\n *\n * Uses banker's rounding for compliance (rounds to 2 decimal places).\n * Consistent with all other money calculations in the system.\n */\nexport function applyTaxBrackets(\n amount: number,\n brackets: Array<{ min: number; max: number; rate: number }>\n): number {\n let tax = 0;\n\n for (const bracket of brackets) {\n if (amount > bracket.min) {\n const taxableAmount = Math.min(amount, bracket.max) - bracket.min;\n tax += taxableAmount * bracket.rate;\n }\n }\n\n // Use roundMoney for consistency with all other money calculations\n return roundMoney(tax);\n}\n\n/**\n * Calculate tax with result\n */\nexport function calculateTax(\n amount: number,\n brackets: Array<{ min: number; max: number; rate: number }>\n): TaxCalculationResult {\n const tax = applyTaxBrackets(amount, brackets);\n return {\n gross: amount,\n tax,\n net: amount - tax,\n };\n}\n\n// ============================================================================\n// Overtime Calculations\n// ============================================================================\n\n/**\n * Calculate overtime pay\n */\nexport function calculateOvertime(\n hourlyRate: number,\n overtimeHours: number,\n multiplier = 1.5\n): number {\n return Math.round(hourlyRate * overtimeHours * multiplier);\n}\n\n/**\n * Calculate hourly rate from monthly salary\n */\nexport function calculateHourlyRate(\n monthlySalary: number,\n hoursPerMonth = 176 // 44 hours/week * 4 weeks\n): number {\n return Math.round(monthlySalary / hoursPerMonth);\n}\n\n/**\n * Calculate daily rate from monthly salary\n */\nexport function calculateDailyRate(\n monthlySalary: number,\n daysPerMonth = 22\n): number {\n return Math.round(monthlySalary / daysPerMonth);\n}\n\n// ============================================================================\n// Default Export\n// ============================================================================\n\nexport default {\n sum,\n sumBy,\n sumAllowances,\n sumDeductions,\n applyPercentage,\n calculatePercentage,\n roundTo,\n calculateGross,\n calculateNet,\n calculateTotalCompensation,\n calculateAllowanceAmount,\n calculateDeductionAmount,\n calculateAllowances,\n calculateDeductions,\n calculateCompensationBreakdown,\n applyTaxBrackets,\n calculateTax,\n calculateOvertime,\n calculateHourlyRate,\n calculateDailyRate,\n};\n\n","/**\n * @classytic/payroll - Configuration & Calculation Utilities\n *\n * DESIGN PRINCIPLES:\n * 1. Accept data, don't manage it\n * 2. Pure functions - easy to test, no side effects\n * 3. Smart defaults that work out of the box\n * 4. Override at operation time when needed\n *\n * The payroll package CALCULATES, it doesn't STORE calendars/holidays.\n * Your app manages that data and passes it when needed.\n */\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Work schedule configuration */\nexport interface WorkSchedule {\n /** Working days (0=Sun, 1=Mon, ..., 6=Sat). Default: Mon-Fri */\n workingDays: number[];\n /** Hours per work day. Default: 8 */\n hoursPerDay: number;\n}\n\n/** Options passed when processing payroll */\nexport interface PayrollProcessingOptions {\n /** Holidays in this period (from YOUR app's holiday model) */\n holidays?: Date[];\n /** Override work schedule for this operation */\n workSchedule?: Partial<WorkSchedule>;\n /** Skip tax calculation */\n skipTax?: boolean;\n /** Skip proration (pay full amount regardless of hire/termination date) */\n skipProration?: boolean;\n /** Skip attendance deduction */\n skipAttendance?: boolean;\n}\n\n/** Working days calculation result */\nexport interface WorkingDaysResult {\n /** Total calendar days in period */\n totalDays: number;\n /** Working days (excluding weekends and holidays) */\n workingDays: number;\n /** Weekend days */\n weekends: number;\n /** Holiday count */\n holidays: number;\n}\n\n/** Proration calculation result */\nexport interface ProrationResult {\n /** Proration ratio (0-1) */\n ratio: number;\n /** Reason for proration */\n reason: 'full' | 'new_hire' | 'termination' | 'both';\n /** Whether salary should be prorated */\n isProrated: boolean;\n}\n\n/** Tax calculation result */\nexport interface TaxResult {\n /** Tax amount */\n amount: number;\n /** Effective tax rate */\n effectiveRate: number;\n}\n\n/** Attendance data (from YOUR attendance system) */\nexport interface AttendanceInput {\n /**\n * Expected work days in period.\n * If not provided, derived from employee's workSchedule and period dates.\n */\n expectedDays?: number;\n /** Actual days worked */\n actualDays: number;\n}\n\n/** Complete salary calculation result */\nexport interface SalaryCalculationResult {\n /** Original base salary */\n baseSalary: number;\n /** Prorated base salary */\n proratedBase: number;\n /** Total allowances */\n totalAllowances: number;\n /** Total deductions (excluding tax) */\n totalDeductions: number;\n /** Attendance deduction */\n attendanceDeduction: number;\n /** Gross salary (prorated base + allowances) */\n grossSalary: number;\n /** Tax amount */\n taxAmount: number;\n /** Net salary (gross - all deductions - tax) */\n netSalary: number;\n /** Proration details */\n proration: ProrationResult;\n /** Working days details */\n workingDays: WorkingDaysResult;\n /** Itemized breakdown */\n breakdown: {\n allowances: Array<{ type: string; amount: number; taxable: boolean }>;\n deductions: Array<{ type: string; amount: number }>;\n };\n}\n\n// ============================================================================\n// Default Configuration\n// ============================================================================\n\n/**\n * Default tax brackets (US federal example)\n * For multi-jurisdiction support, use the jurisdiction system instead\n */\nexport const DEFAULT_TAX_BRACKETS: Array<{ min: number; max: number; rate: number }> = [\n { min: 0, max: 10000, rate: 0.10 },\n { min: 10000, max: 40000, rate: 0.12 },\n { min: 40000, max: 85000, rate: 0.22 },\n { min: 85000, max: 165000, rate: 0.24 },\n { min: 165000, max: 215000, rate: 0.32 },\n { min: 215000, max: 540000, rate: 0.35 },\n { min: 540000, max: Infinity, rate: 0.37 },\n];\n\nexport const DEFAULT_WORK_SCHEDULE: WorkSchedule = {\n workingDays: [1, 2, 3, 4, 5], // Monday to Friday\n hoursPerDay: 8,\n};\n\n// ============================================================================\n// Pure Calculation Functions\n// ============================================================================\n\n/**\n * Count working days in a date range\n *\n * @example\n * const result = countWorkingDays(\n * new Date('2024-03-01'),\n * new Date('2024-03-31'),\n * { workingDays: [1,2,3,4,5], holidays: companyHolidays }\n * );\n */\nexport function countWorkingDays(\n startDate: Date,\n endDate: Date,\n options: {\n workingDays?: number[];\n holidays?: Date[];\n } = {}\n): WorkingDaysResult {\n const workDays = options.workingDays || DEFAULT_WORK_SCHEDULE.workingDays;\n const holidaySet = new Set(\n (options.holidays || []).map(d => new Date(d).toDateString())\n );\n\n let totalDays = 0;\n let workingDays = 0;\n let holidays = 0;\n let weekends = 0;\n\n const current = new Date(startDate);\n current.setHours(0, 0, 0, 0);\n const end = new Date(endDate);\n end.setHours(0, 0, 0, 0);\n\n while (current <= end) {\n totalDays++;\n const isHoliday = holidaySet.has(current.toDateString());\n const isWorkDay = workDays.includes(current.getDay());\n\n if (isHoliday) {\n holidays++;\n } else if (isWorkDay) {\n workingDays++;\n } else {\n weekends++;\n }\n\n current.setDate(current.getDate() + 1);\n }\n\n return { totalDays, workingDays, weekends, holidays };\n}\n\n/**\n * Calculate proration ratio for partial months\n *\n * @example\n * const proration = calculateProration(\n * employee.hireDate,\n * employee.terminationDate,\n * periodStart,\n * periodEnd\n * );\n */\nexport function calculateProration(\n hireDate: Date,\n terminationDate: Date | null | undefined,\n periodStart: Date,\n periodEnd: Date\n): ProrationResult {\n const hire = new Date(hireDate);\n hire.setHours(0, 0, 0, 0);\n const term = terminationDate ? new Date(terminationDate) : null;\n if (term) term.setHours(0, 0, 0, 0);\n const start = new Date(periodStart);\n start.setHours(0, 0, 0, 0);\n const end = new Date(periodEnd);\n end.setHours(0, 0, 0, 0);\n\n // Employee not active in this period\n if (hire > end || (term && term < start)) {\n return { ratio: 0, reason: 'full', isProrated: true };\n }\n\n // Effective dates within the period\n const effectiveStart = hire > start ? hire : start;\n const effectiveEnd = term && term < end ? term : end;\n\n // Calculate days\n const totalDays = Math.ceil((end.getTime() - start.getTime()) / 86400000) + 1;\n const actualDays = Math.ceil((effectiveEnd.getTime() - effectiveStart.getTime()) / 86400000) + 1;\n const ratio = Math.min(1, Math.max(0, actualDays / totalDays));\n\n // Determine reason\n const isNewHire = hire > start;\n const isTermination = term !== null && term < end;\n \n let reason: ProrationResult['reason'] = 'full';\n if (isNewHire && isTermination) {\n reason = 'both';\n } else if (isNewHire) {\n reason = 'new_hire';\n } else if (isTermination) {\n reason = 'termination';\n }\n\n return { ratio, reason, isProrated: ratio < 1 };\n}\n\n/**\n * Internal simple tax calculation\n * For multi-jurisdiction tax, use the jurisdiction system\n * @internal\n */\nfunction calculateSimpleTax(\n monthlyIncome: number,\n brackets: Array<{ min: number; max: number; rate: number }> = DEFAULT_TAX_BRACKETS\n): TaxResult {\n const annualIncome = monthlyIncome * 12;\n let annualTax = 0;\n\n for (const bracket of brackets) {\n if (annualIncome <= bracket.min) continue;\n const taxableInBracket = Math.min(annualIncome, bracket.max) - bracket.min;\n annualTax += taxableInBracket * bracket.rate;\n }\n\n const monthlyTax = Math.round(annualTax / 12);\n const effectiveRate = monthlyIncome > 0 ? monthlyTax / monthlyIncome : 0;\n\n return { amount: monthlyTax, effectiveRate };\n}\n\n/**\n * Calculate attendance deduction\n *\n * @example\n * const deduction = calculateAttendanceDeduction(22, 20, dailyRate);\n */\nexport function calculateAttendanceDeduction(\n expectedDays: number,\n actualDays: number,\n dailyRate: number,\n maxDeductionPercent = 100\n): number {\n const absentDays = Math.max(0, expectedDays - actualDays);\n const deduction = Math.round(absentDays * dailyRate);\n const maxDeduction = Math.round((dailyRate * expectedDays * maxDeductionPercent) / 100);\n return Math.min(deduction, maxDeduction);\n}\n\n/**\n * Calculate complete salary breakdown\n *\n * This is the main function for salary calculation.\n * Pass all data from YOUR app, get back complete breakdown.\n *\n * Note: Uses simple tax calculation. For multi-jurisdiction tax,\n * use the jurisdiction system instead.\n *\n * @example\n * const result = calculateSalaryBreakdown({\n * baseSalary: 100000,\n * hireDate: employee.hireDate,\n * terminationDate: employee.terminationDate,\n * periodStart: new Date('2024-03-01'),\n * periodEnd: new Date('2024-03-31'),\n * allowances: [{ type: 'housing', amount: 20000, taxable: true }],\n * deductions: [{ type: 'provident_fund', amount: 5000 }],\n * options: { holidays: companyHolidays },\n * attendance: { expectedDays: 22, actualDays: 20 },\n * });\n */\nexport function calculateSalaryBreakdown(params: {\n baseSalary: number;\n hireDate: Date;\n terminationDate?: Date | null;\n periodStart: Date;\n periodEnd: Date;\n allowances?: Array<{ type: string; amount: number; taxable?: boolean }>;\n deductions?: Array<{ type: string; amount: number }>;\n options?: PayrollProcessingOptions;\n attendance?: AttendanceInput;\n}): SalaryCalculationResult {\n const {\n baseSalary,\n hireDate,\n terminationDate,\n periodStart,\n periodEnd,\n allowances = [],\n deductions = [],\n options = {},\n attendance,\n } = params;\n\n // 1. Calculate working days\n const workSchedule = { ...DEFAULT_WORK_SCHEDULE, ...options.workSchedule };\n const workingDays = countWorkingDays(periodStart, periodEnd, {\n workingDays: workSchedule.workingDays,\n holidays: options.holidays,\n });\n\n // 2. Calculate proration\n const proration = options.skipProration\n ? { ratio: 1, reason: 'full' as const, isProrated: false }\n : calculateProration(hireDate, terminationDate, periodStart, periodEnd);\n\n // 3. Prorate base salary\n const proratedBase = Math.round(baseSalary * proration.ratio);\n\n // 4. Process allowances (prorate)\n const processedAllowances = allowances.map(a => ({\n type: a.type,\n amount: Math.round(a.amount * proration.ratio),\n taxable: a.taxable ?? true,\n }));\n const totalAllowances = processedAllowances.reduce((sum, a) => sum + a.amount, 0);\n\n // 5. Process deductions (prorate)\n const processedDeductions = deductions.map(d => ({\n type: d.type,\n amount: Math.round(d.amount * proration.ratio),\n }));\n\n // 6. Attendance deduction\n let attendanceDeduction = 0;\n if (attendance && !options.skipAttendance && workingDays.workingDays > 0) {\n // Use expectedDays from attendance if provided, otherwise use working days from schedule\n const expectedDays = attendance.expectedDays ?? workingDays.workingDays;\n const dailyRate = proratedBase / expectedDays;\n attendanceDeduction = calculateAttendanceDeduction(\n expectedDays,\n attendance.actualDays,\n dailyRate\n );\n if (attendanceDeduction > 0) {\n processedDeductions.push({ type: 'attendance', amount: attendanceDeduction });\n }\n }\n\n // 7. Calculate gross salary\n const grossSalary = proratedBase + totalAllowances;\n\n // 8. Calculate tax (simple calculation - for multi-jurisdiction, use jurisdiction system)\n let taxAmount = 0;\n if (!options.skipTax) {\n const taxableAllowances = processedAllowances\n .filter(a => a.taxable)\n .reduce((sum, a) => sum + a.amount, 0);\n const taxableIncome = proratedBase + taxableAllowances;\n const taxResult = calculateSimpleTax(taxableIncome);\n taxAmount = taxResult.amount;\n if (taxAmount > 0) {\n processedDeductions.push({ type: 'tax', amount: taxAmount });\n }\n }\n\n // 9. Calculate net salary\n const totalDeductions = processedDeductions\n .filter(d => d.type !== 'tax') // Exclude only tax, include attendance\n .reduce((sum, d) => sum + d.amount, 0);\n const netSalary = grossSalary - totalDeductions - taxAmount;\n\n return {\n baseSalary,\n proratedBase,\n totalAllowances,\n totalDeductions,\n attendanceDeduction,\n grossSalary,\n taxAmount,\n netSalary,\n proration,\n workingDays,\n breakdown: {\n allowances: processedAllowances,\n deductions: processedDeductions,\n },\n };\n}\n\n/**\n * Get pay period dates for a given month\n *\n * @example\n * const period = getPayPeriod(3, 2024); // March 2024\n */\nexport function getPayPeriod(\n month: number,\n year: number,\n payDay = 28\n): { startDate: Date; endDate: Date; payDate: Date } {\n const startDate = new Date(year, month - 1, 1);\n const endDate = new Date(year, month, 0); // Last day of month\n const payDate = new Date(year, month - 1, Math.min(payDay, endDate.getDate()));\n return { startDate, endDate, payDate };\n}\n\n","/**\n * @classytic/payroll - Pro-Rating Calculator\n *\n * Pure functions for salary pro-rating calculations.\n * No database dependencies - can be used client-side!\n *\n * Handles:\n * - Mid-period hires\n * - Mid-period terminations\n * - Working days (not calendar days)\n * - Holidays exclusion\n *\n * @packageDocumentation\n */\n\nimport { countWorkingDays } from '../core/config.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Input for pro-rating calculation\n */\nexport interface ProRatingInput {\n /**\n * Employee hire date\n */\n hireDate: Date;\n\n /**\n * Employee termination date (null if still employed)\n */\n terminationDate: Date | null;\n\n /**\n * Start of the salary period\n */\n periodStart: Date;\n\n /**\n * End of the salary period\n */\n periodEnd: Date;\n\n /**\n * Working days of the week (1=Monday, 7=Sunday)\n * @default [1, 2, 3, 4, 5] (Monday-Friday)\n */\n workingDays: number[];\n\n /**\n * Public holidays to exclude from working days\n * @default []\n */\n holidays?: Date[];\n}\n\n/**\n * Result of pro-rating calculation\n */\nexport interface ProRatingResult {\n /**\n * Whether the salary needs to be pro-rated\n */\n isProRated: boolean;\n\n /**\n * Pro-rating ratio (0-1)\n * 1 = full salary, 0.5 = half salary, etc.\n */\n ratio: number;\n\n /**\n * Total working days in the period\n */\n periodWorkingDays: number;\n\n /**\n * Working days the employee was actually employed\n */\n effectiveWorkingDays: number;\n\n /**\n * Effective start date (max of hire date and period start)\n */\n effectiveStart: Date;\n\n /**\n * Effective end date (min of termination date and period end)\n */\n effectiveEnd: Date;\n}\n\n// ============================================================================\n// Pure Functions\n// ============================================================================\n\n/**\n * Calculate pro-rating for mid-period hires/terminations\n *\n * This function uses WORKING DAYS (not calendar days) for accurate pro-rating.\n *\n * @example\n * ```typescript\n * // Employee hired on March 15th, process March salary\n * const result = calculateProRating({\n * hireDate: new Date('2024-03-15'),\n * terminationDate: null,\n * periodStart: new Date('2024-03-01'),\n * periodEnd: new Date('2024-03-31'),\n * workingDays: [1, 2, 3, 4, 5], // Mon-Fri\n * });\n * \n * console.log(result);\n * // {\n * // isProRated: true,\n * // ratio: 0.64, // Worked 14 out of 22 working days\n * // periodWorkingDays: 22,\n * // effectiveWorkingDays: 14\n * // }\n * ```\n *\n * @param input - Pro-rating calculation parameters\n * @returns Pro-rating result with ratio and working days breakdown\n *\n * @pure This function has no side effects and doesn't access external state\n */\nexport function calculateProRating(input: ProRatingInput): ProRatingResult {\n const { hireDate, terminationDate, periodStart, periodEnd, workingDays, holidays = [] } = input;\n\n const hire = new Date(hireDate);\n const termination = terminationDate ? new Date(terminationDate) : null;\n\n // Determine the actual start and end dates for this employee in this period\n const effectiveStart = hire > periodStart ? hire : periodStart;\n const effectiveEnd = termination && termination < periodEnd ? termination : periodEnd;\n\n // If employee wasn't active during this period at all\n if (effectiveStart > periodEnd || (termination && termination < periodStart)) {\n const periodWorkingDays = countWorkingDays(periodStart, periodEnd, { workingDays, holidays }).workingDays;\n return {\n isProRated: true,\n ratio: 0,\n periodWorkingDays,\n effectiveWorkingDays: 0,\n effectiveStart: periodStart,\n effectiveEnd: periodStart, // Effectively zero days\n };\n }\n\n // Calculate working days for the full period\n const periodWorkingDays = countWorkingDays(periodStart, periodEnd, { workingDays, holidays }).workingDays;\n\n // Calculate working days the employee was actually employed\n const effectiveWorkingDays = countWorkingDays(effectiveStart, effectiveEnd, { workingDays, holidays }).workingDays;\n\n // Calculate ratio\n const ratio = periodWorkingDays > 0 \n ? Math.min(1, Math.max(0, effectiveWorkingDays / periodWorkingDays)) \n : 0;\n\n // Is pro-rated if ratio is less than 1\n const isProRated = ratio < 1;\n\n return {\n isProRated,\n ratio,\n periodWorkingDays,\n effectiveWorkingDays,\n effectiveStart,\n effectiveEnd,\n };\n}\n\n/**\n * Calculate pro-rated amount from base amount and ratio\n *\n * @example\n * ```typescript\n * const proRatedSalary = applyProRating(100000, 0.64); // 64000\n * ```\n *\n * @param baseAmount - Original amount\n * @param ratio - Pro-rating ratio (0-1)\n * @returns Pro-rated amount (rounded)\n *\n * @pure No side effects\n */\nexport function applyProRating(baseAmount: number, ratio: number): number {\n return Math.round(baseAmount * ratio);\n}\n\n/**\n * Check if pro-rating should be applied for a given hire/termination scenario\n *\n * @param hireDate - Employee hire date\n * @param terminationDate - Employee termination date (null if active)\n * @param periodStart - Salary period start\n * @param periodEnd - Salary period end\n * @returns True if pro-rating is needed\n *\n * @pure No side effects\n */\nexport function shouldProRate(\n hireDate: Date,\n terminationDate: Date | null,\n periodStart: Date,\n periodEnd: Date\n): boolean {\n const hire = new Date(hireDate);\n const termination = terminationDate ? new Date(terminationDate) : null;\n\n // Pro-rate if hired after period start\n if (hire > periodStart) return true;\n\n // Pro-rate if terminated before period end\n if (termination && termination < periodEnd) return true;\n\n return false;\n}\n\n","/**\n * @classytic/payroll - Enums\n *\n * Type-safe enum definitions with const assertions\n * Single source of truth for all enum values\n */\n\nimport type {\n EmploymentType,\n EmployeeStatus,\n Department,\n PaymentFrequency,\n PaymentMethod,\n AllowanceType,\n DeductionType,\n PayrollStatus,\n TerminationReason,\n SalaryBand,\n} from './types.js';\n\n// ============================================================================\n// Employment Type\n// ============================================================================\n\nexport const EMPLOYMENT_TYPE = {\n FULL_TIME: 'full_time',\n PART_TIME: 'part_time',\n CONTRACT: 'contract',\n INTERN: 'intern',\n CONSULTANT: 'consultant',\n} as const satisfies Record<string, EmploymentType>;\n\nexport const EMPLOYMENT_TYPE_VALUES = Object.values(EMPLOYMENT_TYPE);\n\nexport function isValidEmploymentType(value: string): value is EmploymentType {\n return EMPLOYMENT_TYPE_VALUES.includes(value as EmploymentType);\n}\n\n// ============================================================================\n// Employee Status\n// ============================================================================\n\nexport const EMPLOYEE_STATUS = {\n ACTIVE: 'active',\n ON_LEAVE: 'on_leave',\n SUSPENDED: 'suspended',\n TERMINATED: 'terminated',\n} as const satisfies Record<string, EmployeeStatus>;\n\nexport const EMPLOYEE_STATUS_VALUES = Object.values(EMPLOYEE_STATUS);\n\nexport function isValidEmployeeStatus(value: string): value is EmployeeStatus {\n return EMPLOYEE_STATUS_VALUES.includes(value as EmployeeStatus);\n}\n\nexport function isActiveStatus(status: EmployeeStatus): boolean {\n return status === EMPLOYEE_STATUS.ACTIVE;\n}\n\nexport function isEmployedStatus(status: EmployeeStatus): boolean {\n return status !== EMPLOYEE_STATUS.TERMINATED;\n}\n\nexport function canReceiveSalaryStatus(status: EmployeeStatus): boolean {\n return status === EMPLOYEE_STATUS.ACTIVE || status === EMPLOYEE_STATUS.ON_LEAVE;\n}\n\n// ============================================================================\n// Department\n// ============================================================================\n\nexport const DEPARTMENT = {\n MANAGEMENT: 'management',\n TRAINING: 'training',\n SALES: 'sales',\n OPERATIONS: 'operations',\n SUPPORT: 'support',\n HR: 'hr',\n MAINTENANCE: 'maintenance',\n MARKETING: 'marketing',\n FINANCE: 'finance',\n IT: 'it',\n} as const satisfies Record<string, Department>;\n\nexport const DEPARTMENT_VALUES = Object.values(DEPARTMENT);\n\nexport function isValidDepartment(value: string): value is Department {\n return DEPARTMENT_VALUES.includes(value as Department);\n}\n\n// ============================================================================\n// Payment Frequency\n// ============================================================================\n\nexport const PAYMENT_FREQUENCY = {\n MONTHLY: 'monthly',\n BI_WEEKLY: 'bi_weekly',\n WEEKLY: 'weekly',\n HOURLY: 'hourly',\n DAILY: 'daily',\n} as const satisfies Record<string, PaymentFrequency>;\n\nexport const PAYMENT_FREQUENCY_VALUES = Object.values(PAYMENT_FREQUENCY);\n\nexport function isValidPaymentFrequency(value: string): value is PaymentFrequency {\n return PAYMENT_FREQUENCY_VALUES.includes(value as PaymentFrequency);\n}\n\n// ============================================================================\n// Payment Method\n// ============================================================================\n\nexport const PAYMENT_METHOD = {\n BANK: 'bank',\n CASH: 'cash',\n MOBILE: 'mobile',\n BKASH: 'bkash',\n NAGAD: 'nagad',\n ROCKET: 'rocket',\n CHECK: 'check',\n} as const satisfies Record<string, PaymentMethod>;\n\nexport const PAYMENT_METHOD_VALUES = Object.values(PAYMENT_METHOD);\n\nexport function isValidPaymentMethod(value: string): value is PaymentMethod {\n return PAYMENT_METHOD_VALUES.includes(value as PaymentMethod);\n}\n\n// ============================================================================\n// Allowance Type\n// ============================================================================\n\nexport const ALLOWANCE_TYPE = {\n HOUSING: 'housing',\n TRANSPORT: 'transport',\n MEAL: 'meal',\n MOBILE: 'mobile',\n MEDICAL: 'medical',\n EDUCATION: 'education',\n BONUS: 'bonus',\n OTHER: 'other',\n} as const satisfies Record<string, AllowanceType>;\n\nexport const ALLOWANCE_TYPE_VALUES = Object.values(ALLOWANCE_TYPE);\n\nexport function isValidAllowanceType(value: string): value is AllowanceType {\n return ALLOWANCE_TYPE_VALUES.includes(value as AllowanceType);\n}\n\n// ============================================================================\n// Deduction Type\n// ============================================================================\n\nexport const DEDUCTION_TYPE = {\n TAX: 'tax',\n LOAN: 'loan',\n ADVANCE: 'advance',\n PROVIDENT_FUND: 'provident_fund',\n INSURANCE: 'insurance',\n ABSENCE: 'absence',\n OTHER: 'other',\n} as const satisfies Record<string, DeductionType>;\n\nexport const DEDUCTION_TYPE_VALUES = Object.values(DEDUCTION_TYPE);\n\nexport function isValidDeductionType(value: string): value is DeductionType {\n return DEDUCTION_TYPE_VALUES.includes(value as DeductionType);\n}\n\n// ============================================================================\n// Payroll Status\n// ============================================================================\n\nexport const PAYROLL_STATUS = {\n PENDING: 'pending',\n PROCESSING: 'processing',\n PAID: 'paid',\n FAILED: 'failed',\n VOIDED: 'voided',\n REVERSED: 'reversed',\n} as const satisfies Record<string, PayrollStatus>;\n\nexport const PAYROLL_STATUS_VALUES = Object.values(PAYROLL_STATUS);\n\nexport function isValidPayrollStatus(value: string): value is PayrollStatus {\n return PAYROLL_STATUS_VALUES.includes(value as PayrollStatus);\n}\n\nexport function isCompletedPayrollStatus(status: PayrollStatus): boolean {\n return status === PAYROLL_STATUS.PAID;\n}\n\nexport function isPendingPayrollStatus(status: PayrollStatus): boolean {\n return status === PAYROLL_STATUS.PENDING || status === PAYROLL_STATUS.PROCESSING;\n}\n\n/** Check if payroll can be voided (not yet paid) */\nexport function isVoidablePayrollStatus(status: PayrollStatus): boolean {\n return status === PAYROLL_STATUS.PENDING ||\n status === PAYROLL_STATUS.PROCESSING ||\n status === PAYROLL_STATUS.FAILED;\n}\n\n/** Check if payroll requires reversal (already paid) */\nexport function requiresReversalPayrollStatus(status: PayrollStatus): boolean {\n return status === PAYROLL_STATUS.PAID;\n}\n\n/** Check if payroll is in a terminal void/reverse state */\nexport function isVoidedOrReversedStatus(status: PayrollStatus): boolean {\n return status === PAYROLL_STATUS.VOIDED || status === PAYROLL_STATUS.REVERSED;\n}\n\n// ============================================================================\n// Termination Reason\n// ============================================================================\n\nexport const TERMINATION_REASON = {\n RESIGNATION: 'resignation',\n RETIREMENT: 'retirement',\n TERMINATION: 'termination',\n CONTRACT_END: 'contract_end',\n MUTUAL_AGREEMENT: 'mutual_agreement',\n OTHER: 'other',\n} as const satisfies Record<string, TerminationReason>;\n\nexport const TERMINATION_REASON_VALUES = Object.values(TERMINATION_REASON);\n\nexport function isValidTerminationReason(value: string): value is TerminationReason {\n return TERMINATION_REASON_VALUES.includes(value as TerminationReason);\n}\n\n// ============================================================================\n// Salary Band\n// ============================================================================\n\nexport const SALARY_BAND = {\n INTERN: 'intern',\n JUNIOR: 'junior',\n MID: 'mid',\n SENIOR: 'senior',\n LEAD: 'lead',\n EXECUTIVE: 'executive',\n CUSTOM: 'custom',\n} as const satisfies Record<string, SalaryBand>;\n\nexport const SALARY_BAND_VALUES = Object.values(SALARY_BAND);\n\nexport function isValidSalaryBand(value: string): value is SalaryBand {\n return SALARY_BAND_VALUES.includes(value as SalaryBand);\n}\n\n// ============================================================================\n// Leave Type\n// ============================================================================\n\nimport type { LeaveType, LeaveRequestStatus } from './types.js';\n\nexport const LEAVE_TYPE = {\n ANNUAL: 'annual',\n SICK: 'sick',\n UNPAID: 'unpaid',\n MATERNITY: 'maternity',\n PATERNITY: 'paternity',\n BEREAVEMENT: 'bereavement',\n COMPENSATORY: 'compensatory',\n OTHER: 'other',\n} as const satisfies Record<string, LeaveType>;\n\nexport const LEAVE_TYPE_VALUES = Object.values(LEAVE_TYPE);\n\nexport function isValidLeaveType(value: string): value is LeaveType {\n return LEAVE_TYPE_VALUES.includes(value as LeaveType);\n}\n\nexport function isPaidLeaveType(type: LeaveType): boolean {\n return type !== LEAVE_TYPE.UNPAID;\n}\n\n// ============================================================================\n// Leave Request Status\n// ============================================================================\n\nexport const LEAVE_REQUEST_STATUS = {\n PENDING: 'pending',\n APPROVED: 'approved',\n REJECTED: 'rejected',\n CANCELLED: 'cancelled',\n} as const satisfies Record<string, LeaveRequestStatus>;\n\nexport const LEAVE_REQUEST_STATUS_VALUES = Object.values(LEAVE_REQUEST_STATUS);\n\nexport function isValidLeaveRequestStatus(value: string): value is LeaveRequestStatus {\n return LEAVE_REQUEST_STATUS_VALUES.includes(value as LeaveRequestStatus);\n}\n\nexport function isPendingLeaveStatus(status: LeaveRequestStatus): boolean {\n return status === LEAVE_REQUEST_STATUS.PENDING;\n}\n\nexport function isApprovedLeaveStatus(status: LeaveRequestStatus): boolean {\n return status === LEAVE_REQUEST_STATUS.APPROVED;\n}\n\n// ============================================================================\n// Tax Type\n// ============================================================================\n\nexport const TAX_TYPE = {\n INCOME_TAX: 'income_tax',\n SOCIAL_SECURITY: 'social_security',\n HEALTH_INSURANCE: 'health_insurance',\n PENSION: 'pension',\n EMPLOYMENT_INSURANCE: 'employment_insurance',\n LOCAL_TAX: 'local_tax',\n OTHER: 'other',\n} as const satisfies Record<string, import('./types.js').TaxType>;\n\nexport const TAX_TYPE_VALUES = Object.values(TAX_TYPE);\n\nexport function isValidTaxType(value: string): value is import('./types.js').TaxType {\n return TAX_TYPE_VALUES.includes(value as import('./types.js').TaxType);\n}\n\n// ============================================================================\n// Tax Status\n// ============================================================================\n\nexport const TAX_STATUS = {\n PENDING: 'pending',\n SUBMITTED: 'submitted',\n PAID: 'paid',\n CANCELLED: 'cancelled',\n} as const satisfies Record<string, import('./types.js').TaxStatus>;\n\nexport const TAX_STATUS_VALUES = Object.values(TAX_STATUS);\n\nexport function isValidTaxStatus(value: string): value is import('./types.js').TaxStatus {\n return TAX_STATUS_VALUES.includes(value as import('./types.js').TaxStatus);\n}\n\nexport function isPendingTaxStatus(status: import('./types.js').TaxStatus): boolean {\n return status === TAX_STATUS.PENDING;\n}\n\nexport function isPaidTaxStatus(status: import('./types.js').TaxStatus): boolean {\n return status === TAX_STATUS.PAID;\n}\n\nexport function isCancelledTaxStatus(status: import('./types.js').TaxStatus): boolean {\n return status === TAX_STATUS.CANCELLED;\n}\n\n// ============================================================================\n// Default Export\n// ============================================================================\n\nexport default {\n EMPLOYMENT_TYPE,\n EMPLOYMENT_TYPE_VALUES,\n EMPLOYEE_STATUS,\n EMPLOYEE_STATUS_VALUES,\n DEPARTMENT,\n DEPARTMENT_VALUES,\n PAYMENT_FREQUENCY,\n PAYMENT_FREQUENCY_VALUES,\n PAYMENT_METHOD,\n PAYMENT_METHOD_VALUES,\n ALLOWANCE_TYPE,\n ALLOWANCE_TYPE_VALUES,\n DEDUCTION_TYPE,\n DEDUCTION_TYPE_VALUES,\n PAYROLL_STATUS,\n PAYROLL_STATUS_VALUES,\n TERMINATION_REASON,\n TERMINATION_REASON_VALUES,\n SALARY_BAND,\n SALARY_BAND_VALUES,\n LEAVE_TYPE,\n LEAVE_TYPE_VALUES,\n LEAVE_REQUEST_STATUS,\n LEAVE_REQUEST_STATUS_VALUES,\n TAX_TYPE,\n TAX_TYPE_VALUES,\n TAX_STATUS,\n TAX_STATUS_VALUES,\n};\n","/**\n * @classytic/payroll - Validation Utilities\n *\n * Fluent, composable, type-safe validation\n * Clear semantics and helpful error messages\n */\n\nimport type {\n EmployeeStatus,\n EmploymentType,\n Compensation,\n BankDetails,\n EmployeeValidationResult,\n} from '../types.js';\nimport {\n EMPLOYEE_STATUS_VALUES,\n EMPLOYMENT_TYPE_VALUES,\n} from '../enums.js';\nimport { diffInMonths } from './date.js';\n\n// ============================================================================\n// Employee Status Validators\n// ============================================================================\n\n/**\n * Check if employee is active\n */\nexport function isActive(employee: { status?: EmployeeStatus }): boolean {\n return employee?.status === 'active';\n}\n\n/**\n * Check if employee is on leave\n */\nexport function isOnLeave(employee: { status?: EmployeeStatus }): boolean {\n return employee?.status === 'on_leave';\n}\n\n/**\n * Check if employee is suspended\n */\nexport function isSuspended(employee: { status?: EmployeeStatus }): boolean {\n return employee?.status === 'suspended';\n}\n\n/**\n * Check if employee is terminated\n */\nexport function isTerminated(employee: { status?: EmployeeStatus }): boolean {\n return employee?.status === 'terminated';\n}\n\n/**\n * Check if employee is employed (not terminated)\n */\nexport function isEmployed(employee: { status?: EmployeeStatus }): boolean {\n return (\n isActive(employee) || isOnLeave(employee) || isSuspended(employee)\n );\n}\n\n/**\n * Check if employee can receive salary\n */\nexport function canReceiveSalary(employee: {\n status?: EmployeeStatus;\n compensation?: { baseAmount?: number };\n}): boolean {\n return (\n (isActive(employee) || isOnLeave(employee)) &&\n (employee.compensation?.baseAmount ?? 0) > 0\n );\n}\n\n/**\n * Check if employment can be updated\n */\nexport function canUpdateEmployment(employee: { status?: EmployeeStatus }): boolean {\n return !isTerminated(employee);\n}\n\n// ============================================================================\n// Compensation Validators\n// ============================================================================\n\n/**\n * Check if employee has valid compensation\n */\nexport function hasCompensation(employee: {\n compensation?: { baseAmount?: number };\n}): boolean {\n return (employee.compensation?.baseAmount ?? 0) > 0;\n}\n\n/**\n * Check if compensation is valid\n */\nexport function isValidCompensation(compensation?: Compensation): boolean {\n return !!(\n compensation?.baseAmount &&\n compensation.baseAmount > 0 &&\n compensation.frequency &&\n compensation.currency\n );\n}\n\n/**\n * Check if bank details are valid\n */\nexport function isValidBankDetails(bankDetails?: BankDetails): boolean {\n return !!(\n bankDetails?.accountNumber &&\n bankDetails.bankName &&\n bankDetails.accountName\n );\n}\n\n// ============================================================================\n// Probation Validators\n// ============================================================================\n\n/**\n * Check if employee is in probation\n */\nexport function isInProbation(\n employee: { probationEndDate?: Date | null },\n now = new Date()\n): boolean {\n if (!employee?.probationEndDate) return false;\n return new Date(employee.probationEndDate) > now;\n}\n\n/**\n * Check if employee has completed probation\n */\nexport function hasCompletedProbation(\n employee: { probationEndDate?: Date | null },\n now = new Date()\n): boolean {\n if (!employee?.probationEndDate) return true;\n return new Date(employee.probationEndDate) <= now;\n}\n\n// ============================================================================\n// Eligibility Validators\n// ============================================================================\n\n/**\n * Check if employee is eligible for bonus\n */\nexport function isEligibleForBonus(\n employee: { status?: EmployeeStatus; hireDate?: Date },\n requiredMonths = 6\n): boolean {\n if (!isActive(employee) || !employee.hireDate) return false;\n const monthsEmployed = diffInMonths(employee.hireDate, new Date());\n return monthsEmployed >= requiredMonths;\n}\n\n/**\n * Check if employee is eligible for payroll\n */\nexport function isEligibleForPayroll(employee: {\n status?: EmployeeStatus;\n compensation?: { baseAmount?: number };\n bankDetails?: BankDetails;\n}): { eligible: boolean; reasons: string[] } {\n const reasons: string[] = [];\n\n if (!isActive(employee) && !isOnLeave(employee)) {\n reasons.push('Employee is not in active or on-leave status');\n }\n\n if (!hasCompensation(employee)) {\n reasons.push('Employee has no valid compensation');\n }\n\n return {\n eligible: reasons.length === 0,\n reasons,\n };\n}\n\n// ============================================================================\n// Field Validators\n// ============================================================================\n\n/**\n * Check if value is required\n */\nexport function required(fieldName: string): (value: unknown) => string | true {\n return (value: unknown) =>\n value !== undefined && value !== null && value !== ''\n ? true\n : `${fieldName} is required`;\n}\n\n/**\n * Check minimum value\n */\nexport function min(\n minValue: number,\n fieldName: string\n): (value: number) => string | true {\n return (value: number) =>\n value >= minValue ? true : `${fieldName} must be at least ${minValue}`;\n}\n\n/**\n * Check maximum value\n */\nexport function max(\n maxValue: number,\n fieldName: string\n): (value: number) => string | true {\n return (value: number) =>\n value <= maxValue ? true : `${fieldName} must not exceed ${maxValue}`;\n}\n\n/**\n * Check value is in range\n */\nexport function inRange(\n minValue: number,\n maxValue: number,\n fieldName: string\n): (value: number) => string | true {\n return (value: number) =>\n value >= minValue && value <= maxValue\n ? true\n : `${fieldName} must be between ${minValue} and ${maxValue}`;\n}\n\n/**\n * Check value is positive\n */\nexport function isPositive(fieldName: string): (value: number) => string | true {\n return (value: number) =>\n value > 0 ? true : `${fieldName} must be positive`;\n}\n\n/**\n * Check value is one of allowed values\n */\nexport function oneOf<T extends string>(\n allowedValues: readonly T[],\n fieldName: string\n): (value: T) => string | true {\n return (value: T) =>\n allowedValues.includes(value)\n ? true\n : `${fieldName} must be one of: ${allowedValues.join(', ')}`;\n}\n\n// ============================================================================\n// Enum Validators\n// ============================================================================\n\n/**\n * Check if status is valid\n */\nexport function isValidStatus(value: string): value is EmployeeStatus {\n return EMPLOYEE_STATUS_VALUES.includes(value as EmployeeStatus);\n}\n\n/**\n * Check if employment type is valid\n */\nexport function isValidEmploymentType(value: string): value is EmploymentType {\n return EMPLOYMENT_TYPE_VALUES.includes(value as EmploymentType);\n}\n\n// ============================================================================\n// Composite Validators\n// ============================================================================\n\n/**\n * Compose multiple validators\n */\nexport function composeValidators<T>(\n ...validators: Array<(value: T, data?: unknown) => string | true>\n): (value: T, data?: unknown) => string | true {\n return (value: T, data?: unknown) => {\n for (const validator of validators) {\n const result = validator(value, data);\n if (result !== true) return result;\n }\n return true;\n };\n}\n\n/**\n * Create a validator from validation functions\n */\nexport function createValidator<T extends Record<string, unknown>>(\n validationFns: Record<string, (value: unknown, data: T) => string | true>\n): (data: T) => EmployeeValidationResult {\n return (data: T) => {\n const errors: string[] = [];\n\n for (const [field, validator] of Object.entries(validationFns)) {\n const result = validator((data as Record<string, unknown>)[field], data);\n if (result !== true) {\n errors.push(result);\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n };\n}\n\n/**\n * Validate required fields exist\n */\nexport function hasRequiredFields(\n obj: Record<string, unknown>,\n fields: string[]\n): { valid: boolean; missing: string[] } {\n const missing = fields.filter(\n (field) => obj[field] === undefined || obj[field] === null\n );\n return {\n valid: missing.length === 0,\n missing,\n };\n}\n\n// ============================================================================\n// Plugin Method Validators\n// ============================================================================\n\n/**\n * Check if an object has a specific method (typically from a Mongoose plugin)\n *\n * @param obj - Object to check\n * @param method - Method name to verify\n * @returns true if object has the method as a function\n *\n * @example\n * ```typescript\n * if (hasPluginMethod(employee, 'canReceiveSalary')) {\n * employee.canReceiveSalary();\n * }\n * ```\n */\nexport function hasPluginMethod(obj: unknown, method: string): boolean {\n return typeof obj === 'object' && obj !== null && typeof (obj as Record<string, unknown>)[method] === 'function';\n}\n\n/**\n * Assert that an object has a specific plugin method, throwing an error if not\n *\n * @param obj - Object to check\n * @param method - Method name to verify\n * @param context - Context message for error\n * @throws Error if method is not found\n */\nexport function assertPluginMethod(obj: unknown, method: string, context: string): void {\n if (!hasPluginMethod(obj, method)) {\n throw new Error(\n `Method '${method}' not found on object. Did you forget to apply the appropriate plugin? ` +\n `Context: ${context}`\n );\n }\n}\n\n// ============================================================================\n// Aliases for backwards compatibility\n// ============================================================================\n\nexport const minValue = min;\nexport const maxValue = max;\nexport const isInRange = inRange;\n\n// ============================================================================\n// Default Export\n// ============================================================================\n\nexport default {\n // Status validators\n isActive,\n isOnLeave,\n isSuspended,\n isTerminated,\n isEmployed,\n canReceiveSalary,\n canUpdateEmployment,\n // Compensation validators\n hasCompensation,\n isValidCompensation,\n isValidBankDetails,\n // Probation validators\n isInProbation,\n hasCompletedProbation,\n // Eligibility validators\n isEligibleForBonus,\n isEligibleForPayroll,\n // Field validators\n required,\n min,\n max,\n inRange,\n isPositive,\n oneOf,\n // Enum validators\n isValidStatus,\n isValidEmploymentType,\n // Composite validators\n composeValidators,\n createValidator,\n hasRequiredFields,\n // Plugin method validators\n hasPluginMethod,\n assertPluginMethod,\n};\n\n","/**\n * @classytic/payroll - Query Builders\n *\n * Fluent API for building MongoDB queries\n * Type-safe, chainable, beautiful\n */\n\nimport mongoose, { Types } from 'mongoose';\nimport type {\n ObjectIdLike,\n EmployeeStatus,\n PayrollStatus,\n Department,\n EmploymentType,\n} from '../types.js';\n\n// ============================================================================\n// ObjectId Helpers\n// ============================================================================\n\n/**\n * Convert string or ObjectId to ObjectId\n */\nexport function toObjectId(id: ObjectIdLike): Types.ObjectId {\n if (id instanceof Types.ObjectId) return id;\n return new Types.ObjectId(id);\n}\n\n/**\n * Safely convert to ObjectId (returns null if invalid)\n */\nexport function safeToObjectId(id: unknown): Types.ObjectId | null {\n if (id instanceof Types.ObjectId) return id;\n if (typeof id === 'string' && Types.ObjectId.isValid(id)) {\n return new Types.ObjectId(id);\n }\n return null;\n}\n\n/**\n * Check if value is valid ObjectId\n */\nexport function isValidObjectId(value: unknown): boolean {\n if (value instanceof Types.ObjectId) return true;\n if (typeof value === 'string') return Types.ObjectId.isValid(value);\n return false;\n}\n\n// ============================================================================\n// Base Query Builder\n// ============================================================================\n\nexport class QueryBuilder<T extends Record<string, unknown> = Record<string, unknown>> {\n protected query: T;\n\n constructor(initialQuery: T = {} as T) {\n this.query = { ...initialQuery };\n }\n\n /**\n * Add where condition\n */\n where<K extends string>(field: K, value: unknown): this {\n (this.query as Record<string, unknown>)[field] = value;\n return this;\n }\n\n /**\n * Add $in condition\n */\n whereIn<K extends string>(field: K, values: unknown[]): this {\n (this.query as Record<string, unknown>)[field] = { $in: values };\n return this;\n }\n\n /**\n * Add $nin condition\n */\n whereNotIn<K extends string>(field: K, values: unknown[]): this {\n (this.query as Record<string, unknown>)[field] = { $nin: values };\n return this;\n }\n\n /**\n * Add $gte condition\n */\n whereGte<K extends string>(field: K, value: unknown): this {\n const existing = (this.query as Record<string, Record<string, unknown>>)[field] || {};\n (this.query as Record<string, unknown>)[field] = { ...existing, $gte: value };\n return this;\n }\n\n /**\n * Add $lte condition\n */\n whereLte<K extends string>(field: K, value: unknown): this {\n const existing = (this.query as Record<string, Record<string, unknown>>)[field] || {};\n (this.query as Record<string, unknown>)[field] = { ...existing, $lte: value };\n return this;\n }\n\n /**\n * Add $gt condition\n */\n whereGt<K extends string>(field: K, value: unknown): this {\n const existing = (this.query as Record<string, Record<string, unknown>>)[field] || {};\n (this.query as Record<string, unknown>)[field] = { ...existing, $gt: value };\n return this;\n }\n\n /**\n * Add $lt condition\n */\n whereLt<K extends string>(field: K, value: unknown): this {\n const existing = (this.query as Record<string, Record<string, unknown>>)[field] || {};\n (this.query as Record<string, unknown>)[field] = { ...existing, $lt: value };\n return this;\n }\n\n /**\n * Add between condition\n */\n whereBetween<K extends string>(field: K, start: unknown, end: unknown): this {\n (this.query as Record<string, unknown>)[field] = { $gte: start, $lte: end };\n return this;\n }\n\n /**\n * Add $exists condition\n */\n whereExists<K extends string>(field: K): this {\n (this.query as Record<string, unknown>)[field] = { $exists: true };\n return this;\n }\n\n /**\n * Add $exists: false condition\n */\n whereNotExists<K extends string>(field: K): this {\n (this.query as Record<string, unknown>)[field] = { $exists: false };\n return this;\n }\n\n /**\n * Add $ne condition\n */\n whereNot<K extends string>(field: K, value: unknown): this {\n (this.query as Record<string, unknown>)[field] = { $ne: value };\n return this;\n }\n\n /**\n * Add regex condition\n */\n whereRegex<K extends string>(field: K, pattern: string, flags = 'i'): this {\n (this.query as Record<string, unknown>)[field] = { $regex: pattern, $options: flags };\n return this;\n }\n\n /**\n * Merge another query\n */\n merge(otherQuery: Record<string, unknown>): this {\n this.query = { ...this.query, ...otherQuery } as T;\n return this;\n }\n\n /**\n * Build and return the query\n */\n build(): T {\n return { ...this.query };\n }\n}\n\n// ============================================================================\n// Employee Query Builder\n// ============================================================================\n\nexport class EmployeeQueryBuilder extends QueryBuilder {\n /**\n * Filter by organization\n */\n forOrganization(organizationId: ObjectIdLike): this {\n return this.where('organizationId', toObjectId(organizationId));\n }\n\n /**\n * Filter by user\n */\n forUser(userId: ObjectIdLike): this {\n return this.where('userId', toObjectId(userId));\n }\n\n /**\n * Filter by employeeId (human-readable ID)\n */\n forEmployeeId(employeeId: string): this {\n return this.where('employeeId', employeeId);\n }\n\n /**\n * Filter by email (for guest employees)\n */\n forEmail(email: string): this {\n return this.where('email', email.toLowerCase().trim());\n }\n\n /**\n * Filter guest employees (no userId)\n */\n guestEmployees(): this {\n return this.where('userId', null);\n }\n\n /**\n * Filter user-linked employees (has userId)\n * Uses $exists: true and $ne: null to exclude both missing fields and null values\n */\n userLinkedEmployees(): this {\n return this.where('userId', { $exists: true, $ne: null } as unknown as mongoose.Types.ObjectId);\n }\n\n /**\n * Filter by status(es)\n */\n withStatus(...statuses: EmployeeStatus[]): this {\n if (statuses.length === 1) {\n return this.where('status', statuses[0]);\n }\n return this.whereIn('status', statuses);\n }\n\n /**\n * Filter active employees\n */\n active(): this {\n return this.withStatus('active');\n }\n\n /**\n * Filter employed employees (not terminated)\n */\n employed(): this {\n return this.whereIn('status', ['active', 'on_leave', 'suspended']);\n }\n\n /**\n * Filter terminated employees\n */\n terminated(): this {\n return this.withStatus('terminated');\n }\n\n /**\n * Filter by department\n */\n inDepartment(department: Department | string): this {\n return this.where('department', department);\n }\n\n /**\n * Filter by position\n */\n inPosition(position: string): this {\n return this.where('position', position);\n }\n\n /**\n * Filter by employment type\n */\n withEmploymentType(type: EmploymentType | string): this {\n return this.where('employmentType', type);\n }\n\n /**\n * Filter by hire date (after)\n */\n hiredAfter(date: Date): this {\n return this.whereGte('hireDate', date);\n }\n\n /**\n * Filter by hire date (before)\n */\n hiredBefore(date: Date): this {\n return this.whereLte('hireDate', date);\n }\n\n /**\n * Filter by minimum salary\n */\n withMinSalary(amount: number): this {\n return this.whereGte('compensation.netSalary', amount);\n }\n\n /**\n * Filter by maximum salary\n */\n withMaxSalary(amount: number): this {\n return this.whereLte('compensation.netSalary', amount);\n }\n\n /**\n * Filter by salary range\n */\n withSalaryRange(min: number, max: number): this {\n return this.whereBetween('compensation.netSalary', min, max);\n }\n}\n\n// ============================================================================\n// Payroll Query Builder\n// ============================================================================\n\nexport class PayrollQueryBuilder extends QueryBuilder {\n /**\n * Filter by organization\n */\n forOrganization(organizationId: ObjectIdLike): this {\n return this.where('organizationId', toObjectId(organizationId));\n }\n\n /**\n * Filter by employee\n *\n * Note: PayrollRecord.employeeId is always ObjectId _id\n * If passing a string business ID, resolve to _id first\n */\n forEmployee(employeeId: ObjectIdLike | string): this {\n // For payroll queries, employeeId field is always the ObjectId _id\n // If a string business ID is passed, caller should resolve to _id first\n return this.where('employeeId', toObjectId(employeeId as ObjectIdLike));\n }\n\n /**\n * Filter by period\n */\n forPeriod(month?: number, year?: number): this {\n if (month !== undefined) {\n this.where('period.month', month);\n }\n if (year !== undefined) {\n this.where('period.year', year);\n }\n return this;\n }\n\n /**\n * Filter by status(es)\n */\n withStatus(...statuses: PayrollStatus[]): this {\n if (statuses.length === 1) {\n return this.where('status', statuses[0]);\n }\n return this.whereIn('status', statuses);\n }\n\n /**\n * Filter paid records\n */\n paid(): this {\n return this.withStatus('paid');\n }\n\n /**\n * Filter pending records\n */\n pending(): this {\n return this.whereIn('status', ['pending', 'processing']);\n }\n\n /**\n * Filter by date range\n */\n inDateRange(start: Date, end: Date): this {\n return this.whereBetween('period.payDate', start, end);\n }\n\n /**\n * Filter exported records\n */\n exported(): this {\n return this.where('exported', true);\n }\n\n /**\n * Filter not exported records\n */\n notExported(): this {\n return this.where('exported', false);\n }\n}\n\n// ============================================================================\n// Factory Functions\n// ============================================================================\n\n/**\n * Create employee query builder\n */\nexport function employee(): EmployeeQueryBuilder {\n return new EmployeeQueryBuilder();\n}\n\n/**\n * Create payroll query builder\n */\nexport function payroll(): PayrollQueryBuilder {\n return new PayrollQueryBuilder();\n}\n\n/**\n * Create generic query builder\n */\nexport function createQueryBuilder<T extends Record<string, unknown> = Record<string, unknown>>(\n initialQuery?: T\n): QueryBuilder<T> {\n return new QueryBuilder(initialQuery);\n}\n\n// ============================================================================\n// Convenience Query Builders\n// ============================================================================\n\n/**\n * Build employee query from options\n */\nexport function buildEmployeeQuery(options: {\n organizationId: ObjectIdLike;\n userId?: ObjectIdLike;\n statuses?: EmployeeStatus[];\n department?: Department | string;\n employmentType?: EmploymentType | string;\n}): Record<string, unknown> {\n const builder = employee().forOrganization(options.organizationId);\n\n if (options.userId) {\n builder.forUser(options.userId);\n }\n if (options.statuses) {\n builder.withStatus(...options.statuses);\n }\n if (options.department) {\n builder.inDepartment(options.department);\n }\n if (options.employmentType) {\n builder.withEmploymentType(options.employmentType);\n }\n\n return builder.build();\n}\n\n/**\n * Build payroll query from options\n */\nexport function buildPayrollQuery(options: {\n employeeId?: ObjectIdLike;\n organizationId?: ObjectIdLike;\n month?: number;\n year?: number;\n statuses?: PayrollStatus[];\n}): Record<string, unknown> {\n const builder = payroll();\n\n if (options.organizationId) {\n builder.forOrganization(options.organizationId);\n }\n if (options.employeeId) {\n builder.forEmployee(options.employeeId);\n }\n if (options.month || options.year) {\n builder.forPeriod(options.month, options.year);\n }\n if (options.statuses) {\n builder.withStatus(...options.statuses);\n }\n\n return builder.build();\n}\n\n// ============================================================================\n// Aggregation Pipeline Helpers\n// ============================================================================\n\n/**\n * Build aggregation pipeline from stages\n */\nexport function buildAggregationPipeline(\n ...stages: Array<Record<string, unknown> | undefined | null>\n): Record<string, unknown>[] {\n return stages.filter((stage): stage is Record<string, unknown> => !!stage);\n}\n\n/**\n * Match stage\n */\nexport function matchStage(query: Record<string, unknown>): Record<string, unknown> {\n return { $match: query };\n}\n\n/**\n * Group stage\n */\nexport function groupStage(\n groupBy: string | null,\n aggregations: Record<string, unknown>\n): Record<string, unknown> {\n return {\n $group: {\n _id: groupBy,\n ...aggregations,\n },\n };\n}\n\n/**\n * Sort stage\n */\nexport function sortStage(sortBy: Record<string, 1 | -1>): Record<string, unknown> {\n return { $sort: sortBy };\n}\n\n/**\n * Limit stage\n */\nexport function limitStage(limit: number): Record<string, unknown> {\n return { $limit: limit };\n}\n\n/**\n * Skip stage\n */\nexport function skipStage(skip: number): Record<string, unknown> {\n return { $skip: skip };\n}\n\n/**\n * Project stage\n */\nexport function projectStage(fields: Record<string, unknown>): Record<string, unknown> {\n return { $project: fields };\n}\n\n/**\n * Lookup stage\n */\nexport function lookupStage(options: {\n from: string;\n localField: string;\n foreignField: string;\n as: string;\n}): Record<string, unknown> {\n return { $lookup: options };\n}\n\n/**\n * Unwind stage\n */\nexport function unwindStage(\n path: string,\n options: { preserveNullAndEmptyArrays?: boolean } = {}\n): Record<string, unknown> {\n return { $unwind: { path, ...options } };\n}\n\n// ============================================================================\n// Default Export\n// ============================================================================\n\nexport default {\n toObjectId,\n safeToObjectId,\n isValidObjectId,\n QueryBuilder,\n EmployeeQueryBuilder,\n PayrollQueryBuilder,\n employee,\n payroll,\n createQueryBuilder,\n buildEmployeeQuery,\n buildPayrollQuery,\n buildAggregationPipeline,\n matchStage,\n groupStage,\n sortStage,\n limitStage,\n skipStage,\n projectStage,\n lookupStage,\n unwindStage,\n};\n\n","/**\n * @classytic/payroll - Leave Utilities\n *\n * Pure, composable leave calculation functions\n */\n\nimport type {\n LeaveBalance,\n LeaveType,\n LeaveInitConfig,\n LeaveSummaryResult,\n WorkingDaysOptions,\n} from '../types.js';\n\n// ============================================================================\n// Default Configurations\n// ============================================================================\n\n/** Default leave allocations by type (days per year) */\nexport const DEFAULT_LEAVE_ALLOCATIONS: Record<LeaveType, number> = {\n annual: 20,\n sick: 10,\n unpaid: 0, // Unlimited\n maternity: 90,\n paternity: 10,\n bereavement: 5,\n compensatory: 0,\n other: 0,\n};\n\n/** Default carry-over limits by type (days) */\nexport const DEFAULT_CARRY_OVER: Record<LeaveType, number> = {\n annual: 5,\n sick: 0,\n unpaid: 0,\n maternity: 0,\n paternity: 0,\n bereavement: 0,\n compensatory: 5,\n other: 0,\n};\n\n// ============================================================================\n// Working Days Calculation\n// ============================================================================\n\n/**\n * Calculate working days between two dates\n * Excludes weekends and optionally holidays\n *\n * @example\n * // March 2024: Get working days for a week\n * const days = calculateLeaveDays(\n * new Date('2024-03-01'),\n * new Date('2024-03-08'),\n * { holidays: [new Date('2024-03-05')] }\n * );\n */\nexport function calculateLeaveDays(\n startDate: Date,\n endDate: Date,\n options: WorkingDaysOptions = {}\n): number {\n const {\n workingDays = [1, 2, 3, 4, 5], // Mon-Fri by default\n holidays = [],\n includeEndDate = true,\n } = options;\n\n const holidaySet = new Set(holidays.map((d) => new Date(d).toDateString()));\n\n let count = 0;\n const current = new Date(startDate);\n current.setHours(0, 0, 0, 0);\n\n const end = new Date(endDate);\n end.setHours(0, 0, 0, 0);\n\n if (!includeEndDate) {\n end.setDate(end.getDate() - 1);\n }\n\n while (current <= end) {\n const isWorkDay = workingDays.includes(current.getDay());\n const isHoliday = holidaySet.has(current.toDateString());\n\n if (isWorkDay && !isHoliday) {\n count++;\n }\n\n current.setDate(current.getDate() + 1);\n }\n\n return count;\n}\n\n// ============================================================================\n// Balance Checks\n// ============================================================================\n\n/**\n * Check if employee has sufficient leave balance\n *\n * @example\n * if (hasLeaveBalance(employee, 'annual', 5)) {\n * // Can request 5 days annual leave\n * }\n */\nexport function hasLeaveBalance(\n employee: { leaveBalances?: LeaveBalance[] },\n type: LeaveType,\n days: number,\n year = new Date().getFullYear()\n): boolean {\n // Unpaid leave is always allowed\n if (type === 'unpaid') return true;\n\n const balance = getLeaveBalance(employee, type, year);\n if (!balance) return false;\n\n const available =\n balance.allocated + balance.carriedOver - balance.used - balance.pending;\n return available >= days;\n}\n\n/**\n * Get leave balance for a specific type\n */\nexport function getLeaveBalance(\n employee: { leaveBalances?: LeaveBalance[] },\n type: LeaveType,\n year = new Date().getFullYear()\n): LeaveBalance | undefined {\n return employee.leaveBalances?.find((b) => b.type === type && b.year === year);\n}\n\n/**\n * Get all leave balances for a year\n */\nexport function getLeaveBalances(\n employee: { leaveBalances?: LeaveBalance[] },\n year = new Date().getFullYear()\n): LeaveBalance[] {\n return (employee.leaveBalances || []).filter((b) => b.year === year);\n}\n\n/**\n * Calculate available days for a leave type\n */\nexport function getAvailableDays(\n employee: { leaveBalances?: LeaveBalance[] },\n type: LeaveType,\n year = new Date().getFullYear()\n): number {\n // Unpaid leave has no limit\n if (type === 'unpaid') return Infinity;\n\n const balance = getLeaveBalance(employee, type, year);\n if (!balance) return 0;\n\n return Math.max(\n 0,\n balance.allocated + balance.carriedOver - balance.used - balance.pending\n );\n}\n\n// ============================================================================\n// Leave Summary\n// ============================================================================\n\n/**\n * Get comprehensive leave summary for an employee\n *\n * @example\n * const summary = getLeaveSummary(employee, 2024);\n * console.log(`Available: ${summary.totalAvailable} days`);\n * console.log(`Annual: ${summary.byType.annual?.available || 0} days`);\n */\nexport function getLeaveSummary(\n employee: { leaveBalances?: LeaveBalance[] },\n year = new Date().getFullYear()\n): LeaveSummaryResult {\n const balances = getLeaveBalances(employee, year);\n\n const byType = {} as LeaveSummaryResult['byType'];\n let totalAllocated = 0;\n let totalUsed = 0;\n let totalPending = 0;\n\n for (const balance of balances) {\n const available = Math.max(\n 0,\n balance.allocated + balance.carriedOver - balance.used - balance.pending\n );\n byType[balance.type] = {\n allocated: balance.allocated + balance.carriedOver,\n used: balance.used,\n pending: balance.pending,\n available,\n };\n totalAllocated += balance.allocated + balance.carriedOver;\n totalUsed += balance.used;\n totalPending += balance.pending;\n }\n\n return {\n year,\n balances,\n totalAllocated,\n totalUsed,\n totalPending,\n totalAvailable: Math.max(0, totalAllocated - totalUsed - totalPending),\n byType,\n };\n}\n\n// ============================================================================\n// Balance Initialization\n// ============================================================================\n\n/**\n * Initialize leave balances for a new employee\n *\n * @example\n * // Full allocation for employee hired Jan 1st\n * const balances = initializeLeaveBalances(new Date('2024-01-01'));\n *\n * // Pro-rated for mid-year hire\n * const balances = initializeLeaveBalances(new Date('2024-07-01'), {\n * proRateNewHires: true,\n * });\n */\nexport function initializeLeaveBalances(\n hireDate: Date,\n config: LeaveInitConfig = {},\n year = new Date().getFullYear()\n): LeaveBalance[] {\n const {\n defaultAllocations = DEFAULT_LEAVE_ALLOCATIONS,\n proRateNewHires = true,\n fiscalYearStartMonth = 1,\n } = config;\n\n const fiscalYearStart = new Date(year, fiscalYearStartMonth - 1, 1);\n const fiscalYearEnd = new Date(year + 1, fiscalYearStartMonth - 1, 0);\n\n // Calculate proration if hired mid-year\n let prorationRatio = 1;\n if (proRateNewHires && hireDate > fiscalYearStart) {\n const totalDays = diffInDays(fiscalYearStart, fiscalYearEnd);\n const remainingDays = diffInDays(hireDate, fiscalYearEnd);\n prorationRatio = Math.max(0, Math.min(1, remainingDays / totalDays));\n }\n\n const balances: LeaveBalance[] = [];\n\n for (const [type, allocation] of Object.entries(defaultAllocations)) {\n if (allocation > 0) {\n balances.push({\n type: type as LeaveType,\n allocated: Math.round(allocation * prorationRatio),\n used: 0,\n pending: 0,\n carriedOver: 0,\n year,\n });\n }\n }\n\n return balances;\n}\n\n/**\n * Calculate prorated allocation for mid-year hire\n */\nexport function proRateAllocation(\n fullAllocation: number,\n hireDate: Date,\n fiscalYearStartMonth = 1,\n year = new Date().getFullYear()\n): number {\n const fiscalYearStart = new Date(year, fiscalYearStartMonth - 1, 1);\n\n if (hireDate <= fiscalYearStart) {\n return fullAllocation;\n }\n\n const fiscalYearEnd = new Date(year + 1, fiscalYearStartMonth - 1, 0);\n const totalDays = diffInDays(fiscalYearStart, fiscalYearEnd);\n const remainingDays = Math.max(0, diffInDays(hireDate, fiscalYearEnd));\n\n return Math.round((fullAllocation * remainingDays) / totalDays);\n}\n\n// ============================================================================\n// Payroll Integration\n// ============================================================================\n\n/**\n * Calculate unpaid leave deduction for payroll\n *\n * @example\n * const deduction = calculateUnpaidLeaveDeduction(100000, 5, 22);\n * // Daily rate: 100000 / 22 = 4545\n * // Deduction: 4545 * 5 = 22727\n */\nexport function calculateUnpaidLeaveDeduction(\n baseSalary: number,\n unpaidDays: number,\n workingDaysInMonth: number\n): number {\n if (unpaidDays <= 0 || workingDaysInMonth <= 0) return 0;\n\n const dailyRate = baseSalary / workingDaysInMonth;\n return Math.round(dailyRate * unpaidDays);\n}\n\n/**\n * Get total unpaid leave days from a list of leave requests\n */\nexport function getUnpaidLeaveDays(\n leaveRequests: Array<{ type: LeaveType; days: number; status: string }>,\n status = 'approved'\n): number {\n return leaveRequests\n .filter((r) => r.type === 'unpaid' && r.status === status)\n .reduce((sum, r) => sum + r.days, 0);\n}\n\n// ============================================================================\n// Year-End Processing\n// ============================================================================\n\n/**\n * Calculate carry-over balances for year-end\n *\n * Creates new year balances for ALL leave types from the current year.\n * Types with carry-over limits get their unused balance carried forward.\n * Types without carry-over (or 0 limit) start fresh with 0 carriedOver.\n *\n * @example\n * // With default allocations - creates balances for all types\n * const newBalances = calculateCarryOver(employee.leaveBalances, {\n * annual: 5,\n * compensatory: 3,\n * });\n * // Merge with existing (don't replace entirely)\n * employee.leaveBalances.push(...newBalances);\n *\n * // With custom allocations (org-specific entitlements)\n * const newBalances = calculateCarryOver(employee.leaveBalances, {\n * annual: 5,\n * compensatory: 3,\n * }, {\n * annual: 25, // Custom org policy\n * sick: 15,\n * });\n */\nexport function calculateCarryOver(\n balances: LeaveBalance[],\n maxCarryOver: Partial<Record<LeaveType, number>> = DEFAULT_CARRY_OVER,\n newYearAllocations: Partial<Record<LeaveType, number>> = DEFAULT_LEAVE_ALLOCATIONS\n): LeaveBalance[] {\n if (!balances.length) return [];\n\n const currentYear = balances[0].year;\n const newYear = currentYear + 1;\n\n // Process ALL leave types from current year, not just those with carry-over\n return balances.map((balance) => {\n const available =\n balance.allocated + balance.carriedOver - balance.used - balance.pending;\n const maxForType = maxCarryOver[balance.type] ?? 0;\n // Only carry over if max > 0, otherwise start fresh\n const carryOver = maxForType > 0\n ? Math.min(Math.max(0, available), maxForType)\n : 0;\n\n return {\n type: balance.type,\n allocated: newYearAllocations[balance.type] ?? DEFAULT_LEAVE_ALLOCATIONS[balance.type] ?? 0,\n used: 0,\n pending: 0,\n carriedOver: carryOver,\n year: newYear,\n };\n });\n}\n\n/**\n * Add accrued leave to balances\n */\nexport function accrueLeaveToBalance(\n balances: LeaveBalance[],\n type: LeaveType,\n amount: number,\n year = new Date().getFullYear()\n): LeaveBalance[] {\n const existingIdx = balances.findIndex((b) => b.type === type && b.year === year);\n\n if (existingIdx >= 0) {\n balances[existingIdx].allocated += amount;\n } else {\n balances.push({\n type,\n allocated: amount,\n used: 0,\n pending: 0,\n carriedOver: 0,\n year,\n });\n }\n\n return balances;\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Calculate difference in days between two dates\n */\nfunction diffInDays(start: Date, end: Date): number {\n const startDate = new Date(start);\n const endDate = new Date(end);\n startDate.setHours(0, 0, 0, 0);\n endDate.setHours(0, 0, 0, 0);\n return Math.ceil((endDate.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24));\n}\n\n// ============================================================================\n// Default Export\n// ============================================================================\n\nexport default {\n DEFAULT_LEAVE_ALLOCATIONS,\n DEFAULT_CARRY_OVER,\n calculateLeaveDays,\n hasLeaveBalance,\n getLeaveBalance,\n getLeaveBalances,\n getAvailableDays,\n getLeaveSummary,\n initializeLeaveBalances,\n proRateAllocation,\n calculateUnpaidLeaveDeduction,\n getUnpaidLeaveDays,\n calculateCarryOver,\n accrueLeaveToBalance,\n};\n","/**\n * @classytic/payroll - Error Handling\n *\n * Custom error classes with error codes and HTTP status\n */\n\nimport type { ErrorCode, HttpError } from '../types.js';\n\n// ============================================================================\n// Base Error Class\n// ============================================================================\n\nexport class PayrollError extends Error implements HttpError {\n readonly code: ErrorCode;\n readonly status: number;\n readonly context: Record<string, unknown>;\n readonly timestamp: Date;\n\n /**\n * Create a PayrollError.\n *\n * Supports BOTH constructor styles for backwards compatibility:\n * - new PayrollError(message, code?, status?, context?)\n * - new PayrollError(code, status, message, context?)\n */\n constructor(\n messageOrCode: string | ErrorCode,\n codeOrStatus: ErrorCode | number = 'PAYROLL_ERROR',\n statusOrMessage: number | string = 500,\n context: Record<string, unknown> = {}\n ) {\n const isLegacySignature = typeof messageOrCode === 'string' && typeof codeOrStatus === 'string';\n\n const message = isLegacySignature ? (messageOrCode as string) : (statusOrMessage as string);\n super(message);\n\n this.name = this.constructor.name;\n this.code = isLegacySignature ? (codeOrStatus as ErrorCode) : (messageOrCode as ErrorCode);\n this.status = isLegacySignature ? (statusOrMessage as number) : (codeOrStatus as number);\n this.context = context ?? {};\n this.timestamp = new Date();\n\n // Maintains proper stack trace for where error was thrown\n Error.captureStackTrace?.(this, this.constructor);\n }\n\n /**\n * Convert error to JSON for API responses (ClockIn-compatible shape)\n */\n toJSON(): Record<string, unknown> {\n return {\n error: {\n type: this.name,\n code: this.code,\n message: this.message,\n status: this.status,\n context: this.context,\n timestamp: this.timestamp.toISOString(),\n },\n };\n }\n\n /**\n * Check if error is operational (expected) vs programmer error\n */\n isOperational(): boolean {\n return true;\n }\n}\n\n// ============================================================================\n// Specific Error Classes\n// ============================================================================\n\n/**\n * Not initialized error\n */\nexport class NotInitializedError extends PayrollError {\n constructor(message = 'Payroll not initialized. Call Payroll.initialize() first.') {\n super(message, 'NOT_INITIALIZED', 500);\n }\n}\n\n/**\n * Employee not found error\n */\nexport class EmployeeNotFoundError extends PayrollError {\n constructor(employeeId?: string, context?: Record<string, unknown>) {\n super(\n employeeId ? `Employee not found: ${employeeId}` : 'Employee not found',\n 'EMPLOYEE_NOT_FOUND',\n 404,\n context ?? {}\n );\n }\n}\n\n/**\n * Invalid employee error\n */\nexport class InvalidEmployeeError extends PayrollError {\n constructor(message: string, context?: Record<string, unknown>) {\n super(message, 'INVALID_EMPLOYEE', 400, context ?? {});\n }\n}\n\n/**\n * Duplicate payroll error\n */\nexport class DuplicatePayrollError extends PayrollError {\n constructor(\n employeeId: string,\n month: number,\n year: number,\n context?: Record<string, unknown>\n ) {\n super(\n `Payroll already processed for employee ${employeeId} in ${month}/${year}`,\n 'DUPLICATE_PAYROLL',\n 409,\n { employeeId, month, year, ...context }\n );\n }\n}\n\n/**\n * Validation error\n */\nexport class ValidationError extends PayrollError {\n readonly errors: string[];\n\n constructor(errors: string | string[], context?: Record<string, unknown>) {\n const errorArray = Array.isArray(errors) ? errors : [errors];\n super(errorArray.join(', '), 'VALIDATION_ERROR', 400, context ?? {});\n this.errors = errorArray;\n }\n}\n\n/**\n * Employee terminated error\n */\nexport class EmployeeTerminatedError extends PayrollError {\n constructor(employeeId?: string, context?: Record<string, unknown>) {\n super(\n employeeId\n ? `Cannot perform operation on terminated employee: ${employeeId}`\n : 'Cannot perform operation on terminated employee',\n 'EMPLOYEE_TERMINATED',\n 400,\n context ?? {}\n );\n }\n}\n\n/**\n * Already processed error\n */\nexport class AlreadyProcessedError extends PayrollError {\n constructor(message: string, context?: Record<string, unknown>) {\n super(message, 'ALREADY_PROCESSED', 409, context ?? {});\n }\n}\n\n/**\n * Not eligible error\n */\nexport class NotEligibleError extends PayrollError {\n constructor(message: string, context?: Record<string, unknown>) {\n super(message, 'NOT_ELIGIBLE', 400, context ?? {});\n }\n}\n\n/**\n * Security error (unauthorized access, cross-organization access, etc.)\n */\nexport class SecurityError extends PayrollError {\n constructor(message: string, context?: Record<string, unknown>) {\n super(message, 'SECURITY_ERROR', 403, context ?? {});\n }\n}\n\n// ============================================================================\n// Error Factory\n// ============================================================================\n\n/**\n * Create error from code\n */\nexport function createError(\n code: ErrorCode,\n message: string,\n context?: Record<string, unknown>\n): PayrollError {\n const statusMap: Record<ErrorCode, number> = {\n PAYROLL_ERROR: 500,\n NOT_INITIALIZED: 500,\n EMPLOYEE_NOT_FOUND: 404,\n INVALID_EMPLOYEE: 400,\n DUPLICATE_PAYROLL: 409,\n VALIDATION_ERROR: 400,\n EMPLOYEE_TERMINATED: 400,\n ALREADY_PROCESSED: 409,\n NOT_ELIGIBLE: 400,\n SECURITY_ERROR: 403,\n };\n\n return new PayrollError(message, code, statusMap[code] || 500, context ?? {});\n}\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\n/**\n * Check if error is PayrollError\n */\nexport function isPayrollError(error: unknown): error is PayrollError {\n return error instanceof PayrollError;\n}\n\n/**\n * Check if error has specific code\n */\nexport function isErrorCode(error: unknown, code: ErrorCode): boolean {\n return isPayrollError(error) && error.code === code;\n}\n\n/**\n * Extract error info for logging\n */\nexport function extractErrorInfo(error: unknown): {\n code: string;\n status: number;\n message: string;\n context?: Record<string, unknown>;\n} {\n if (isPayrollError(error)) {\n return {\n code: error.code,\n status: error.status,\n message: error.message,\n context: error.context,\n };\n }\n\n if (error instanceof Error) {\n return {\n code: 'PAYROLL_ERROR',\n status: 500,\n message: error.message,\n };\n }\n\n return {\n code: 'PAYROLL_ERROR',\n status: 500,\n message: String(error),\n };\n}\n\n// ============================================================================\n// Error Handling Utilities\n// ============================================================================\n\n/**\n * Wrap async function with error handling\n */\nexport function withErrorHandling<T extends (...args: unknown[]) => Promise<unknown>>(\n fn: T,\n errorHandler?: (error: unknown) => void\n): T {\n return (async (...args: Parameters<T>) => {\n try {\n return await fn(...args);\n } catch (error) {\n if (errorHandler) {\n errorHandler(error);\n }\n throw error;\n }\n }) as T;\n}\n\n/**\n * Convert unknown error to PayrollError\n */\nexport function toPayrollError(error: unknown): PayrollError {\n if (isPayrollError(error)) {\n return error;\n }\n\n if (error instanceof Error) {\n return new PayrollError(error.message);\n }\n\n return new PayrollError(String(error));\n}\n\n// ============================================================================\n// Legacy Alias\n// ============================================================================\n\nexport { PayrollError as HRMError };\n\n","/**\n * @classytic/payroll - Secure Employee Lookup\n *\n * Multi-tenant safe employee lookup utilities\n * Enforces organizationId isolation on all queries\n */\n\nimport type { Model } from 'mongoose';\nimport type { EmployeeDocument, ObjectIdLike } from '../types.js';\nimport { toObjectId, isValidObjectId } from './query-builders.js';\nimport { EmployeeNotFoundError } from '../errors/index.js';\n\n/**\n * Employee ID mode for explicit disambiguation\n *\n * Controls how the employeeId parameter is interpreted:\n * - 'auto': Auto-detect via isValidObjectId() (default)\n * - 'objectId': Force treat as MongoDB _id (ObjectId)\n * - 'businessId': Force treat as business employeeId string\n *\n * @since v2.3.0\n */\nexport type EmployeeIdMode = 'auto' | 'objectId' | 'businessId';\n\n/**\n * Lookup options for secure employee queries\n */\nexport interface SecureEmployeeLookupOptions {\n /**\n * Organization ID (required for multi-tenant isolation)\n * Can be omitted only in single-tenant mode with auto-inject\n */\n organizationId?: ObjectIdLike;\n\n /**\n * Strict multi-tenant mode\n *\n * When true (default), throws if organizationId is not provided.\n * This prevents accidental cross-tenant data leaks.\n *\n * Set to false ONLY for:\n * - Single-tenant applications with auto-inject configured\n * - Migration scripts with explicit security review\n *\n * @default true (secure by default)\n * @since v3.0.0\n */\n strictMultiTenant?: boolean;\n\n /**\n * Employee's Mongoose _id (ObjectId)\n * Use this when you have the database ID\n * Takes priority over employeeId if both are provided\n */\n _id?: ObjectIdLike;\n\n /**\n * Employee identifier (supports both formats):\n * - ObjectId: employee._id (MongoDB document ID)\n * - String: \"EMP-001\" (business identifier)\n *\n * System auto-detects type by default:\n * - If valid ObjectId → queries by _id field\n * - If string → queries by employeeId field\n *\n * Use employeeIdType to override auto-detection if your business\n * employeeIds look like ObjectIds (24 hex characters).\n *\n * Note: If _id parameter is also provided, _id takes priority\n */\n employeeId?: ObjectIdLike | string;\n\n /**\n * Explicit mode hint for employeeId disambiguation\n *\n * - 'auto' (default): Auto-detect via isValidObjectId()\n * - 'objectId': Force treat as MongoDB _id (ObjectId)\n * - 'businessId': Force treat as business employeeId string\n *\n * Use 'businessId' if your employeeIds are 24-hex strings like\n * \"507f1f77bcf86cd799439011\" to prevent ObjectId collision.\n *\n * @default 'auto'\n * @since v2.3.0\n */\n employeeIdMode?: EmployeeIdMode;\n\n /**\n * User ID reference (optional)\n */\n userId?: ObjectIdLike;\n\n /**\n * Email address (optional)\n */\n email?: string;\n\n /**\n * Mongoose session for transactions\n */\n session?: any;\n\n /**\n * Fields to populate\n */\n populate?: string | string[];\n}\n\n/**\n * Securely find an employee by various identifiers\n *\n * SECURITY: By default (strictMultiTenant=true), this function REQUIRES organizationId\n * and will throw an error if not provided. This prevents cross-tenant data leaks.\n *\n * @param model - Employee model\n * @param options - Lookup options\n * @returns Employee document or throws EmployeeNotFoundError\n * @throws Error if organizationId not provided in strict mode (default)\n *\n * @example\n * // By ObjectId _id (organizationId required)\n * const emp = await findEmployeeSecure(Employee, {\n * _id: employee._id,\n * organizationId: org._id\n * });\n *\n * @example\n * // By string employeeId\n * const emp = await findEmployeeSecure(Employee, {\n * employeeId: \"EMP-001\",\n * organizationId: org._id\n * });\n *\n * @example\n * // By 24-hex business employeeId (force businessId mode)\n * const emp = await findEmployeeSecure(Employee, {\n * employeeId: \"507f1f77bcf86cd799439011\", // Looks like ObjectId!\n * employeeIdMode: 'businessId', // Force treat as business ID\n * organizationId: org._id\n * });\n *\n * @example\n * // Single-tenant mode (explicitly opt-out of strict mode)\n * const emp = await findEmployeeSecure(Employee, {\n * employeeId: \"EMP-001\",\n * strictMultiTenant: false, // Only for single-tenant apps!\n * });\n */\nexport async function findEmployeeSecure<T extends EmployeeDocument>(\n model: Model<T>,\n options: SecureEmployeeLookupOptions\n): Promise<T> {\n const {\n organizationId,\n strictMultiTenant = true, // SECURITY: Default to strict mode to prevent cross-tenant leaks\n _id,\n employeeId,\n employeeIdMode = 'auto',\n userId,\n email,\n session,\n populate,\n } = options;\n\n // Build query with organizationId isolation\n const query: Record<string, any> = {};\n\n // STRICT MODE: Enforce organizationId in multi-tenant apps\n if (strictMultiTenant && !organizationId) {\n throw new Error(\n 'findEmployeeSecure requires organizationId in strict multi-tenant mode. ' +\n 'Set strictMultiTenant: false for single-tenant apps.'\n );\n }\n\n // Include organizationId for multi-tenant safety\n if (organizationId) {\n query.organizationId = toObjectId(organizationId);\n }\n\n // Add identifier filters (priority: _id > employeeId > userId > email)\n if (_id) {\n query._id = toObjectId(_id);\n } else if (employeeId !== undefined) {\n // Resolve employeeId mode based on explicit hint or auto-detection\n const shouldTreatAsObjectId =\n employeeIdMode === 'objectId' ||\n (employeeIdMode === 'auto' && isValidObjectId(employeeId));\n\n const shouldTreatAsBusinessId =\n employeeIdMode === 'businessId' ||\n (employeeIdMode === 'auto' && !isValidObjectId(employeeId));\n\n if (shouldTreatAsObjectId) {\n // It's an ObjectId → query by _id field\n query._id = toObjectId(employeeId as ObjectIdLike);\n } else if (shouldTreatAsBusinessId) {\n // It's a string business identifier → query by employeeId field\n query.employeeId = employeeId as string;\n }\n } else if (userId) {\n query.userId = toObjectId(userId);\n } else if (email) {\n // Normalize email (lowercase + trim) to match schema storage format\n query.email = email.toLowerCase().trim();\n } else {\n throw new Error(\n 'findEmployeeSecure requires at least one identifier: _id, employeeId, userId, or email'\n );\n }\n\n // Build Mongoose query\n let mongooseQuery = model.findOne(query);\n\n if (session) {\n mongooseQuery = mongooseQuery.session(session);\n }\n\n if (populate) {\n const fields = Array.isArray(populate) ? populate : [populate];\n for (const field of fields) {\n mongooseQuery = mongooseQuery.populate(field);\n }\n }\n\n const employee = await mongooseQuery;\n\n if (!employee) {\n // Provide helpful error message\n const identifier = _id\n ? `_id=${_id}`\n : employeeId\n ? `employeeId=${employeeId}`\n : userId\n ? `userId=${userId}`\n : `email=${email}`;\n\n throw new EmployeeNotFoundError(\n `Employee not found: ${identifier}${organizationId ? ` in organization ${organizationId}` : ''}`\n );\n }\n\n return employee;\n}\n\n/**\n * Check if an employee exists securely (with org isolation)\n *\n * @param model - Employee model\n * @param options - Lookup options\n * @returns true if employee exists, false otherwise\n */\nexport async function employeeExistsSecure<T extends EmployeeDocument>(\n model: Model<T>,\n options: SecureEmployeeLookupOptions\n): Promise<boolean> {\n try {\n await findEmployeeSecure(model, options);\n return true;\n } catch (error) {\n if (error instanceof EmployeeNotFoundError) {\n return false;\n }\n throw error;\n }\n}\n\n/**\n * Find multiple employees securely (with org isolation)\n *\n * @param model - Employee model\n * @param options - Query options\n * @returns Array of employee documents\n */\nexport async function findEmployeesSecure<T extends EmployeeDocument>(\n model: Model<T>,\n options: {\n organizationId: ObjectIdLike;\n filter?: Record<string, any>;\n session?: any;\n limit?: number;\n skip?: number;\n sort?: Record<string, 1 | -1>;\n }\n): Promise<T[]> {\n const { organizationId, filter = {}, session, limit, skip, sort } = options;\n\n // CRITICAL: Always include organizationId\n const query = {\n organizationId: toObjectId(organizationId),\n ...filter,\n };\n\n let mongooseQuery = model.find(query);\n\n if (session) {\n mongooseQuery = mongooseQuery.session(session);\n }\n\n if (limit) {\n mongooseQuery = mongooseQuery.limit(limit);\n }\n\n if (skip) {\n mongooseQuery = mongooseQuery.skip(skip);\n }\n\n if (sort) {\n mongooseQuery = mongooseQuery.sort(sort);\n }\n\n return mongooseQuery;\n}\n\n/**\n * Validate organizationId is provided (unless single-tenant with auto-inject)\n *\n * @param organizationId - Organization ID to validate\n * @param operation - Operation name for error message\n * @throws Error if organizationId is missing\n */\nexport function requireOrganizationId(\n organizationId: ObjectIdLike | undefined,\n operation: string\n): void {\n if (!organizationId) {\n throw new Error(\n `${operation} requires organizationId. ` +\n 'In multi-tenant mode, you must explicitly provide organizationId. ' +\n 'In single-tenant mode, ensure autoInject is enabled in configuration.'\n );\n }\n}\n","/**\n * @classytic/payroll - Organization Resolution Utility\n *\n * Smart organization ID resolution with priority chain:\n * 1. Explicit parameter (highest priority)\n * 2. Context.organizationId (from middleware/auth)\n * 3. Single-tenant config (if autoInject enabled)\n * 4. Error (if none found in multi-tenant mode)\n */\n\nimport type { ObjectIdLike, OperationContext } from '../types.js';\nimport type { Container } from '../core/container.js';\nimport { toObjectId } from './query-builders.js';\nimport { Types } from 'mongoose';\n\n/**\n * Container-like interface for organization resolution\n * Only requires the methods we need, allowing any generic Container type\n */\nexport interface ContainerLike {\n isSingleTenant(): boolean;\n getSingleTenantConfig(): { organizationId?: any; autoInject?: boolean } | null;\n getOrganizationId(): string | null;\n}\n\n/**\n * Organization resolution parameters\n */\nexport interface ResolveOrganizationIdParams {\n /**\n * Explicitly provided organizationId (highest priority)\n */\n explicit?: ObjectIdLike;\n\n /**\n * Operation context with possible organizationId\n */\n context?: OperationContext;\n\n /**\n * Container for single-tenant config access\n * Accepts any Container with any generic types\n */\n container?: ContainerLike;\n\n /**\n * Operation name for error messages\n */\n operation?: string;\n}\n\n/**\n * Smart organization ID resolution\n *\n * Priority Chain:\n * 1. Explicit param (highest)\n * 2. Context.organizationId (middleware/auth)\n * 3. Single-tenant config (if autoInject enabled)\n * 4. Error (if none found)\n *\n * @param params - Resolution parameters\n * @returns Resolved ObjectId\n * @throws Error if organizationId cannot be resolved\n *\n * @example\n * // Explicit param wins\n * const orgId = resolveOrganizationId({\n * explicit: org._id,\n * context: { organizationId: other._id },\n * operation: 'processSalary'\n * });\n * // Returns: org._id\n *\n * @example\n * // Context fallback\n * const orgId = resolveOrganizationId({\n * context: { organizationId: org._id },\n * operation: 'processSalary'\n * });\n * // Returns: org._id from context\n *\n * @example\n * // Single-tenant auto-inject\n * const orgId = resolveOrganizationId({\n * container: singleTenantContainer,\n * operation: 'processSalary'\n * });\n * // Returns: organizationId from container config\n */\nexport function resolveOrganizationId(\n params: ResolveOrganizationIdParams\n): Types.ObjectId {\n const { explicit, context, container, operation } = params;\n\n // 1. Explicit param wins\n if (explicit) {\n return toObjectId(explicit);\n }\n\n // 2. Context from middleware/auth\n if (context?.organizationId) {\n return toObjectId(context.organizationId);\n }\n\n // 3. Single-tenant auto-inject\n // FIX: Check if single-tenant mode is enabled AND autoInject is not disabled\n if (container?.isSingleTenant()) {\n const singleTenantConfig = container.getSingleTenantConfig();\n const autoInjectEnabled = singleTenantConfig?.autoInject !== false; // default: true\n\n if (autoInjectEnabled) {\n const orgId = container.getOrganizationId();\n if (orgId) {\n return toObjectId(orgId);\n }\n // Single-tenant with autoInject but no organizationId configured\n const operationName = operation || 'Operation';\n throw new Error(\n `${operationName}: Single-tenant mode with autoInject enabled requires organizationId in configuration.\\n\\n` +\n 'Fix by configuring organizationId:\\n' +\n ' const payroll = createPayrollInstance()\\n' +\n ' .withModels({ ... })\\n' +\n ' .forSingleTenant({ organizationId: YOUR_ORG_ID, autoInject: true })\\n' +\n ' .build();\\n\\n' +\n 'Or provide organizationId explicitly in each operation:\\n' +\n ` await payroll.${operation || 'method'}({ organizationId: org._id, ... });`\n );\n }\n // autoInject is explicitly disabled - fall through to require explicit orgId\n }\n\n // 4. Error - no organizationId found (multi-tenant or single-tenant with autoInject: false)\n const operationName = operation || 'Operation';\n const isSingleTenantNoAutoInject = container?.isSingleTenant() &&\n container.getSingleTenantConfig()?.autoInject === false;\n\n if (isSingleTenantNoAutoInject) {\n throw new Error(\n `${operationName} requires organizationId (autoInject is disabled in single-tenant config).\\n\\n` +\n 'Provide organizationId explicitly:\\n' +\n ` await payroll.${operation || 'method'}({ organizationId: org._id, ... });`\n );\n }\n\n throw new Error(\n `${operationName} requires organizationId in multi-tenant mode.\\n\\n` +\n 'Options:\\n' +\n '1. Provide it explicitly in parameters\\n' +\n '2. Pass it via context (from middleware/auth)\\n' +\n '3. Enable single-tenant mode with autoInject\\n\\n' +\n 'Example (multi-tenant):\\n' +\n ` await payroll.${operation || 'method'}({ organizationId: org._id, ... });\\n\\n` +\n 'Example (single-tenant):\\n' +\n ' const payroll = createPayrollInstance()\\n' +\n ' .withModels({ ... })\\n' +\n ' .forSingleTenant({ organizationId: myOrg._id, autoInject: true })\\n' +\n ' .build();'\n );\n}\n\n/**\n * Validate that organizationId is present\n *\n * @param organizationId - Organization ID to validate\n * @param operation - Operation name for error message\n * @returns ObjectId if valid\n * @throws Error if organizationId is missing or invalid\n */\nexport function validateOrganizationId(\n organizationId: ObjectIdLike | undefined,\n operation: string\n): Types.ObjectId {\n if (!organizationId) {\n throw new Error(\n `${operation} requires organizationId. ` +\n 'Provide it explicitly, via context, or enable single-tenant mode with autoInject.'\n );\n }\n\n try {\n return toObjectId(organizationId);\n } catch (error) {\n throw new Error(\n `${operation} received invalid organizationId: ${organizationId}. ` +\n 'Must be a valid ObjectId, ObjectId string, or ObjectId-like object.'\n );\n }\n}\n\n/**\n * Try to resolve organizationId without throwing\n *\n * @param params - Resolution parameters\n * @returns ObjectId if resolved, null otherwise\n */\nexport function tryResolveOrganizationId(\n params: ResolveOrganizationIdParams\n): Types.ObjectId | null {\n try {\n return resolveOrganizationId(params);\n } catch {\n return null;\n }\n}\n","/**\n * @classytic/payroll - Employee Identity Helper\n *\n * Dual identity system for employee lookups:\n * - MongoDB ObjectId (_id field)\n * - Business string identifier (employeeId field like \"EMP-001\")\n *\n * Auto-detects identifier type and builds appropriate queries\n */\n\nimport type { ObjectIdLike } from '../types.js';\nimport { Types } from 'mongoose';\nimport { isValidObjectId, toObjectId } from './query-builders.js';\n\n/**\n * Employee identifier type\n */\nexport type EmployeeIdType = 'objectId' | 'string';\n\n/**\n * Employee query filter\n */\nexport interface EmployeeQueryFilter {\n _id?: Types.ObjectId;\n employeeId?: string;\n organizationId: Types.ObjectId;\n}\n\n/**\n * Detect if employeeId is ObjectId or string\n *\n * @param employeeId - Employee identifier (ObjectId or string)\n * @returns 'objectId' if valid ObjectId, 'string' otherwise\n *\n * @example\n * detectEmployeeIdType(employee._id)\n * // Returns: 'objectId'\n *\n * @example\n * detectEmployeeIdType(\"EMP-001\")\n * // Returns: 'string'\n */\nexport function detectEmployeeIdType(\n employeeId: ObjectIdLike | string\n): EmployeeIdType {\n // Check if it's a valid ObjectId\n if (isValidObjectId(employeeId)) {\n return 'objectId';\n }\n\n // Otherwise treat as string business identifier\n return 'string';\n}\n\n/**\n * Build employee query based on identifier type\n *\n * Automatically detects if employeeId is ObjectId or string\n * and builds the appropriate MongoDB query filter.\n *\n * @param employeeId - Employee identifier (ObjectId or string)\n * @param organizationId - Organization ID for multi-tenant isolation\n * @returns Query filter for MongoDB\n *\n * @example\n * // By ObjectId _id\n * const query = buildEmployeeQuery(employee._id, org._id);\n * // Returns: { _id: ObjectId(...), organizationId: ObjectId(...) }\n *\n * @example\n * // By string employeeId\n * const query = buildEmployeeQuery(\"EMP-001\", org._id);\n * // Returns: { employeeId: \"EMP-001\", organizationId: ObjectId(...) }\n */\nexport function buildEmployeeQuery(\n employeeId: ObjectIdLike | string,\n organizationId: Types.ObjectId\n): EmployeeQueryFilter {\n const idType = detectEmployeeIdType(employeeId);\n\n if (idType === 'objectId') {\n return {\n _id: toObjectId(employeeId as ObjectIdLike),\n organizationId,\n };\n }\n\n // String business identifier\n return {\n employeeId: employeeId as string,\n organizationId,\n };\n}\n\n/**\n * Normalize employee identifier to consistent format\n *\n * Converts ObjectIdLike to ObjectId, keeps strings as-is\n *\n * @param employeeId - Employee identifier\n * @returns Normalized ObjectId or string\n */\nexport function normalizeEmployeeId(\n employeeId: ObjectIdLike | string\n): Types.ObjectId | string {\n const idType = detectEmployeeIdType(employeeId);\n\n if (idType === 'objectId') {\n return toObjectId(employeeId as ObjectIdLike);\n }\n\n return employeeId as string;\n}\n\n/**\n * Check if value is a string employee ID (not ObjectId)\n *\n * @param value - Value to check\n * @returns true if string employeeId, false if ObjectId\n */\nexport function isStringEmployeeId(value: unknown): value is string {\n return typeof value === 'string' && !isValidObjectId(value);\n}\n\n/**\n * Check if value is an ObjectId employee identifier\n *\n * @param value - Value to check\n * @returns true if ObjectId, false otherwise\n */\nexport function isObjectIdEmployeeId(value: unknown): value is ObjectIdLike {\n return isValidObjectId(value);\n}\n\n/**\n * Format employee identifier for display\n *\n * @param employeeId - Employee identifier\n * @returns Human-readable string\n *\n * @example\n * formatEmployeeId(employee._id)\n * // Returns: \"_id=507f1f77bcf86cd799439011\"\n *\n * @example\n * formatEmployeeId(\"EMP-001\")\n * // Returns: \"employeeId=EMP-001\"\n */\nexport function formatEmployeeId(employeeId: ObjectIdLike | string): string {\n const idType = detectEmployeeIdType(employeeId);\n\n if (idType === 'objectId') {\n return `_id=${toObjectId(employeeId as ObjectIdLike).toString()}`;\n }\n\n return `employeeId=${employeeId}`;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utils/logger.ts","../../src/utils/date.ts","../../src/utils/money.ts","../../src/utils/calculation.ts","../../src/core/config.ts","../../src/calculators/prorating.calculator.ts","../../src/enums.ts","../../src/utils/validation.ts","../../src/utils/query-builders.ts","../../src/utils/leave.ts","../../src/errors/index.ts","../../src/utils/employee-lookup.ts","../../src/utils/org-resolution.ts","../../src/utils/employee-identity.ts","../../src/utils/type-guards.ts","../../src/utils/error-helpers.ts"],"names":["logger","periodWorkingDays","employee","minValue","maxValue","min","max","sum","operationName"],"mappings":";;;;AAaA,IAAM,sBAAsB,OAAe;AAAA,EACzC,IAAA,EAAM,CAAC,OAAA,EAAiB,IAAA,KAAmC;AACzD,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,gBAAA,EAAmB,OAAO,CAAA,CAAA,EAAI,IAAI,CAAA;AAAA,IAChD,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,gBAAA,EAAmB,OAAO,CAAA,CAAE,CAAA;AAAA,IAC1C;AAAA,EACF,CAAA;AAAA,EACA,KAAA,EAAO,CAAC,OAAA,EAAiB,IAAA,KAAmC;AAC1D,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,iBAAA,EAAoB,OAAO,CAAA,CAAA,EAAI,IAAI,CAAA;AAAA,IACnD,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,KAAA,CAAM,CAAA,iBAAA,EAAoB,OAAO,CAAA,CAAE,CAAA;AAAA,IAC7C;AAAA,EACF,CAAA;AAAA,EACA,IAAA,EAAM,CAAC,OAAA,EAAiB,IAAA,KAAmC;AACzD,IAAA,IAAI,IAAA,EAAM;AACR,MAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,gBAAA,EAAmB,OAAO,CAAA,CAAA,EAAI,IAAI,CAAA;AAAA,IACjD,CAAA,MAAO;AACL,MAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,gBAAA,EAAmB,OAAO,CAAA,CAAE,CAAA;AAAA,IAC3C;AAAA,EACF,CAAA;AAAA,EACA,KAAA,EAAO,CAAC,OAAA,EAAiB,IAAA,KAAmC;AAC1D,IAAA,IAAI,OAAA,CAAQ,GAAA,CAAI,QAAA,KAAa,YAAA,EAAc;AACzC,MAAA,IAAI,IAAA,EAAM;AACR,QAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,iBAAA,EAAoB,OAAO,CAAA,CAAA,EAAI,IAAI,CAAA;AAAA,MACjD,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,iBAAA,EAAoB,OAAO,CAAA,CAAE,CAAA;AAAA,MAC3C;AAAA,IACF;AAAA,EACF;AACF,CAAA,CAAA;AAMA,IAAI,gBAAwB,mBAAA,EAAoB;AAChD,IAAI,cAAA,GAAiB,IAAA;AASd,SAAS,SAAA,GAAoB;AAClC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,CAAC,OAAA,EAAiB,IAAA,KAAmC;AACzD,MAAA,IAAI,cAAA,EAAgB,aAAA,CAAc,IAAA,CAAK,OAAA,EAAS,IAAI,CAAA;AAAA,IACtD,CAAA;AAAA,IACA,KAAA,EAAO,CAAC,OAAA,EAAiB,IAAA,KAAmC;AAC1D,MAAA,IAAI,cAAA,EAAgB,aAAA,CAAc,KAAA,CAAM,OAAA,EAAS,IAAI,CAAA;AAAA,IACvD,CAAA;AAAA,IACA,IAAA,EAAM,CAAC,OAAA,EAAiB,IAAA,KAAmC;AACzD,MAAA,IAAI,cAAA,EAAgB,aAAA,CAAc,IAAA,CAAK,OAAA,EAAS,IAAI,CAAA;AAAA,IACtD,CAAA;AAAA,IACA,KAAA,EAAO,CAAC,OAAA,EAAiB,IAAA,KAAmC;AAC1D,MAAA,IAAI,cAAA,EAAgB,aAAA,CAAc,KAAA,CAAM,OAAA,EAAS,IAAI,CAAA;AAAA,IACvD;AAAA,GACF;AACF;AAKO,SAAS,UAAUA,OAAAA,EAAsB;AAC9C,EAAA,aAAA,GAAgBA,OAAAA;AAClB;AAKO,SAAS,WAAA,GAAoB;AAClC,EAAA,aAAA,GAAgB,mBAAA,EAAoB;AACtC;AAKO,SAAS,kBAAkB,MAAA,EAAwB;AACxD,EAAA,MAAM,MAAA,GAAS,aAAA;AACf,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,CAAC,OAAA,EAAiB,IAAA,KAAmC;AACzD,MAAA,IAAI,cAAA,SAAuB,IAAA,CAAK,CAAA,CAAA,EAAI,MAAM,CAAA,EAAA,EAAK,OAAO,IAAI,IAAI,CAAA;AAAA,IAChE,CAAA;AAAA,IACA,KAAA,EAAO,CAAC,OAAA,EAAiB,IAAA,KAAmC;AAC1D,MAAA,IAAI,cAAA,SAAuB,KAAA,CAAM,CAAA,CAAA,EAAI,MAAM,CAAA,EAAA,EAAK,OAAO,IAAI,IAAI,CAAA;AAAA,IACjE,CAAA;AAAA,IACA,IAAA,EAAM,CAAC,OAAA,EAAiB,IAAA,KAAmC;AACzD,MAAA,IAAI,cAAA,SAAuB,IAAA,CAAK,CAAA,CAAA,EAAI,MAAM,CAAA,EAAA,EAAK,OAAO,IAAI,IAAI,CAAA;AAAA,IAChE,CAAA;AAAA,IACA,KAAA,EAAO,CAAC,OAAA,EAAiB,IAAA,KAAmC;AAC1D,MAAA,IAAI,cAAA,SAAuB,KAAA,CAAM,CAAA,CAAA,EAAI,MAAM,CAAA,EAAA,EAAK,OAAO,IAAI,IAAI,CAAA;AAAA,IACjE;AAAA,GACF;AACF;AAKO,SAAS,kBAAA,GAA6B;AAC3C,EAAA,OAAO;AAAA,IACL,MAAM,MAAM;AAAA,IAAC,CAAA;AAAA,IACb,OAAO,MAAM;AAAA,IAAC,CAAA;AAAA,IACd,MAAM,MAAM;AAAA,IAAC,CAAA;AAAA,IACb,OAAO,MAAM;AAAA,IAAC;AAAA,GAChB;AACF;AAKO,SAAS,aAAA,GAAsB;AACpC,EAAA,cAAA,GAAiB,IAAA;AACnB;AAKO,SAAS,cAAA,GAAuB;AACrC,EAAA,cAAA,GAAiB,KAAA;AACnB;AAKO,SAAS,gBAAA,GAA4B;AAC1C,EAAA,OAAO,cAAA;AACT;AAUO,IAAM,MAAA,GAAiB;AAAA,EAC5B,IAAA,EAAM,CAAC,OAAA,EAAiB,IAAA,KAAmC;AACzD,IAAA,IAAI,cAAA,EAAgB,aAAA,CAAc,IAAA,CAAK,OAAA,EAAS,IAAI,CAAA;AAAA,EACtD,CAAA;AAAA,EACA,KAAA,EAAO,CAAC,OAAA,EAAiB,IAAA,KAAmC;AAC1D,IAAA,IAAI,cAAA,EAAgB,aAAA,CAAc,KAAA,CAAM,OAAA,EAAS,IAAI,CAAA;AAAA,EACvD,CAAA;AAAA,EACA,IAAA,EAAM,CAAC,OAAA,EAAiB,IAAA,KAAmC;AACzD,IAAA,IAAI,cAAA,EAAgB,aAAA,CAAc,IAAA,CAAK,OAAA,EAAS,IAAI,CAAA;AAAA,EACtD,CAAA;AAAA,EACA,KAAA,EAAO,CAAC,OAAA,EAAiB,IAAA,KAAmC;AAC1D,IAAA,IAAI,cAAA,EAAgB,aAAA,CAAc,KAAA,CAAM,OAAA,EAAS,IAAI,CAAA;AAAA,EACvD;AACF;;;ACtJO,SAAS,OAAA,CAAQ,MAAY,IAAA,EAAoB;AACtD,EAAA,MAAM,MAAA,GAAS,IAAI,IAAA,CAAK,IAAI,CAAA;AAC5B,EAAA,MAAA,CAAO,OAAA,CAAQ,MAAA,CAAO,OAAA,EAAQ,GAAI,IAAI,CAAA;AACtC,EAAA,OAAO,MAAA;AACT;AAKO,SAAS,SAAA,CAAU,MAAY,MAAA,EAAsB;AAC1D,EAAA,MAAM,MAAA,GAAS,IAAI,IAAA,CAAK,IAAI,CAAA;AAC5B,EAAA,MAAA,CAAO,QAAA,CAAS,MAAA,CAAO,QAAA,EAAS,GAAI,MAAM,CAAA;AAC1C,EAAA,OAAO,MAAA;AACT;AAKO,SAAS,QAAA,CAAS,MAAY,KAAA,EAAqB;AACxD,EAAA,MAAM,MAAA,GAAS,IAAI,IAAA,CAAK,IAAI,CAAA;AAC5B,EAAA,MAAA,CAAO,WAAA,CAAY,MAAA,CAAO,WAAA,EAAY,GAAI,KAAK,CAAA;AAC/C,EAAA,OAAO,MAAA;AACT;AAKO,SAAS,OAAA,CAAQ,MAAY,IAAA,EAAoB;AACtD,EAAA,OAAO,OAAA,CAAQ,IAAA,EAAM,CAAC,IAAI,CAAA;AAC5B;AAKO,SAAS,SAAA,CAAU,MAAY,MAAA,EAAsB;AAC1D,EAAA,OAAO,SAAA,CAAU,IAAA,EAAM,CAAC,MAAM,CAAA;AAChC;AASO,SAAS,aAAa,IAAA,EAAkB;AAC7C,EAAA,MAAM,MAAA,GAAS,IAAI,IAAA,CAAK,IAAI,CAAA;AAC5B,EAAA,MAAA,CAAO,QAAQ,CAAC,CAAA;AAChB,EAAA,MAAA,CAAO,QAAA,CAAS,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AAC1B,EAAA,OAAO,MAAA;AACT;AAKO,SAAS,WAAW,IAAA,EAAkB;AAC3C,EAAA,MAAM,MAAA,GAAS,IAAI,IAAA,CAAK,IAAI,CAAA;AAC5B,EAAA,MAAA,CAAO,QAAA,CAAS,MAAA,CAAO,QAAA,EAAS,GAAI,GAAG,CAAC,CAAA;AACxC,EAAA,MAAA,CAAO,QAAA,CAAS,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,GAAG,CAAA;AAC/B,EAAA,OAAO,MAAA;AACT;AAKO,SAAS,YAAY,IAAA,EAAkB;AAC5C,EAAA,MAAM,MAAA,GAAS,IAAI,IAAA,CAAK,IAAI,CAAA;AAC5B,EAAA,MAAA,CAAO,QAAA,CAAS,GAAG,CAAC,CAAA;AACpB,EAAA,MAAA,CAAO,QAAA,CAAS,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AAC1B,EAAA,OAAO,MAAA;AACT;AAKO,SAAS,UAAU,IAAA,EAAkB;AAC1C,EAAA,MAAM,MAAA,GAAS,IAAI,IAAA,CAAK,IAAI,CAAA;AAC5B,EAAA,MAAA,CAAO,QAAA,CAAS,IAAI,EAAE,CAAA;AACtB,EAAA,MAAA,CAAO,QAAA,CAAS,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,GAAG,CAAA;AAC/B,EAAA,OAAO,MAAA;AACT;AAKO,SAAS,WAAW,IAAA,EAAkB;AAC3C,EAAA,MAAM,MAAA,GAAS,IAAI,IAAA,CAAK,IAAI,CAAA;AAC5B,EAAA,MAAA,CAAO,QAAA,CAAS,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AAC1B,EAAA,OAAO,MAAA;AACT;AAKO,SAAS,SAAS,IAAA,EAAkB;AACzC,EAAA,MAAM,MAAA,GAAS,IAAI,IAAA,CAAK,IAAI,CAAA;AAC5B,EAAA,MAAA,CAAO,QAAA,CAAS,EAAA,EAAI,EAAA,EAAI,EAAA,EAAI,GAAG,CAAA;AAC/B,EAAA,OAAO,MAAA;AACT;AAaO,SAAS,gBAAgB,IAAA,EAAoB;AAClD,EAAA,MAAM,CAAA,GAAI,IAAI,IAAA,CAAK,IAAI,CAAA;AACvB,EAAA,CAAA,CAAE,QAAA,CAAS,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AACrB,EAAA,OAAO,CAAA,EAAG,CAAA,CAAE,WAAA,EAAa,CAAA,CAAA,EAAI,OAAO,CAAA,CAAE,QAAA,EAAS,GAAI,CAAC,CAAA,CAAE,QAAA,CAAS,GAAG,GAAG,CAAC,CAAA,CAAA,EAAI,MAAA,CAAO,CAAA,CAAE,OAAA,EAAS,CAAA,CAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAA;AAChH;AAYO,SAAS,UAAA,CAAW,OAAa,GAAA,EAAmB;AACzD,EAAA,MAAM,CAAA,GAAI,IAAI,IAAA,CAAK,KAAK,CAAA;AACxB,EAAA,MAAM,CAAA,GAAI,IAAI,IAAA,CAAK,GAAG,CAAA;AACtB,EAAA,CAAA,CAAE,QAAA,CAAS,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AACrB,EAAA,CAAA,CAAE,QAAA,CAAS,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AACrB,EAAA,OAAO,IAAA,CAAK,IAAA,CAAA,CAAM,CAAA,CAAE,OAAA,EAAQ,GAAI,CAAA,CAAE,OAAA,EAAQ,KAAM,GAAA,GAAO,EAAA,GAAK,EAAA,GAAK,EAAA,CAAG,CAAA;AACtE;AAKO,SAAS,YAAA,CAAa,OAAa,GAAA,EAAmB;AAC3D,EAAA,MAAM,SAAA,GAAY,IAAI,IAAA,CAAK,KAAK,CAAA;AAChC,EAAA,MAAM,OAAA,GAAU,IAAI,IAAA,CAAK,GAAG,CAAA;AAC5B,EAAA,OAAA,CACG,OAAA,CAAQ,WAAA,EAAY,GAAI,SAAA,CAAU,WAAA,EAAY,IAAK,EAAA,IACnD,OAAA,CAAQ,QAAA,EAAS,GAAI,SAAA,CAAU,QAAA,EAAS,CAAA;AAE7C;AAKO,SAAS,WAAA,CAAY,OAAa,GAAA,EAAmB;AAC1D,EAAA,OAAO,KAAK,KAAA,CAAM,YAAA,CAAa,KAAA,EAAO,GAAG,IAAI,EAAE,CAAA;AACjD;AAGO,IAAM,WAAA,GAAc;AACpB,IAAM,aAAA,GAAgB;AAStB,SAAS,UAAU,IAAA,EAAqB;AAC7C,EAAA,MAAM,GAAA,GAAM,IAAI,IAAA,CAAK,IAAI,EAAE,MAAA,EAAO;AAClC,EAAA,OAAO,GAAA,IAAO,KAAK,GAAA,IAAO,CAAA;AAC5B;AAKO,SAAS,UAAU,IAAA,EAAqB;AAC7C,EAAA,MAAM,GAAA,GAAM,IAAI,IAAA,CAAK,IAAI,EAAE,MAAA,EAAO;AAClC,EAAA,OAAO,GAAA,KAAQ,KAAK,GAAA,KAAQ,CAAA;AAC9B;AAKO,SAAS,aAAa,IAAA,EAAoB;AAC/C,EAAA,OAAO,IAAI,IAAA,CAAK,IAAI,CAAA,CAAE,MAAA,EAAO;AAC/B;AAKO,SAAS,WAAW,IAAA,EAAoB;AAC7C,EAAA,MAAM,IAAA,GAAO;AAAA,IACX,QAAA;AAAA,IACA,QAAA;AAAA,IACA,SAAA;AAAA,IACA,WAAA;AAAA,IACA,UAAA;AAAA,IACA,QAAA;AAAA,IACA;AAAA,GACF;AACA,EAAA,OAAO,IAAA,CAAK,YAAA,CAAa,IAAI,CAAC,CAAA;AAChC;AASO,SAAS,YAAA,CAAa,OAAe,IAAA,EAA6B;AACvE,EAAA,MAAM,YAAY,IAAI,IAAA,CAAK,IAAA,EAAM,KAAA,GAAQ,GAAG,CAAC,CAAA;AAC7C,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,IAAA;AAAA,IACA,SAAA,EAAW,aAAa,SAAS,CAAA;AAAA,IACjC,OAAA,EAAS,WAAW,SAAS;AAAA,GAC/B;AACF;AA8EO,SAAS,gBAAA,CAAiB,IAAA,mBAAO,IAAI,IAAA,EAAK,EAAoC;AACnF,EAAA,MAAM,CAAA,GAAI,IAAI,IAAA,CAAK,IAAI,CAAA;AACvB,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,EAAE,WAAA,EAAY;AAAA,IACpB,KAAA,EAAO,CAAA,CAAE,QAAA,EAAS,GAAI;AAAA,GACxB;AACF;AAKO,SAAS,qBAAA,CAAsB,MAAc,KAAA,EAAuB;AACzE,EAAA,MAAM,QAAQ,IAAI,IAAA,CAAK,IAAA,EAAM,KAAA,GAAQ,GAAG,CAAC,CAAA;AACzC,EAAA,MAAM,GAAA,GAAM,WAAW,KAAK,CAAA;AAC5B,EAAA,IAAI,KAAA,GAAQ,CAAA;AAEZ,EAAA,MAAM,OAAA,GAAU,IAAI,IAAA,CAAK,KAAK,CAAA;AAC9B,EAAA,OAAO,WAAW,GAAA,EAAK;AACrB,IAAA,IAAI,SAAA,CAAU,OAAO,CAAA,EAAG;AACtB,MAAA,KAAA,EAAA;AAAA,IACF;AACA,IAAA,OAAA,CAAQ,OAAA,CAAQ,OAAA,CAAQ,OAAA,EAAQ,GAAI,CAAC,CAAA;AAAA,EACvC;AAEA,EAAA,OAAO,KAAA;AACT;AAKO,SAAS,cAAA,CAAe,MAAc,KAAA,EAAuB;AAClE,EAAA,OAAO,IAAI,IAAA,CAAK,IAAA,EAAM,KAAA,EAAO,CAAC,EAAE,OAAA,EAAQ;AAC1C;AASO,SAAS,qBAAA,CACd,UACA,eAAA,EACa;AACb,EAAA,IAAI,CAAC,eAAA,IAAmB,eAAA,IAAmB,CAAA,EAAG,OAAO,IAAA;AACrD,EAAA,OAAO,SAAA,CAAU,UAAU,eAAe,CAAA;AAC5C;AAKO,SAAS,aAAA,CACd,gBAAA,EACA,GAAA,mBAAM,IAAI,MAAK,EACN;AACT,EAAA,IAAI,CAAC,kBAAkB,OAAO,KAAA;AAC9B,EAAA,OAAO,GAAA,GAAM,IAAI,IAAA,CAAK,gBAAgB,CAAA;AACxC;AAKO,SAAS,uBAAA,CACd,UACA,eAAA,EACQ;AACR,EAAA,MAAM,GAAA,GAAM,eAAA,oBAAmB,IAAI,IAAA,EAAK;AACxC,EAAA,MAAM,IAAA,GAAO,UAAA,CAAW,QAAA,EAAU,GAAG,CAAA;AACrC,EAAA,OAAO,IAAA,CAAK,IAAI,CAAA,EAAG,IAAA,CAAK,MAAO,IAAA,GAAO,MAAA,GAAU,EAAE,CAAA,GAAI,EAAE,CAAA;AAC1D;AASO,SAAS,aAAA,CAAc,IAAA,EAAY,KAAA,EAAa,GAAA,EAAoB;AACzE,EAAA,MAAM,SAAA,GAAY,IAAI,IAAA,CAAK,IAAI,CAAA;AAC/B,EAAA,OAAO,SAAA,IAAa,IAAI,IAAA,CAAK,KAAK,KAAK,SAAA,IAAa,IAAI,KAAK,GAAG,CAAA;AAClE;AAsBO,SAAS,oBAAA,CACd,IAAA,EACA,WAAA,EACA,SAAA,EACS;AACT,EAAA,MAAM,aAAA,GAAgB,IAAA,CAAK,aAAA,GAAgB,IAAI,IAAA,CAAK,KAAK,aAAa,CAAA,mBAAI,IAAI,IAAA,CAAK,CAAC,CAAA;AACpF,EAAA,MAAM,WAAA,GAAc,IAAA,CAAK,WAAA,GAAc,IAAI,IAAA,CAAK,KAAK,WAAW,CAAA,mBAAI,IAAI,IAAA,CAAK,YAAY,CAAA;AAGzF,EAAA,OAAO,aAAA,IAAiB,aAAa,WAAA,IAAe,WAAA;AACtD;AAKO,SAAS,qBAAA,CACd,OACA,IAAA,EAC4B;AAC5B,EAAA,MAAM,MAAA,GAAS,YAAA,CAAa,KAAA,EAAO,IAAI,CAAA;AACvC,EAAA,OAAO,EAAE,KAAA,EAAO,MAAA,CAAO,SAAA,EAAW,GAAA,EAAK,OAAO,OAAA,EAAQ;AACxD;AASO,SAAS,gBAAgB,IAAA,EAAoB;AAClD,EAAA,IAAI,CAAC,MAAM,OAAO,EAAA;AAClB,EAAA,OAAO,IAAI,IAAA,CAAK,IAAI,CAAA,CAAE,WAAA,EAAY;AACpC;AAKO,SAAS,YAAY,UAAA,EAAiC;AAC3D,EAAA,IAAI,CAAC,YAAY,OAAO,IAAA;AACxB,EAAA,OAAO,IAAI,KAAK,UAAU,CAAA;AAC5B;AAKO,SAAS,YAAA,CAAa,EAAE,KAAA,EAAO,IAAA,EAAK,EAA4C;AACrF,EAAA,OAAO,CAAA,EAAG,OAAO,KAAK,CAAA,CAAE,SAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA;AAClD;AAKO,SAAS,YAAY,YAAA,EAAuD;AACjF,EAAA,MAAM,CAAC,OAAO,IAAI,CAAA,GAAI,aAAa,KAAA,CAAM,GAAG,CAAA,CAAE,GAAA,CAAI,MAAM,CAAA;AACxD,EAAA,OAAO,EAAE,OAAO,IAAA,EAAK;AACvB;AAKO,SAAS,aAAa,KAAA,EAAuB;AAClD,EAAA,MAAM,MAAA,GAAS;AAAA,IACb,SAAA;AAAA,IAAW,UAAA;AAAA,IAAY,OAAA;AAAA,IAAS,OAAA;AAAA,IAAS,KAAA;AAAA,IAAO,MAAA;AAAA,IAChD,MAAA;AAAA,IAAQ,QAAA;AAAA,IAAU,WAAA;AAAA,IAAa,SAAA;AAAA,IAAW,UAAA;AAAA,IAAY;AAAA,GACxD;AACA,EAAA,OAAO,MAAA,CAAO,KAAA,GAAQ,CAAC,CAAA,IAAK,EAAA;AAC9B;AAKO,SAAS,kBAAkB,KAAA,EAAuB;AACvD,EAAA,MAAM,MAAA,GAAS;AAAA,IACb,KAAA;AAAA,IAAO,KAAA;AAAA,IAAO,KAAA;AAAA,IAAO,KAAA;AAAA,IAAO,KAAA;AAAA,IAAO,KAAA;AAAA,IACnC,KAAA;AAAA,IAAO,KAAA;AAAA,IAAO,KAAA;AAAA,IAAO,KAAA;AAAA,IAAO,KAAA;AAAA,IAAO;AAAA,GACrC;AACA,EAAA,OAAO,MAAA,CAAO,KAAA,GAAQ,CAAC,CAAA,IAAK,EAAA;AAC9B;AAMA,IAAO,YAAA,GAAQ;AAAA,EACb,eAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA,EACA,YAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA;AAAA,EACA,SAAA;AAAA,EACA,UAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,YAAA;AAAA,EACA,WAAA;AAAA,EACA,WAAA;AAAA,EACA,aAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,YAAA;AAAA,EACA,UAAA;AAAA,EACA,YAAA;AAAA,EACA,gBAAA;AAAA,EACA,qBAAA;AAAA,EACA,cAAA;AAAA,EACA,qBAAA;AAAA,EACA,aAAA;AAAA,EACA,uBAAA;AAAA,EACA,aAAA;AAAA,EACA,oBAAA;AAAA,EACA,qBAAA;AAAA,EACA,eAAA;AAAA,EACA,WAAA;AAAA,EACA,YAAA;AAAA,EACA,WAAA;AAAA,EACA,YAAA;AAAA,EACA;AACF;;;ACheO,SAAS,UAAA,CAAW,KAAA,EAAe,QAAA,GAAW,CAAA,EAAW;AAC9D,EAAA,MAAM,UAAA,GAAa,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,QAAQ,CAAA;AACxC,EAAA,MAAM,SAAS,KAAA,GAAQ,UAAA;AACvB,EAAA,MAAM,QAAA,GAAW,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,MAAM,CAAA;AAI3C,EAAA,IAAI,IAAA,CAAK,GAAA,CAAI,QAAA,GAAW,GAAG,IAAI,KAAA,EAAO;AACpC,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,MAAM,CAAA;AAC/B,IAAA,MAAM,OAAA,GAAU,KAAA,GAAQ,CAAA,KAAM,CAAA,GAAI,QAAQ,KAAA,GAAQ,CAAA;AAClD,IAAA,OAAO,OAAA,GAAU,UAAA;AAAA,EACnB;AAGA,EAAA,OAAO,IAAA,CAAK,KAAA,CAAM,MAAM,CAAA,GAAI,UAAA;AAC9B;AASO,SAAS,kBAAA,CAAmB,KAAA,EAAe,QAAA,GAAW,CAAA,EAAW;AACtE,EAAA,OAAO,KAAK,GAAA,CAAI,CAAA,EAAG,UAAA,CAAW,KAAA,EAAO,QAAQ,CAAC,CAAA;AAChD;AAUO,SAAS,YAAA,CAAa,MAAA,EAAgB,UAAA,EAAoB,QAAA,GAAW,CAAA,EAAW;AACrF,EAAA,OAAO,UAAA,CAAY,MAAA,GAAS,UAAA,GAAc,GAAA,EAAK,QAAQ,CAAA;AACzD;AAUO,SAAS,aAAA,CAAc,MAAA,EAAgB,KAAA,EAAe,QAAA,GAAW,CAAA,EAAW;AACjF,EAAA,OAAO,UAAA,CAAW,MAAA,GAAS,KAAA,EAAO,QAAQ,CAAA;AAC5C;;;ACnFO,SAAS,IAAI,OAAA,EAA2B;AAC7C,EAAA,OAAO,QAAQ,MAAA,CAAO,CAAC,OAAO,CAAA,KAAM,KAAA,GAAQ,GAAG,CAAC,CAAA;AAClD;AAKO,SAAS,KAAA,CAAS,OAAY,MAAA,EAAqC;AACxE,EAAA,OAAO,KAAA,CAAM,OAAO,CAAC,KAAA,EAAO,SAAS,KAAA,GAAQ,MAAA,CAAO,IAAI,CAAA,EAAG,CAAC,CAAA;AAC9D;AAKO,SAAS,cAAc,UAAA,EAA+C;AAC3E,EAAA,OAAO,KAAA,CAAM,UAAA,EAAY,CAAC,CAAA,KAAM,EAAE,MAAM,CAAA;AAC1C;AAKO,SAAS,cAAc,UAAA,EAA+C;AAC3E,EAAA,OAAO,KAAA,CAAM,UAAA,EAAY,CAAC,CAAA,KAAM,EAAE,MAAM,CAAA;AAC1C;AA2CO,SAAS,aAAa,KAAA,EAAuB;AAClD,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,KAAK,CAAA;AAC9B,EAAA,MAAM,WAAW,KAAA,GAAQ,KAAA;AAGzB,EAAA,IAAI,KAAK,GAAA,CAAI,QAAA,GAAW,GAAG,CAAA,GAAI,OAAO,OAAA,EAAS;AAE7C,IAAA,OAAO,KAAA,GAAQ,CAAA,KAAM,CAAA,GAAI,KAAA,GAAQ,KAAA,GAAQ,CAAA;AAAA,EAC3C;AAGA,EAAA,OAAO,IAAA,CAAK,MAAM,KAAK,CAAA;AACzB;AAYO,SAAS,eAAA,CAAgB,MAAA,EAAgB,UAAA,EAAoB,QAAA,GAAW,CAAA,EAAW;AAExF,EAAA,MAAM,MAAA,GAAU,SAAS,UAAA,GAAc,GAAA;AACvC,EAAA,OAAO,UAAA,CAAW,QAAQ,QAAQ,CAAA;AACpC;AAKO,SAAS,mBAAA,CAAoB,MAAc,KAAA,EAAuB;AACvE,EAAA,OAAO,QAAQ,CAAA,GAAI,YAAA,CAAc,IAAA,GAAO,KAAA,GAAS,GAAG,CAAA,GAAI,CAAA;AAC1D;AAKO,SAAS,OAAA,CAAQ,KAAA,EAAe,QAAA,GAAW,CAAA,EAAW;AAC3D,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,QAAQ,CAAA;AACpC,EAAA,OAAO,YAAA,CAAa,KAAA,GAAQ,MAAM,CAAA,GAAI,MAAA;AACxC;AASO,SAAS,cAAA,CACd,YACA,UAAA,EACQ;AACR,EAAA,OAAO,UAAA,GAAa,cAAc,UAAU,CAAA;AAC9C;AAKO,SAAS,YAAA,CACd,OACA,UAAA,EACQ;AACR,EAAA,OAAO,KAAK,GAAA,CAAI,CAAA,EAAG,KAAA,GAAQ,aAAA,CAAc,UAAU,CAAC,CAAA;AACtD;AAKO,SAAS,0BAAA,CACd,UAAA,EACA,UAAA,EACA,UAAA,EACoD;AACpD,EAAA,MAAM,KAAA,GAAQ,cAAA,CAAe,UAAA,EAAY,UAAU,CAAA;AACnD,EAAA,MAAM,eAAA,GAAkB,cAAc,UAAU,CAAA;AAChD,EAAA,MAAM,GAAA,GAAM,YAAA,CAAa,KAAA,EAAO,UAAU,CAAA;AAC1C,EAAA,OAAO,EAAE,KAAA,EAAO,GAAA,EAAK,UAAA,EAAY,eAAA,EAAgB;AACnD;AASO,SAAS,wBAAA,CACd,WACA,UAAA,EACQ;AACR,EAAA,IAAI,SAAA,CAAU,YAAA,IAAgB,SAAA,CAAU,KAAA,KAAU,MAAA,EAAW;AAC3D,IAAA,OAAO,eAAA,CAAgB,UAAA,EAAY,SAAA,CAAU,KAAK,CAAA;AAAA,EACpD;AACA,EAAA,OAAO,SAAA,CAAU,MAAA;AACnB;AAKO,SAAS,wBAAA,CACd,WACA,UAAA,EACQ;AACR,EAAA,IAAI,SAAA,CAAU,YAAA,IAAgB,SAAA,CAAU,KAAA,KAAU,MAAA,EAAW;AAC3D,IAAA,OAAO,eAAA,CAAgB,UAAA,EAAY,SAAA,CAAU,KAAK,CAAA;AAAA,EACpD;AACA,EAAA,OAAO,SAAA,CAAU,MAAA;AACnB;AAKO,SAAS,mBAAA,CACd,YACA,UAAA,EACiD;AACjD,EAAA,OAAO,UAAA,CAAW,GAAA,CAAI,CAAC,SAAA,MAAe;AAAA,IACpC,GAAG,SAAA;AAAA,IACH,gBAAA,EAAkB,wBAAA,CAAyB,SAAA,EAAW,UAAU;AAAA,GAClE,CAAE,CAAA;AACJ;AAKO,SAAS,mBAAA,CACd,YACA,UAAA,EACiD;AACjD,EAAA,OAAO,UAAA,CAAW,GAAA,CAAI,CAAC,SAAA,MAAe;AAAA,IACpC,GAAG,SAAA;AAAA,IACH,gBAAA,EAAkB,wBAAA,CAAyB,SAAA,EAAW,UAAU;AAAA,GAClE,CAAE,CAAA;AACJ;AASO,SAAS,+BACd,YAAA,EAC6B;AAC7B,EAAA,MAAM,EAAE,YAAY,UAAA,GAAa,IAAI,UAAA,GAAa,IAAG,GAAI,YAAA;AAEzD,EAAA,MAAM,oBAAA,GAAuB,mBAAA,CAAoB,UAAA,EAAY,UAAU,CAAA;AACvE,EAAA,MAAM,oBAAA,GAAuB,mBAAA,CAAoB,UAAA,EAAY,UAAU,CAAA;AAEvE,EAAA,MAAM,cACJ,UAAA,GAAa,KAAA,CAAM,sBAAsB,CAAC,CAAA,KAAM,EAAE,gBAAgB,CAAA;AACpE,EAAA,MAAM,YACJ,WAAA,GAAc,KAAA,CAAM,sBAAsB,CAAC,CAAA,KAAM,EAAE,gBAAgB,CAAA;AAErE,EAAA,OAAO;AAAA,IACL,UAAA;AAAA,IACA,UAAA,EAAY,oBAAA;AAAA,IACZ,UAAA,EAAY,oBAAA;AAAA,IACZ,WAAA;AAAA,IACA,SAAA,EAAW,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,SAAS;AAAA,GAClC;AACF;AAYO,SAAS,gBAAA,CACd,QACA,QAAA,EACQ;AACR,EAAA,IAAI,GAAA,GAAM,CAAA;AAEV,EAAA,KAAA,MAAW,WAAW,QAAA,EAAU;AAC9B,IAAA,IAAI,MAAA,GAAS,QAAQ,GAAA,EAAK;AACxB,MAAA,MAAM,gBAAgB,IAAA,CAAK,GAAA,CAAI,QAAQ,OAAA,CAAQ,GAAG,IAAI,OAAA,CAAQ,GAAA;AAC9D,MAAA,GAAA,IAAO,gBAAgB,OAAA,CAAQ,IAAA;AAAA,IACjC;AAAA,EACF;AAGA,EAAA,OAAO,WAAW,GAAG,CAAA;AACvB;AAKO,SAAS,YAAA,CACd,QACA,QAAA,EACsB;AACtB,EAAA,MAAM,GAAA,GAAM,gBAAA,CAAiB,MAAA,EAAQ,QAAQ,CAAA;AAC7C,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,MAAA;AAAA,IACP,GAAA;AAAA,IACA,KAAK,MAAA,GAAS;AAAA,GAChB;AACF;AASO,SAAS,iBAAA,CACd,UAAA,EACA,aAAA,EACA,UAAA,GAAa,GAAA,EACL;AACR,EAAA,OAAO,UAAA,CAAW,UAAA,GAAa,aAAA,GAAgB,UAAA,EAAY,CAAC,CAAA;AAC9D;AAKO,SAAS,mBAAA,CACd,aAAA,EACA,aAAA,GAAgB,GAAA,EACR;AACR,EAAA,OAAO,UAAA,CAAW,aAAA,GAAgB,aAAA,EAAe,CAAC,CAAA;AACpD;AAKO,SAAS,kBAAA,CACd,aAAA,EACA,YAAA,GAAe,EAAA,EACP;AACR,EAAA,OAAO,UAAA,CAAW,aAAA,GAAgB,YAAA,EAAc,CAAC,CAAA;AACnD;AAMA,IAAO,mBAAA,GAAQ;AAAA,EACb,GAAA;AAAA,EACA,KAAA;AAAA,EACA,aAAA;AAAA,EACA,aAAA;AAAA,EACA,eAAA;AAAA,EACA,mBAAA;AAAA,EACA,OAAA;AAAA,EACA,cAAA;AAAA,EACA,YAAA;AAAA,EACA,0BAAA;AAAA,EACA,wBAAA;AAAA,EACA,wBAAA;AAAA,EACA,mBAAA;AAAA,EACA,mBAAA;AAAA,EACA,8BAAA;AAAA,EACA,gBAAA;AAAA,EACA,YAAA;AAAA,EACA,iBAAA;AAAA,EACA,mBAAA;AAAA,EACA;AACF;;;ACjRO,IAAM,qBAAA,GAAsC;AAAA,EACjD,aAAa,CAAC,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,GAAG,CAAC,CAE7B,CAAA;AAgBO,SAAS,gBAAA,CACd,SAAA,EACA,OAAA,EACA,OAAA,GAGI,EAAC,EACc;AACnB,EAAA,MAAM,QAAA,GAAW,OAAA,CAAQ,WAAA,IAAe,qBAAA,CAAsB,WAAA;AAC9D,EAAA,MAAM,aAAa,IAAI,GAAA;AAAA,IAAA,CACpB,OAAA,CAAQ,YAAY,EAAC,EAAG,IAAI,CAAA,CAAA,KAAK,eAAA,CAAgB,CAAC,CAAC;AAAA,GACtD;AAEA,EAAA,IAAI,SAAA,GAAY,CAAA;AAChB,EAAA,IAAI,WAAA,GAAc,CAAA;AAClB,EAAA,IAAI,QAAA,GAAW,CAAA;AACf,EAAA,IAAI,QAAA,GAAW,CAAA;AAEf,EAAA,MAAM,OAAA,GAAU,IAAI,IAAA,CAAK,SAAS,CAAA;AAClC,EAAA,OAAA,CAAQ,QAAA,CAAS,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AAC3B,EAAA,MAAM,GAAA,GAAM,IAAI,IAAA,CAAK,OAAO,CAAA;AAC5B,EAAA,GAAA,CAAI,QAAA,CAAS,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AAEvB,EAAA,OAAO,WAAW,GAAA,EAAK;AACrB,IAAA,SAAA,EAAA;AACA,IAAA,MAAM,SAAA,GAAY,UAAA,CAAW,GAAA,CAAI,eAAA,CAAgB,OAAO,CAAC,CAAA;AACzD,IAAA,MAAM,SAAA,GAAY,QAAA,CAAS,QAAA,CAAS,OAAA,CAAQ,QAAQ,CAAA;AAEpD,IAAA,IAAI,SAAA,EAAW;AACb,MAAA,QAAA,EAAA;AAAA,IACF,WAAW,SAAA,EAAW;AACpB,MAAA,WAAA,EAAA;AAAA,IACF,CAAA,MAAO;AACL,MAAA,QAAA,EAAA;AAAA,IACF;AAEA,IAAA,OAAA,CAAQ,OAAA,CAAQ,OAAA,CAAQ,OAAA,EAAQ,GAAI,CAAC,CAAA;AAAA,EACvC;AAEA,EAAA,OAAO,EAAE,SAAA,EAAW,WAAA,EAAa,QAAA,EAAU,QAAA,EAAS;AACtD;;;AChBO,SAAS,mBAAmB,KAAA,EAAwC;AACzE,EAAA,MAAM,EAAE,UAAU,eAAA,EAAiB,WAAA,EAAa,WAAW,WAAA,EAAa,QAAA,GAAW,EAAC,EAAE,GAAI,KAAA;AAE1F,EAAA,MAAM,IAAA,GAAO,IAAI,IAAA,CAAK,QAAQ,CAAA;AAC9B,EAAA,MAAM,WAAA,GAAc,eAAA,GAAkB,IAAI,IAAA,CAAK,eAAe,CAAA,GAAI,IAAA;AAGlE,EAAA,MAAM,cAAA,GAAiB,IAAA,GAAO,WAAA,GAAc,IAAA,GAAO,WAAA;AACnD,EAAA,MAAM,YAAA,GAAe,WAAA,IAAe,WAAA,GAAc,SAAA,GAAY,WAAA,GAAc,SAAA;AAG5E,EAAA,IAAI,cAAA,GAAiB,SAAA,IAAc,WAAA,IAAe,WAAA,GAAc,WAAA,EAAc;AAC5E,IAAA,MAAMC,kBAAAA,GAAoB,iBAAiB,WAAA,EAAa,SAAA,EAAW,EAAE,WAAA,EAAa,QAAA,EAAU,CAAA,CAAE,WAAA;AAC9F,IAAA,OAAO;AAAA,MACL,UAAA,EAAY,IAAA;AAAA,MACZ,KAAA,EAAO,CAAA;AAAA,MACP,iBAAA,EAAAA,kBAAAA;AAAA,MACA,oBAAA,EAAsB,CAAA;AAAA,MACtB,cAAA,EAAgB,WAAA;AAAA,MAChB,YAAA,EAAc;AAAA;AAAA,KAChB;AAAA,EACF;AAGA,EAAA,MAAM,iBAAA,GAAoB,iBAAiB,WAAA,EAAa,SAAA,EAAW,EAAE,WAAA,EAAa,QAAA,EAAU,CAAA,CAAE,WAAA;AAG9F,EAAA,MAAM,oBAAA,GAAuB,iBAAiB,cAAA,EAAgB,YAAA,EAAc,EAAE,WAAA,EAAa,QAAA,EAAU,CAAA,CAAE,WAAA;AAGvG,EAAA,MAAM,KAAA,GAAQ,iBAAA,GAAoB,CAAA,GAC9B,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,oBAAA,GAAuB,iBAAiB,CAAC,CAAA,GACjE,CAAA;AAGJ,EAAA,MAAM,aAAa,KAAA,GAAQ,CAAA;AAE3B,EAAA,OAAO;AAAA,IACL,UAAA;AAAA,IACA,KAAA;AAAA,IACA,iBAAA;AAAA,IACA,oBAAA;AAAA,IACA,cAAA;AAAA,IACA;AAAA,GACF;AACF;AAgBO,SAAS,cAAA,CAAe,YAAoB,KAAA,EAAuB;AACxE,EAAA,OAAO,UAAA,CAAW,UAAA,GAAa,KAAA,EAAO,CAAC,CAAA;AACzC;;;ACxKO,IAAM,eAAA,GAAkB;AAAA,EAC7B,SAAA,EAAW,WAAA;AAAA,EACX,SAAA,EAAW,WAAA;AAAA,EACX,QAAA,EAAU,UAAA;AAAA,EACV,MAAA,EAAQ,QAAA;AAAA,EACR,UAAA,EAAY;AACd,CAAA;AAEO,IAAM,sBAAA,GAAyB,MAAA,CAAO,MAAA,CAAO,eAAe,CAAA;AAU5D,IAAM,eAAA,GAAkB;AAAA,EAC7B,MAAA,EAAQ,QAAA;AAAA,EACR,QAAA,EAAU,UAAA;AAAA,EACV,SAAA,EAAW,WAAA;AAAA,EACX,UAAA,EAAY;AACd,CAAA;AAEO,IAAM,sBAAA,GAAyB,MAAA,CAAO,MAAA,CAAO,eAAe,CAAA;;;ACtB5D,SAAS,SAASC,SAAAA,EAAgD;AACvE,EAAA,OAAOA,WAAU,MAAA,KAAW,QAAA;AAC9B;AAKO,SAAS,UAAUA,SAAAA,EAAgD;AACxE,EAAA,OAAOA,WAAU,MAAA,KAAW,UAAA;AAC9B;AAKO,SAAS,YAAYA,SAAAA,EAAgD;AAC1E,EAAA,OAAOA,WAAU,MAAA,KAAW,WAAA;AAC9B;AAKO,SAAS,aAAaA,SAAAA,EAAgD;AAC3E,EAAA,OAAOA,WAAU,MAAA,KAAW,YAAA;AAC9B;AAKO,SAAS,WAAWA,SAAAA,EAAgD;AACzE,EAAA,OACE,SAASA,SAAQ,CAAA,IAAK,UAAUA,SAAQ,CAAA,IAAK,YAAYA,SAAQ,CAAA;AAErE;AAKO,SAAS,iBAAiBA,SAAAA,EAGrB;AACV,EAAA,OAAA,CACG,QAAA,CAASA,SAAQ,CAAA,IAAK,SAAA,CAAUA,SAAQ,CAAA,KAAA,CACxCA,SAAAA,CAAS,YAAA,EAAc,UAAA,IAAc,CAAA,IAAK,CAAA;AAE/C;AAKO,SAAS,oBAAoBA,SAAAA,EAAgD;AAClF,EAAA,OAAO,CAAC,aAAaA,SAAQ,CAAA;AAC/B;AASO,SAAS,gBAAgBA,SAAAA,EAEpB;AACV,EAAA,OAAA,CAAQA,SAAAA,CAAS,YAAA,EAAc,UAAA,IAAc,CAAA,IAAK,CAAA;AACpD;AAKO,SAAS,oBAAoB,YAAA,EAAsC;AACxE,EAAA,OAAO,CAAC,EACN,YAAA,EAAc,UAAA,IACd,aAAa,UAAA,GAAa,CAAA,IAC1B,YAAA,CAAa,SAAA,IACb,YAAA,CAAa,QAAA,CAAA;AAEjB;AAKO,SAAS,mBAAmB,WAAA,EAAoC;AACrE,EAAA,OAAO,CAAC,EACN,WAAA,EAAa,aAAA,IACb,WAAA,CAAY,YACZ,WAAA,CAAY,WAAA,CAAA;AAEhB;AASO,SAAS,aAAA,CACdA,SAAAA,EACA,GAAA,mBAAM,IAAI,MAAK,EACN;AACT,EAAA,IAAI,CAACA,SAAAA,EAAU,gBAAA,EAAkB,OAAO,KAAA;AACxC,EAAA,OAAO,IAAI,IAAA,CAAKA,SAAAA,CAAS,gBAAgB,CAAA,GAAI,GAAA;AAC/C;AAKO,SAAS,qBAAA,CACdA,SAAAA,EACA,GAAA,mBAAM,IAAI,MAAK,EACN;AACT,EAAA,IAAI,CAACA,SAAAA,EAAU,gBAAA,EAAkB,OAAO,IAAA;AACxC,EAAA,OAAO,IAAI,IAAA,CAAKA,SAAAA,CAAS,gBAAgB,CAAA,IAAK,GAAA;AAChD;AASO,SAAS,kBAAA,CACdA,SAAAA,EACA,cAAA,GAAiB,CAAA,EACR;AACT,EAAA,IAAI,CAAC,QAAA,CAASA,SAAQ,KAAK,CAACA,SAAAA,CAAS,UAAU,OAAO,KAAA;AACtD,EAAA,MAAM,iBAAiB,YAAA,CAAaA,SAAAA,CAAS,QAAA,kBAAU,IAAI,MAAM,CAAA;AACjE,EAAA,OAAO,cAAA,IAAkB,cAAA;AAC3B;AAKO,SAAS,qBAAqBA,SAAAA,EAIQ;AAC3C,EAAA,MAAM,UAAoB,EAAC;AAE3B,EAAA,IAAI,CAAC,QAAA,CAASA,SAAQ,KAAK,CAAC,SAAA,CAAUA,SAAQ,CAAA,EAAG;AAC/C,IAAA,OAAA,CAAQ,KAAK,8CAA8C,CAAA;AAAA,EAC7D;AAEA,EAAA,IAAI,CAAC,eAAA,CAAgBA,SAAQ,CAAA,EAAG;AAC9B,IAAA,OAAA,CAAQ,KAAK,oCAAoC,CAAA;AAAA,EACnD;AAEA,EAAA,OAAO;AAAA,IACL,QAAA,EAAU,QAAQ,MAAA,KAAW,CAAA;AAAA,IAC7B;AAAA,GACF;AACF;AASO,SAAS,SAAS,SAAA,EAAsD;AAC7E,EAAA,OAAO,CAAC,KAAA,KACN,KAAA,KAAU,MAAA,IAAa,KAAA,KAAU,QAAQ,KAAA,KAAU,EAAA,GAC/C,IAAA,GACA,CAAA,EAAG,SAAS,CAAA,YAAA,CAAA;AACpB;AAKO,SAAS,GAAA,CACdC,WACA,SAAA,EACkC;AAClC,EAAA,OAAO,CAAC,UACN,KAAA,IAASA,SAAAA,GAAW,OAAO,CAAA,EAAG,SAAS,qBAAqBA,SAAQ,CAAA,CAAA;AACxE;AAKO,SAAS,GAAA,CACdC,WACA,SAAA,EACkC;AAClC,EAAA,OAAO,CAAC,UACN,KAAA,IAASA,SAAAA,GAAW,OAAO,CAAA,EAAG,SAAS,oBAAoBA,SAAQ,CAAA,CAAA;AACvE;AAKO,SAAS,OAAA,CACdD,SAAAA,EACAC,SAAAA,EACA,SAAA,EACkC;AAClC,EAAA,OAAO,CAAC,KAAA,KACN,KAAA,IAASD,SAAAA,IAAY,KAAA,IAASC,SAAAA,GAC1B,IAAA,GACA,CAAA,EAAG,SAAS,CAAA,iBAAA,EAAoBD,SAAQ,CAAA,KAAA,EAAQC,SAAQ,CAAA,CAAA;AAChE;AAKO,SAAS,WAAW,SAAA,EAAqD;AAC9E,EAAA,OAAO,CAAC,KAAA,KACN,KAAA,GAAQ,CAAA,GAAI,IAAA,GAAO,GAAG,SAAS,CAAA,iBAAA,CAAA;AACnC;AAKO,SAAS,KAAA,CACd,eACA,SAAA,EAC6B;AAC7B,EAAA,OAAO,CAAC,KAAA,KACN,aAAA,CAAc,QAAA,CAAS,KAAK,CAAA,GACxB,IAAA,GACA,CAAA,EAAG,SAAS,CAAA,iBAAA,EAAoB,aAAA,CAAc,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA;AAChE;AASO,SAAS,cAAc,KAAA,EAAwC;AACpE,EAAA,OAAO,sBAAA,CAAuB,SAAS,KAAuB,CAAA;AAChE;AAKO,SAAS,sBAAsB,KAAA,EAAwC;AAC5E,EAAA,OAAO,sBAAA,CAAuB,SAAS,KAAuB,CAAA;AAChE;AASO,SAAS,qBACX,UAAA,EAC0C;AAC7C,EAAA,OAAO,CAAC,OAAU,IAAA,KAAmB;AACnC,IAAA,KAAA,MAAW,aAAa,UAAA,EAAY;AAClC,MAAA,MAAM,MAAA,GAAS,SAAA,CAAU,KAAA,EAAO,IAAI,CAAA;AACpC,MAAA,IAAI,MAAA,KAAW,MAAM,OAAO,MAAA;AAAA,IAC9B;AACA,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AACF;AAKO,SAAS,gBACd,aAAA,EACuC;AACvC,EAAA,OAAO,CAAC,IAAA,KAAY;AAClB,IAAA,MAAM,SAAmB,EAAC;AAE1B,IAAA,KAAA,MAAW,CAAC,KAAA,EAAO,SAAS,KAAK,MAAA,CAAO,OAAA,CAAQ,aAAa,CAAA,EAAG;AAC9D,MAAA,MAAM,MAAA,GAAS,SAAA,CAAW,IAAA,CAAiC,KAAK,GAAG,IAAI,CAAA;AACvE,MAAA,IAAI,WAAW,IAAA,EAAM;AACnB,QAAA,MAAA,CAAO,KAAK,MAAM,CAAA;AAAA,MACpB;AAAA,IACF;AAEA,IAAA,OAAO;AAAA,MACL,KAAA,EAAO,OAAO,MAAA,KAAW,CAAA;AAAA,MACzB;AAAA,KACF;AAAA,EACF,CAAA;AACF;AAKO,SAAS,iBAAA,CACd,KACA,MAAA,EACuC;AACvC,EAAA,MAAM,UAAU,MAAA,CAAO,MAAA;AAAA,IACrB,CAAC,UAAU,GAAA,CAAI,KAAK,MAAM,MAAA,IAAa,GAAA,CAAI,KAAK,CAAA,KAAM;AAAA,GACxD;AACA,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,QAAQ,MAAA,KAAW,CAAA;AAAA,IAC1B;AAAA,GACF;AACF;AAoBO,SAAS,eAAA,CAAgB,KAAc,MAAA,EAAyB;AACrE,EAAA,OAAO,OAAO,QAAQ,QAAA,IAAY,GAAA,KAAQ,QAAQ,OAAQ,GAAA,CAAgC,MAAM,CAAA,KAAM,UAAA;AACxG;AAUO,SAAS,kBAAA,CAAmB,GAAA,EAAc,MAAA,EAAgB,OAAA,EAAuB;AACtF,EAAA,IAAI,CAAC,eAAA,CAAgB,GAAA,EAAK,MAAM,CAAA,EAAG;AACjC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,QAAA,EAAW,MAAM,CAAA,gFAAA,EACL,OAAO,CAAA;AAAA,KACrB;AAAA,EACF;AACF;AAMO,IAAM,QAAA,GAAW;AACjB,IAAM,QAAA,GAAW;AACjB,IAAM,SAAA,GAAY;AAMzB,IAAO,kBAAA,GAAQ;AAAA;AAAA,EAEb,QAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA,YAAA;AAAA,EACA,UAAA;AAAA,EACA,gBAAA;AAAA,EACA,mBAAA;AAAA;AAAA,EAEA,eAAA;AAAA,EACA,mBAAA;AAAA,EACA,kBAAA;AAAA;AAAA,EAEA,aAAA;AAAA,EACA,qBAAA;AAAA;AAAA,EAEA,kBAAA;AAAA,EACA,oBAAA;AAAA;AAAA,EAEA,QAAA;AAAA,EACA,GAAA;AAAA,EACA,GAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA;AAAA,EACA,KAAA;AAAA;AAAA,EAEA,aAAA;AAAA,EACA,qBAAA;AAAA;AAAA,EAEA,iBAAA;AAAA,EACA,eAAA;AAAA,EACA,iBAAA;AAAA;AAAA,EAEA,eAAA;AAAA,EACA;AACF;AC1YO,SAAS,WAAW,EAAA,EAAkC;AAC3D,EAAA,IAAI,EAAA,YAAc,KAAA,CAAM,QAAA,EAAU,OAAO,EAAA;AACzC,EAAA,OAAO,IAAI,KAAA,CAAM,QAAA,CAAS,EAAE,CAAA;AAC9B;AAKO,SAAS,eAAe,EAAA,EAAoC;AACjE,EAAA,IAAI,EAAA,YAAc,KAAA,CAAM,QAAA,EAAU,OAAO,EAAA;AACzC,EAAA,IAAI,OAAO,EAAA,KAAO,QAAA,IAAY,MAAM,QAAA,CAAS,OAAA,CAAQ,EAAE,CAAA,EAAG;AACxD,IAAA,OAAO,IAAI,KAAA,CAAM,QAAA,CAAS,EAAE,CAAA;AAAA,EAC9B;AACA,EAAA,OAAO,IAAA;AACT;AAKO,SAAS,gBAAgB,KAAA,EAAyB;AACvD,EAAA,IAAI,KAAA,YAAiB,KAAA,CAAM,QAAA,EAAU,OAAO,IAAA;AAC5C,EAAA,IAAI,OAAO,KAAA,KAAU,QAAA,SAAiB,KAAA,CAAM,QAAA,CAAS,QAAQ,KAAK,CAAA;AAClE,EAAA,OAAO,KAAA;AACT;AAMO,IAAM,eAAN,MAAgF;AAAA,EAC3E,KAAA;AAAA,EAEV,WAAA,CAAY,YAAA,GAAkB,EAAC,EAAQ;AACrC,IAAA,IAAA,CAAK,KAAA,GAAQ,EAAE,GAAG,YAAA,EAAa;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,KAAA,CAAwB,OAAU,KAAA,EAAsB;AACtD,IAAC,IAAA,CAAK,KAAA,CAAkC,KAAK,CAAA,GAAI,KAAA;AACjD,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,OAAA,CAA0B,OAAU,MAAA,EAAyB;AAC3D,IAAC,KAAK,KAAA,CAAkC,KAAK,CAAA,GAAI,EAAE,KAAK,MAAA,EAAO;AAC/D,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,UAAA,CAA6B,OAAU,MAAA,EAAyB;AAC9D,IAAC,KAAK,KAAA,CAAkC,KAAK,CAAA,GAAI,EAAE,MAAM,MAAA,EAAO;AAChE,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,QAAA,CAA2B,OAAU,KAAA,EAAsB;AACzD,IAAA,MAAM,QAAA,GAAY,IAAA,CAAK,KAAA,CAAkD,KAAK,KAAK,EAAC;AACpF,IAAC,IAAA,CAAK,MAAkC,KAAK,CAAA,GAAI,EAAE,GAAG,QAAA,EAAU,MAAM,KAAA,EAAM;AAC5E,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,QAAA,CAA2B,OAAU,KAAA,EAAsB;AACzD,IAAA,MAAM,QAAA,GAAY,IAAA,CAAK,KAAA,CAAkD,KAAK,KAAK,EAAC;AACpF,IAAC,IAAA,CAAK,MAAkC,KAAK,CAAA,GAAI,EAAE,GAAG,QAAA,EAAU,MAAM,KAAA,EAAM;AAC5E,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,OAAA,CAA0B,OAAU,KAAA,EAAsB;AACxD,IAAA,MAAM,QAAA,GAAY,IAAA,CAAK,KAAA,CAAkD,KAAK,KAAK,EAAC;AACpF,IAAC,IAAA,CAAK,MAAkC,KAAK,CAAA,GAAI,EAAE,GAAG,QAAA,EAAU,KAAK,KAAA,EAAM;AAC3E,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,OAAA,CAA0B,OAAU,KAAA,EAAsB;AACxD,IAAA,MAAM,QAAA,GAAY,IAAA,CAAK,KAAA,CAAkD,KAAK,KAAK,EAAC;AACpF,IAAC,IAAA,CAAK,MAAkC,KAAK,CAAA,GAAI,EAAE,GAAG,QAAA,EAAU,KAAK,KAAA,EAAM;AAC3E,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,YAAA,CAA+B,KAAA,EAAU,KAAA,EAAgB,GAAA,EAAoB;AAC3E,IAAC,IAAA,CAAK,MAAkC,KAAK,CAAA,GAAI,EAAE,IAAA,EAAM,KAAA,EAAO,MAAM,GAAA,EAAI;AAC1E,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,YAA8B,KAAA,EAAgB;AAC5C,IAAC,KAAK,KAAA,CAAkC,KAAK,CAAA,GAAI,EAAE,SAAS,IAAA,EAAK;AACjE,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,eAAiC,KAAA,EAAgB;AAC/C,IAAC,KAAK,KAAA,CAAkC,KAAK,CAAA,GAAI,EAAE,SAAS,KAAA,EAAM;AAClE,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,QAAA,CAA2B,OAAU,KAAA,EAAsB;AACzD,IAAC,KAAK,KAAA,CAAkC,KAAK,CAAA,GAAI,EAAE,KAAK,KAAA,EAAM;AAC9D,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,UAAA,CAA6B,KAAA,EAAU,OAAA,EAAiB,KAAA,GAAQ,GAAA,EAAW;AACzE,IAAC,IAAA,CAAK,MAAkC,KAAK,CAAA,GAAI,EAAE,MAAA,EAAQ,OAAA,EAAS,UAAU,KAAA,EAAM;AACpF,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAA,EAA2C;AAC/C,IAAA,IAAA,CAAK,QAAQ,EAAE,GAAG,IAAA,CAAK,KAAA,EAAO,GAAG,UAAA,EAAW;AAC5C,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,KAAA,GAAW;AACT,IAAA,OAAO,EAAE,GAAG,IAAA,CAAK,KAAA,EAAM;AAAA,EACzB;AACF;AAMO,IAAM,oBAAA,GAAN,cAAmC,YAAA,CAAa;AAAA;AAAA;AAAA;AAAA,EAIrD,gBAAgB,cAAA,EAAoC;AAClD,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,gBAAA,EAAkB,UAAA,CAAW,cAAc,CAAC,CAAA;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA,EAKA,QAAQ,MAAA,EAA4B;AAClC,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,QAAA,EAAU,UAAA,CAAW,MAAM,CAAC,CAAA;AAAA,EAChD;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,UAAA,EAA0B;AACtC,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,YAAA,EAAc,UAAU,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKA,SAAS,KAAA,EAAqB;AAC5B,IAAA,OAAO,KAAK,KAAA,CAAM,OAAA,EAAS,MAAM,WAAA,EAAY,CAAE,MAAM,CAAA;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA,EAKA,cAAA,GAAuB;AACrB,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,QAAA,EAAU,IAAI,CAAA;AAAA,EAClC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,mBAAA,GAA4B;AAC1B,IAAA,OAAO,IAAA,CAAK,MAAM,QAAA,EAAU,EAAE,SAAS,IAAA,EAAM,GAAA,EAAK,MAA4C,CAAA;AAAA,EAChG;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,QAAA,EAAkC;AAC9C,IAAA,IAAI,QAAA,CAAS,WAAW,CAAA,EAAG;AACzB,MAAA,OAAO,IAAA,CAAK,KAAA,CAAM,QAAA,EAAU,QAAA,CAAS,CAAC,CAAC,CAAA;AAAA,IACzC;AACA,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,QAAA,EAAU,QAAQ,CAAA;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,MAAA,GAAe;AACb,IAAA,OAAO,IAAA,CAAK,WAAW,QAAQ,CAAA;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,QAAA,GAAiB;AACf,IAAA,OAAO,KAAK,OAAA,CAAQ,QAAA,EAAU,CAAC,QAAA,EAAU,UAAA,EAAY,WAAW,CAAC,CAAA;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA,EAKA,UAAA,GAAmB;AACjB,IAAA,OAAO,IAAA,CAAK,WAAW,YAAY,CAAA;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKA,aAAa,UAAA,EAAuC;AAClD,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,YAAA,EAAc,UAAU,CAAA;AAAA,EAC5C;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,QAAA,EAAwB;AACjC,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,UAAA,EAAY,QAAQ,CAAA;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,mBAAmB,IAAA,EAAqC;AACtD,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,gBAAA,EAAkB,IAAI,CAAA;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKA,WAAW,IAAA,EAAkB;AAC3B,IAAA,OAAO,IAAA,CAAK,QAAA,CAAS,UAAA,EAAY,IAAI,CAAA;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,IAAA,EAAkB;AAC5B,IAAA,OAAO,IAAA,CAAK,QAAA,CAAS,UAAA,EAAY,IAAI,CAAA;AAAA,EACvC;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,MAAA,EAAsB;AAClC,IAAA,OAAO,IAAA,CAAK,QAAA,CAAS,wBAAA,EAA0B,MAAM,CAAA;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,MAAA,EAAsB;AAClC,IAAA,OAAO,IAAA,CAAK,QAAA,CAAS,wBAAA,EAA0B,MAAM,CAAA;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA,EAKA,eAAA,CAAgBC,MAAaC,IAAAA,EAAmB;AAC9C,IAAA,OAAO,IAAA,CAAK,YAAA,CAAa,wBAAA,EAA0BD,IAAAA,EAAKC,IAAG,CAAA;AAAA,EAC7D;AACF;AAMO,IAAM,mBAAA,GAAN,cAAkC,YAAA,CAAa;AAAA;AAAA;AAAA;AAAA,EAIpD,gBAAgB,cAAA,EAAoC;AAClD,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,gBAAA,EAAkB,UAAA,CAAW,cAAc,CAAC,CAAA;AAAA,EAChE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,YAAY,UAAA,EAAyC;AAGnD,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,YAAA,EAAc,UAAA,CAAW,UAA0B,CAAC,CAAA;AAAA,EACxE;AAAA;AAAA;AAAA;AAAA,EAKA,SAAA,CAAU,OAAgB,IAAA,EAAqB;AAC7C,IAAA,IAAI,UAAU,MAAA,EAAW;AACvB,MAAA,IAAA,CAAK,KAAA,CAAM,gBAAgB,KAAK,CAAA;AAAA,IAClC;AACA,IAAA,IAAI,SAAS,MAAA,EAAW;AACtB,MAAA,IAAA,CAAK,KAAA,CAAM,eAAe,IAAI,CAAA;AAAA,IAChC;AACA,IAAA,OAAO,IAAA;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,cAAc,QAAA,EAAiC;AAC7C,IAAA,IAAI,QAAA,CAAS,WAAW,CAAA,EAAG;AACzB,MAAA,OAAO,IAAA,CAAK,KAAA,CAAM,QAAA,EAAU,QAAA,CAAS,CAAC,CAAC,CAAA;AAAA,IACzC;AACA,IAAA,OAAO,IAAA,CAAK,OAAA,CAAQ,QAAA,EAAU,QAAQ,CAAA;AAAA,EACxC;AAAA;AAAA;AAAA;AAAA,EAKA,IAAA,GAAa;AACX,IAAA,OAAO,IAAA,CAAK,WAAW,MAAM,CAAA;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKA,OAAA,GAAgB;AACd,IAAA,OAAO,KAAK,OAAA,CAAQ,QAAA,EAAU,CAAC,SAAA,EAAW,YAAY,CAAC,CAAA;AAAA,EACzD;AAAA;AAAA;AAAA;AAAA,EAKA,WAAA,CAAY,OAAa,GAAA,EAAiB;AACxC,IAAA,OAAO,IAAA,CAAK,YAAA,CAAa,gBAAA,EAAkB,KAAA,EAAO,GAAG,CAAA;AAAA,EACvD;AAAA;AAAA;AAAA;AAAA,EAKA,QAAA,GAAiB;AACf,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,UAAA,EAAY,IAAI,CAAA;AAAA,EACpC;AAAA;AAAA;AAAA;AAAA,EAKA,WAAA,GAAoB;AAClB,IAAA,OAAO,IAAA,CAAK,KAAA,CAAM,UAAA,EAAY,KAAK,CAAA;AAAA,EACrC;AACF;AASO,SAAS,QAAA,GAAiC;AAC/C,EAAA,OAAO,IAAI,oBAAA,EAAqB;AAClC;AAKO,SAAS,OAAA,GAA+B;AAC7C,EAAA,OAAO,IAAI,mBAAA,EAAoB;AACjC;AAKO,SAAS,mBACd,YAAA,EACiB;AACjB,EAAA,OAAO,IAAI,aAAa,YAAY,CAAA;AACtC;AASO,SAAS,mBAAmB,OAAA,EAMP;AAC1B,EAAA,MAAM,OAAA,GAAU,QAAA,EAAS,CAAE,eAAA,CAAgB,QAAQ,cAAc,CAAA;AAEjE,EAAA,IAAI,QAAQ,MAAA,EAAQ;AAClB,IAAA,OAAA,CAAQ,OAAA,CAAQ,QAAQ,MAAM,CAAA;AAAA,EAChC;AACA,EAAA,IAAI,QAAQ,QAAA,EAAU;AACpB,IAAA,OAAA,CAAQ,UAAA,CAAW,GAAG,OAAA,CAAQ,QAAQ,CAAA;AAAA,EACxC;AACA,EAAA,IAAI,QAAQ,UAAA,EAAY;AACtB,IAAA,OAAA,CAAQ,YAAA,CAAa,QAAQ,UAAU,CAAA;AAAA,EACzC;AACA,EAAA,IAAI,QAAQ,cAAA,EAAgB;AAC1B,IAAA,OAAA,CAAQ,kBAAA,CAAmB,QAAQ,cAAc,CAAA;AAAA,EACnD;AAEA,EAAA,OAAO,QAAQ,KAAA,EAAM;AACvB;AAKO,SAAS,kBAAkB,OAAA,EAMN;AAC1B,EAAA,MAAM,UAAU,OAAA,EAAQ;AAExB,EAAA,IAAI,QAAQ,cAAA,EAAgB;AAC1B,IAAA,OAAA,CAAQ,eAAA,CAAgB,QAAQ,cAAc,CAAA;AAAA,EAChD;AACA,EAAA,IAAI,QAAQ,UAAA,EAAY;AACtB,IAAA,OAAA,CAAQ,WAAA,CAAY,QAAQ,UAAU,CAAA;AAAA,EACxC;AACA,EAAA,IAAI,OAAA,CAAQ,KAAA,IAAS,OAAA,CAAQ,IAAA,EAAM;AACjC,IAAA,OAAA,CAAQ,SAAA,CAAU,OAAA,CAAQ,KAAA,EAAO,OAAA,CAAQ,IAAI,CAAA;AAAA,EAC/C;AACA,EAAA,IAAI,QAAQ,QAAA,EAAU;AACpB,IAAA,OAAA,CAAQ,UAAA,CAAW,GAAG,OAAA,CAAQ,QAAQ,CAAA;AAAA,EACxC;AAEA,EAAA,OAAO,QAAQ,KAAA,EAAM;AACvB;AASO,SAAS,4BACX,MAAA,EACwB;AAC3B,EAAA,OAAO,OAAO,MAAA,CAAO,CAAC,KAAA,KAA4C,CAAC,CAAC,KAAK,CAAA;AAC3E;AAKO,SAAS,WAAW,KAAA,EAAyD;AAClF,EAAA,OAAO,EAAE,QAAQ,KAAA,EAAM;AACzB;AAKO,SAAS,UAAA,CACd,SACA,YAAA,EACyB;AACzB,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ;AAAA,MACN,GAAA,EAAK,OAAA;AAAA,MACL,GAAG;AAAA;AACL,GACF;AACF;AAKO,SAAS,UAAU,MAAA,EAAyD;AACjF,EAAA,OAAO,EAAE,OAAO,MAAA,EAAO;AACzB;AAKO,SAAS,WAAW,KAAA,EAAwC;AACjE,EAAA,OAAO,EAAE,QAAQ,KAAA,EAAM;AACzB;AAKO,SAAS,UAAU,IAAA,EAAuC;AAC/D,EAAA,OAAO,EAAE,OAAO,IAAA,EAAK;AACvB;AAKO,SAAS,aAAa,MAAA,EAA0D;AACrF,EAAA,OAAO,EAAE,UAAU,MAAA,EAAO;AAC5B;AAKO,SAAS,YAAY,OAAA,EAKA;AAC1B,EAAA,OAAO,EAAE,SAAS,OAAA,EAAQ;AAC5B;AAKO,SAAS,WAAA,CACd,IAAA,EACA,OAAA,GAAoD,EAAC,EAC5B;AACzB,EAAA,OAAO,EAAE,OAAA,EAAS,EAAE,IAAA,EAAM,GAAG,SAAQ,EAAE;AACzC;AAMA,IAAO,sBAAA,GAAQ;AAAA,EACb,UAAA;AAAA,EACA,cAAA;AAAA,EACA,eAAA;AAAA,EACA,YAAA;AAAA,EACA,oBAAA;AAAA,EACA,mBAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,kBAAA;AAAA,EACA,kBAAA;AAAA,EACA,iBAAA;AAAA,EACA,wBAAA;AAAA,EACA,UAAA;AAAA,EACA,UAAA;AAAA,EACA,SAAA;AAAA,EACA,UAAA;AAAA,EACA,SAAA;AAAA,EACA,YAAA;AAAA,EACA,WAAA;AAAA,EACA;AACF;;;AC1jBO,IAAM,yBAAA,GAAuD;AAAA,EAClE,MAAA,EAAQ,EAAA;AAAA,EACR,IAAA,EAAM,EAAA;AAAA,EACN,MAAA,EAAQ,CAAA;AAAA;AAAA,EACR,SAAA,EAAW,EAAA;AAAA,EACX,SAAA,EAAW,EAAA;AAAA,EACX,WAAA,EAAa,CAAA;AAAA,EACb,YAAA,EAAc,CAAA;AAAA,EACd,KAAA,EAAO;AACT;AAGO,IAAM,kBAAA,GAAgD;AAAA,EAC3D,MAAA,EAAQ,CAAA;AAAA,EACR,IAAA,EAAM,CAAA;AAAA,EACN,MAAA,EAAQ,CAAA;AAAA,EACR,SAAA,EAAW,CAAA;AAAA,EACX,SAAA,EAAW,CAAA;AAAA,EACX,WAAA,EAAa,CAAA;AAAA,EACb,YAAA,EAAc,CAAA;AAAA,EACd,KAAA,EAAO;AACT;AAkBO,SAAS,kBAAA,CACd,SAAA,EACA,OAAA,EACA,OAAA,GAA8B,EAAC,EACvB;AACR,EAAA,MAAM;AAAA,IACJ,cAAc,CAAC,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,GAAG,CAAC,CAAA;AAAA;AAAA,IAC5B,WAAW,EAAC;AAAA,IACZ,cAAA,GAAiB;AAAA,GACnB,GAAI,OAAA;AAEJ,EAAA,MAAM,UAAA,GAAa,IAAI,GAAA,CAAI,QAAA,CAAS,GAAA,CAAI,CAAC,CAAA,KAAM,eAAA,CAAgB,CAAC,CAAC,CAAC,CAAA;AAElE,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,MAAM,OAAA,GAAU,IAAI,IAAA,CAAK,SAAS,CAAA;AAClC,EAAA,OAAA,CAAQ,QAAA,CAAS,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AAE3B,EAAA,MAAM,GAAA,GAAM,IAAI,IAAA,CAAK,OAAO,CAAA;AAC5B,EAAA,GAAA,CAAI,QAAA,CAAS,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAC,CAAA;AAEvB,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAA,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,OAAA,EAAQ,GAAI,CAAC,CAAA;AAAA,EAC/B;AAEA,EAAA,OAAO,WAAW,GAAA,EAAK;AACrB,IAAA,MAAM,SAAA,GAAY,WAAA,CAAY,QAAA,CAAS,OAAA,CAAQ,QAAQ,CAAA;AACvD,IAAA,MAAM,SAAA,GAAY,UAAA,CAAW,GAAA,CAAI,eAAA,CAAgB,OAAO,CAAC,CAAA;AAEzD,IAAA,IAAI,SAAA,IAAa,CAAC,SAAA,EAAW;AAC3B,MAAA,KAAA,EAAA;AAAA,IACF;AAEA,IAAA,OAAA,CAAQ,OAAA,CAAQ,OAAA,CAAQ,OAAA,EAAQ,GAAI,CAAC,CAAA;AAAA,EACvC;AAEA,EAAA,OAAO,KAAA;AACT;AAcO,SAAS,eAAA,CACdJ,WACA,IAAA,EACA,IAAA,EACA,wBAAO,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY,EACrB;AAET,EAAA,IAAI,IAAA,KAAS,UAAU,OAAO,IAAA;AAE9B,EAAA,MAAM,OAAA,GAAU,eAAA,CAAgBA,SAAAA,EAAU,IAAA,EAAM,IAAI,CAAA;AACpD,EAAA,IAAI,CAAC,SAAS,OAAO,KAAA;AAErB,EAAA,MAAM,YACJ,OAAA,CAAQ,SAAA,GAAY,QAAQ,WAAA,GAAc,OAAA,CAAQ,OAAO,OAAA,CAAQ,OAAA;AACnE,EAAA,OAAO,SAAA,IAAa,IAAA;AACtB;AAKO,SAAS,eAAA,CACdA,WACA,IAAA,EACA,IAAA,GAAA,qBAAW,IAAA,EAAK,EAAE,aAAY,EACJ;AAC1B,EAAA,OAAOA,SAAAA,CAAS,aAAA,EAAe,IAAA,CAAK,CAAC,CAAA,KAAM,EAAE,IAAA,KAAS,IAAA,IAAQ,CAAA,CAAE,IAAA,KAAS,IAAI,CAAA;AAC/E;AAKO,SAAS,iBACdA,SAAAA,EACA,IAAA,GAAA,qBAAW,IAAA,EAAK,EAAE,aAAY,EACd;AAChB,EAAA,OAAA,CAAQA,SAAAA,CAAS,iBAAiB,EAAC,EAAG,OAAO,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,IAAI,CAAA;AACrE;AAKO,SAAS,gBAAA,CACdA,WACA,IAAA,EACA,IAAA,GAAA,qBAAW,IAAA,EAAK,EAAE,aAAY,EACtB;AAER,EAAA,IAAI,IAAA,KAAS,UAAU,OAAO,QAAA;AAE9B,EAAA,MAAM,OAAA,GAAU,eAAA,CAAgBA,SAAAA,EAAU,IAAA,EAAM,IAAI,CAAA;AACpD,EAAA,IAAI,CAAC,SAAS,OAAO,CAAA;AAErB,EAAA,OAAO,IAAA,CAAK,GAAA;AAAA,IACV,CAAA;AAAA,IACA,QAAQ,SAAA,GAAY,OAAA,CAAQ,WAAA,GAAc,OAAA,CAAQ,OAAO,OAAA,CAAQ;AAAA,GACnE;AACF;AAcO,SAAS,gBACdA,SAAAA,EACA,IAAA,GAAA,qBAAW,IAAA,EAAK,EAAE,aAAY,EACV;AACpB,EAAA,MAAM,QAAA,GAAW,gBAAA,CAAiBA,SAAAA,EAAU,IAAI,CAAA;AAEhD,EAAA,MAAM,SAAS,EAAC;AAChB,EAAA,IAAI,cAAA,GAAiB,CAAA;AACrB,EAAA,IAAI,SAAA,GAAY,CAAA;AAChB,EAAA,IAAI,YAAA,GAAe,CAAA;AAEnB,EAAA,KAAA,MAAW,WAAW,QAAA,EAAU;AAC9B,IAAA,MAAM,YAAY,IAAA,CAAK,GAAA;AAAA,MACrB,CAAA;AAAA,MACA,QAAQ,SAAA,GAAY,OAAA,CAAQ,WAAA,GAAc,OAAA,CAAQ,OAAO,OAAA,CAAQ;AAAA,KACnE;AACA,IAAA,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA,GAAI;AAAA,MACrB,SAAA,EAAW,OAAA,CAAQ,SAAA,GAAY,OAAA,CAAQ,WAAA;AAAA,MACvC,MAAM,OAAA,CAAQ,IAAA;AAAA,MACd,SAAS,OAAA,CAAQ,OAAA;AAAA,MACjB;AAAA,KACF;AACA,IAAA,cAAA,IAAkB,OAAA,CAAQ,YAAY,OAAA,CAAQ,WAAA;AAC9C,IAAA,SAAA,IAAa,OAAA,CAAQ,IAAA;AACrB,IAAA,YAAA,IAAgB,OAAA,CAAQ,OAAA;AAAA,EAC1B;AAEA,EAAA,OAAO;AAAA,IACL,IAAA;AAAA,IACA,QAAA;AAAA,IACA,cAAA;AAAA,IACA,SAAA;AAAA,IACA,YAAA;AAAA,IACA,gBAAgB,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,cAAA,GAAiB,YAAY,YAAY,CAAA;AAAA,IACrE;AAAA,GACF;AACF;AAkBO,SAAS,uBAAA,CACd,QAAA,EACA,MAAA,GAA0B,EAAC,EAC3B,wBAAO,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY,EACd;AAChB,EAAA,MAAM;AAAA,IACJ,kBAAA,GAAqB,yBAAA;AAAA,IACrB,eAAA,GAAkB,IAAA;AAAA,IAClB,oBAAA,GAAuB;AAAA,GACzB,GAAI,MAAA;AAEJ,EAAA,MAAM,kBAAkB,IAAI,IAAA,CAAK,IAAA,EAAM,oBAAA,GAAuB,GAAG,CAAC,CAAA;AAClE,EAAA,MAAM,gBAAgB,IAAI,IAAA,CAAK,OAAO,CAAA,EAAG,oBAAA,GAAuB,GAAG,CAAC,CAAA;AAGpE,EAAA,IAAI,cAAA,GAAiB,CAAA;AACrB,EAAA,IAAI,eAAA,IAAmB,WAAW,eAAA,EAAiB;AACjD,IAAA,MAAM,SAAA,GAAY,UAAA,CAAW,eAAA,EAAiB,aAAa,CAAA;AAC3D,IAAA,MAAM,aAAA,GAAgB,UAAA,CAAW,QAAA,EAAU,aAAa,CAAA;AACxD,IAAA,cAAA,GAAiB,IAAA,CAAK,IAAI,CAAA,EAAG,IAAA,CAAK,IAAI,CAAA,EAAG,aAAA,GAAgB,SAAS,CAAC,CAAA;AAAA,EACrE;AAEA,EAAA,MAAM,WAA2B,EAAC;AAElC,EAAA,KAAA,MAAW,CAAC,IAAA,EAAM,UAAU,KAAK,MAAA,CAAO,OAAA,CAAQ,kBAAkB,CAAA,EAAG;AACnE,IAAA,IAAI,aAAa,CAAA,EAAG;AAClB,MAAA,QAAA,CAAS,IAAA,CAAK;AAAA,QACZ,IAAA;AAAA,QACA,SAAA,EAAW,IAAA,CAAK,KAAA,CAAM,UAAA,GAAa,cAAc,CAAA;AAAA,QACjD,IAAA,EAAM,CAAA;AAAA,QACN,OAAA,EAAS,CAAA;AAAA,QACT,WAAA,EAAa,CAAA;AAAA,QACb;AAAA,OACD,CAAA;AAAA,IACH;AAAA,EACF;AAEA,EAAA,OAAO,QAAA;AACT;AAKO,SAAS,iBAAA,CACd,cAAA,EACA,QAAA,EACA,oBAAA,GAAuB,CAAA,EACvB,wBAAO,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY,EACtB;AACR,EAAA,MAAM,kBAAkB,IAAI,IAAA,CAAK,IAAA,EAAM,oBAAA,GAAuB,GAAG,CAAC,CAAA;AAElE,EAAA,IAAI,YAAY,eAAA,EAAiB;AAC/B,IAAA,OAAO,cAAA;AAAA,EACT;AAEA,EAAA,MAAM,gBAAgB,IAAI,IAAA,CAAK,OAAO,CAAA,EAAG,oBAAA,GAAuB,GAAG,CAAC,CAAA;AACpE,EAAA,MAAM,SAAA,GAAY,UAAA,CAAW,eAAA,EAAiB,aAAa,CAAA;AAC3D,EAAA,MAAM,gBAAgB,IAAA,CAAK,GAAA,CAAI,GAAG,UAAA,CAAW,QAAA,EAAU,aAAa,CAAC,CAAA;AAErE,EAAA,OAAO,IAAA,CAAK,KAAA,CAAO,cAAA,GAAiB,aAAA,GAAiB,SAAS,CAAA;AAChE;AAcO,SAAS,6BAAA,CACd,UAAA,EACA,UAAA,EACA,kBAAA,EACQ;AACR,EAAA,IAAI,UAAA,IAAc,CAAA,IAAK,kBAAA,IAAsB,CAAA,EAAG,OAAO,CAAA;AAEvD,EAAA,MAAM,SAAA,GAAY,UAAA,CAAW,UAAA,GAAa,kBAAA,EAAoB,CAAC,CAAA;AAC/D,EAAA,OAAO,UAAA,CAAW,SAAA,GAAY,UAAA,EAAY,CAAC,CAAA;AAC7C;AAKO,SAAS,kBAAA,CACd,aAAA,EACA,MAAA,GAAS,UAAA,EACD;AACR,EAAA,OAAO,cACJ,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,IAAA,KAAS,YAAY,CAAA,CAAE,MAAA,KAAW,MAAM,CAAA,CACxD,OAAO,CAACK,IAAAA,EAAK,MAAMA,IAAAA,GAAM,CAAA,CAAE,MAAM,CAAC,CAAA;AACvC;AA+BO,SAAS,kBAAA,CACd,QAAA,EACA,YAAA,GAAmD,kBAAA,EACnD,qBAAyD,yBAAA,EACzC;AAChB,EAAA,IAAI,CAAC,QAAA,CAAS,MAAA,EAAQ,OAAO,EAAC;AAE9B,EAAA,MAAM,WAAA,GAAc,QAAA,CAAS,CAAC,CAAA,CAAE,IAAA;AAChC,EAAA,MAAM,UAAU,WAAA,GAAc,CAAA;AAG9B,EAAA,OAAO,QAAA,CAAS,GAAA,CAAI,CAAC,OAAA,KAAY;AAC/B,IAAA,MAAM,YACJ,OAAA,CAAQ,SAAA,GAAY,QAAQ,WAAA,GAAc,OAAA,CAAQ,OAAO,OAAA,CAAQ,OAAA;AACnE,IAAA,MAAM,UAAA,GAAa,YAAA,CAAa,OAAA,CAAQ,IAAI,CAAA,IAAK,CAAA;AAEjD,IAAA,MAAM,SAAA,GAAY,UAAA,GAAa,CAAA,GAC3B,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,SAAS,CAAA,EAAG,UAAU,CAAA,GAC3C,CAAA;AAEJ,IAAA,OAAO;AAAA,MACL,MAAM,OAAA,CAAQ,IAAA;AAAA,MACd,SAAA,EAAW,mBAAmB,OAAA,CAAQ,IAAI,KAAK,yBAAA,CAA0B,OAAA,CAAQ,IAAI,CAAA,IAAK,CAAA;AAAA,MAC1F,IAAA,EAAM,CAAA;AAAA,MACN,OAAA,EAAS,CAAA;AAAA,MACT,WAAA,EAAa,SAAA;AAAA,MACb,IAAA,EAAM;AAAA,KACR;AAAA,EACF,CAAC,CAAA;AACH;AAKO,SAAS,oBAAA,CACd,UACA,IAAA,EACA,MAAA,EACA,wBAAO,IAAI,IAAA,EAAK,EAAE,WAAA,EAAY,EACd;AAChB,EAAA,MAAM,WAAA,GAAc,QAAA,CAAS,SAAA,CAAU,CAAC,CAAA,KAAM,EAAE,IAAA,KAAS,IAAA,IAAQ,CAAA,CAAE,IAAA,KAAS,IAAI,CAAA;AAEhF,EAAA,IAAI,eAAe,CAAA,EAAG;AACpB,IAAA,QAAA,CAAS,WAAW,EAAE,SAAA,IAAa,MAAA;AAAA,EACrC,CAAA,MAAO;AACL,IAAA,QAAA,CAAS,IAAA,CAAK;AAAA,MACZ,IAAA;AAAA,MACA,SAAA,EAAW,MAAA;AAAA,MACX,IAAA,EAAM,CAAA;AAAA,MACN,OAAA,EAAS,CAAA;AAAA,MACT,WAAA,EAAa,CAAA;AAAA,MACb;AAAA,KACD,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,QAAA;AACT;AAMA,IAAO,aAAA,GAAQ;AAAA,EACb,yBAAA;AAAA,EACA,kBAAA;AAAA,EACA,kBAAA;AAAA,EACA,eAAA;AAAA,EACA,eAAA;AAAA,EACA,gBAAA;AAAA,EACA,gBAAA;AAAA,EACA,eAAA;AAAA,EACA,uBAAA;AAAA,EACA,iBAAA;AAAA,EACA,6BAAA;AAAA,EACA,kBAAA;AAAA,EACA,kBAAA;AAAA,EACA;AACF;;;ACzaO,IAAM,YAAA,GAAN,cAA2B,KAAA,CAA2B;AAAA,EAClD,IAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,SAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAST,WAAA,CACE,eACA,YAAA,GAAmC,eAAA,EACnC,kBAAmC,GAAA,EACnC,OAAA,GAAmC,EAAC,EACpC;AACA,IAAA,MAAM,iBAAA,GAAoB,OAAO,aAAA,KAAkB,QAAA,IAAY,OAAO,YAAA,KAAiB,QAAA;AAEvF,IAAA,MAAM,OAAA,GAAU,oBAAqB,aAAA,GAA4B,eAAA;AACjE,IAAA,KAAA,CAAM,OAAO,CAAA;AAEb,IAAA,IAAA,CAAK,IAAA,GAAO,KAAK,WAAA,CAAY,IAAA;AAC7B,IAAA,IAAA,CAAK,IAAA,GAAO,oBAAqB,YAAA,GAA8B,aAAA;AAC/D,IAAA,IAAA,CAAK,MAAA,GAAS,oBAAqB,eAAA,GAA8B,YAAA;AACjE,IAAA,IAAA,CAAK,OAAA,GAAU,WAAW,EAAC;AAC3B,IAAA,IAAA,CAAK,SAAA,uBAAgB,IAAA,EAAK;AAG1B,IAAA,KAAA,CAAM,iBAAA,GAAoB,IAAA,EAAM,IAAA,CAAK,WAAW,CAAA;AAAA,EAClD;AAAA;AAAA;AAAA;AAAA,EAKA,MAAA,GAAkC;AAChC,IAAA,OAAO;AAAA,MACL,KAAA,EAAO;AAAA,QACL,MAAM,IAAA,CAAK,IAAA;AAAA,QACX,MAAM,IAAA,CAAK,IAAA;AAAA,QACX,SAAS,IAAA,CAAK,OAAA;AAAA,QACd,QAAQ,IAAA,CAAK,MAAA;AAAA,QACb,SAAS,IAAA,CAAK,OAAA;AAAA,QACd,SAAA,EAAW,IAAA,CAAK,SAAA,CAAU,WAAA;AAAY;AACxC,KACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,aAAA,GAAyB;AACvB,IAAA,OAAO,IAAA;AAAA,EACT;AACF,CAAA;AAkBO,IAAM,qBAAA,GAAN,cAAoC,YAAA,CAAa;AAAA,EACtD,WAAA,CAAY,YAAqB,OAAA,EAAmC;AAClE,IAAA,KAAA;AAAA,MACE,UAAA,GAAa,CAAA,oBAAA,EAAuB,UAAU,CAAA,CAAA,GAAK,oBAAA;AAAA,MACnD,oBAAA;AAAA,MACA,GAAA;AAAA,MACA,WAAW;AAAC,KACd;AAAA,EACF;AACF,CAAA;AAcO,IAAM,qBAAA,GAAN,cAAoC,YAAA,CAAa;AAAA,EACtD,WAAA,CACE,UAAA,EACA,KAAA,EACA,IAAA,EACA,gBACA,OAAA,EACA;AACA,IAAA,MAAM,WAAA,GAAc,cAAA,GAAiB,CAAA,EAAA,EAAK,cAAc,CAAA,CAAA,CAAA,GAAM,EAAA;AAC9D,IAAA,KAAA;AAAA,MACE,0CAA0C,UAAU,CAAA,IAAA,EAAO,KAAK,CAAA,CAAA,EAAI,IAAI,GAAG,WAAW,CAAA,CAAA;AAAA,MACtF,mBAAA;AAAA,MACA,GAAA;AAAA,MACA,EAAE,UAAA,EAAY,KAAA,EAAO,IAAA,EAAM,cAAA,EAAgB,GAAG,OAAA;AAAQ,KACxD;AAAA,EACF;AACF,CAAA;AAgGO,SAAS,eAAe,KAAA,EAAuC;AACpE,EAAA,OAAO,KAAA,YAAiB,YAAA;AAC1B;AAYO,SAAS,iBAAiB,KAAA,EAK/B;AACA,EAAA,IAAI,cAAA,CAAe,KAAK,CAAA,EAAG;AACzB,IAAA,OAAO;AAAA,MACL,MAAM,KAAA,CAAM,IAAA;AAAA,MACZ,QAAQ,KAAA,CAAM,MAAA;AAAA,MACd,SAAS,KAAA,CAAM,OAAA;AAAA,MACf,SAAS,KAAA,CAAM;AAAA,KACjB;AAAA,EACF;AAEA,EAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,eAAA;AAAA,MACN,MAAA,EAAQ,GAAA;AAAA,MACR,SAAS,KAAA,CAAM;AAAA,KACjB;AAAA,EACF;AAEA,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,eAAA;AAAA,IACN,MAAA,EAAQ,GAAA;AAAA,IACR,OAAA,EAAS,OAAO,KAAK;AAAA,GACvB;AACF;AA4BO,SAAS,eAAe,KAAA,EAA8B;AAC3D,EAAA,IAAI,cAAA,CAAe,KAAK,CAAA,EAAG;AACzB,IAAA,OAAO,KAAA;AAAA,EACT;AAEA,EAAA,IAAI,iBAAiB,KAAA,EAAO;AAC1B,IAAA,OAAO,IAAI,YAAA,CAAa,KAAA,CAAM,OAAO,CAAA;AAAA,EACvC;AAEA,EAAA,OAAO,IAAI,YAAA,CAAa,MAAA,CAAO,KAAK,CAAC,CAAA;AACvC;;;ACzJA,eAAsB,kBAAA,CACpB,OACA,OAAA,EACY;AACZ,EAAA,MAAM;AAAA,IACJ,cAAA;AAAA,IACA,iBAAA,GAAoB,IAAA;AAAA;AAAA,IACpB,GAAA;AAAA,IACA,UAAA;AAAA,IACA,cAAA,GAAiB,MAAA;AAAA,IACjB,MAAA;AAAA,IACA,KAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACF,GAAI,OAAA;AAGJ,EAAA,MAAM,QAAiC,EAAC;AAGxC,EAAA,IAAI,iBAAA,IAAqB,CAAC,cAAA,EAAgB;AACxC,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KAEF;AAAA,EACF;AAGA,EAAA,IAAI,cAAA,EAAgB;AAClB,IAAA,KAAA,CAAM,cAAA,GAAiB,WAAW,cAAc,CAAA;AAAA,EAClD;AAGA,EAAA,IAAI,GAAA,EAAK;AACP,IAAA,KAAA,CAAM,GAAA,GAAM,WAAW,GAAG,CAAA;AAAA,EAC5B,CAAA,MAAA,IAAW,eAAe,MAAA,EAAW;AAEnC,IAAA,MAAM,wBACJ,cAAA,KAAmB,UAAA,IAClB,cAAA,KAAmB,MAAA,IAAU,gBAAgB,UAAU,CAAA;AAE1D,IAAA,MAAM,0BACJ,cAAA,KAAmB,YAAA,IAClB,mBAAmB,MAAA,IAAU,CAAC,gBAAgB,UAAU,CAAA;AAE3D,IAAA,IAAI,qBAAA,EAAuB;AAEzB,MAAA,KAAA,CAAM,GAAA,GAAM,WAAW,UAA0B,CAAA;AAAA,IACnD,WAAW,uBAAA,EAAyB;AAElC,MAAA,KAAA,CAAM,UAAA,GAAa,UAAA;AAAA,IACrB;AAAA,EACF,WAAW,MAAA,EAAQ;AACjB,IAAA,KAAA,CAAM,MAAA,GAAS,WAAW,MAAM,CAAA;AAAA,EAClC,WAAW,KAAA,EAAO;AAEhB,IAAA,KAAA,CAAM,KAAA,GAAQ,KAAA,CAAM,WAAA,EAAY,CAAE,IAAA,EAAK;AAAA,EACzC,CAAA,MAAO;AACL,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AAGA,EAAA,IAAI,aAAA,GAAgB,KAAA,CAAM,OAAA,CAAQ,KAAK,CAAA;AAEvC,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,aAAA,GAAgB,aAAA,CAAc,QAAQ,OAAO,CAAA;AAAA,EAC/C;AAEA,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,MAAM,SAAS,KAAA,CAAM,OAAA,CAAQ,QAAQ,CAAA,GAAI,QAAA,GAAW,CAAC,QAAQ,CAAA;AAC7D,IAAA,KAAA,MAAW,SAAS,MAAA,EAAQ;AAC1B,MAAA,aAAA,GAAgB,aAAA,CAAc,SAAS,KAAK,CAAA;AAAA,IAC9C;AAAA,EACF;AAEA,EAAA,MAAML,YAAW,MAAM,aAAA;AAEvB,EAAA,IAAI,CAACA,SAAAA,EAAU;AAEb,IAAA,MAAM,UAAA,GAAa,GAAA,GACf,CAAA,IAAA,EAAO,GAAG,KACV,UAAA,GACA,CAAA,WAAA,EAAc,UAAU,CAAA,CAAA,GACxB,MAAA,GACA,CAAA,OAAA,EAAU,MAAM,CAAA,CAAA,GAChB,SAAS,KAAK,CAAA,CAAA;AAElB,IAAA,MAAM,IAAI,qBAAA;AAAA,MACR,uBAAuB,UAAU,CAAA,EAAG,iBAAiB,CAAA,iBAAA,EAAoB,cAAc,KAAK,EAAE,CAAA;AAAA,KAChG;AAAA,EACF;AAEA,EAAA,OAAOA,SAAAA;AACT;AASA,eAAsB,oBAAA,CACpB,OACA,OAAA,EACkB;AAClB,EAAA,IAAI;AACF,IAAA,MAAM,kBAAA,CAAmB,OAAO,OAAO,CAAA;AACvC,IAAA,OAAO,IAAA;AAAA,EACT,SAAS,KAAA,EAAO;AACd,IAAA,IAAI,iBAAiB,qBAAA,EAAuB;AAC1C,MAAA,OAAO,KAAA;AAAA,IACT;AACA,IAAA,MAAM,KAAA;AAAA,EACR;AACF;AASA,eAAsB,mBAAA,CACpB,OACA,OAAA,EAQc;AACd,EAAA,MAAM,EAAE,gBAAgB,MAAA,GAAS,IAAI,OAAA,EAAS,KAAA,EAAO,IAAA,EAAM,IAAA,EAAK,GAAI,OAAA;AAGpE,EAAA,MAAM,KAAA,GAAQ;AAAA,IACZ,cAAA,EAAgB,WAAW,cAAc,CAAA;AAAA,IACzC,GAAG;AAAA,GACL;AAEA,EAAA,IAAI,aAAA,GAAgB,KAAA,CAAM,IAAA,CAAK,KAAK,CAAA;AAEpC,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,aAAA,GAAgB,aAAA,CAAc,QAAQ,OAAO,CAAA;AAAA,EAC/C;AAEA,EAAA,IAAI,KAAA,EAAO;AACT,IAAA,aAAA,GAAgB,aAAA,CAAc,MAAM,KAAK,CAAA;AAAA,EAC3C;AAEA,EAAA,IAAI,IAAA,EAAM;AACR,IAAA,aAAA,GAAgB,aAAA,CAAc,KAAK,IAAI,CAAA;AAAA,EACzC;AAEA,EAAA,IAAI,IAAA,EAAM;AACR,IAAA,aAAA,GAAgB,aAAA,CAAc,KAAK,IAAI,CAAA;AAAA,EACzC;AAEA,EAAA,OAAO,aAAA;AACT;AASO,SAAS,qBAAA,CACd,gBACA,SAAA,EACM;AACN,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,GAAG,SAAS,CAAA,iKAAA;AAAA,KAGd;AAAA,EACF;AACF;ACnPO,SAAS,sBACd,MAAA,EACgB;AAChB,EAAA,MAAM,EAAE,QAAA,EAAU,OAAA,EAAS,SAAA,EAAW,WAAU,GAAI,MAAA;AAGpD,EAAA,IAAI,QAAA,EAAU;AACZ,IAAA,OAAO,WAAW,QAAQ,CAAA;AAAA,EAC5B;AAGA,EAAA,IAAI,SAAS,cAAA,EAAgB;AAC3B,IAAA,OAAO,UAAA,CAAW,QAAQ,cAAc,CAAA;AAAA,EAC1C;AAIA,EAAA,IAAI,SAAA,EAAW,gBAAe,EAAG;AAC/B,IAAA,MAAM,kBAAA,GAAqB,UAAU,qBAAA,EAAsB;AAC3D,IAAA,MAAM,iBAAA,GAAoB,oBAAoB,UAAA,KAAe,KAAA;AAE7D,IAAA,IAAI,iBAAA,EAAmB;AACrB,MAAA,MAAM,KAAA,GAAQ,UAAU,iBAAA,EAAkB;AAC1C,MAAA,IAAI,KAAA,EAAO;AACT,QAAA,OAAO,WAAW,KAAK,CAAA;AAAA,MACzB;AAEA,MAAA,MAAMM,iBAAgB,SAAA,IAAa,WAAA;AACnC,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,GAAGA,cAAa,CAAA;;AAAA;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,gBAAA,EAOG,aAAa,QAAQ,CAAA,mCAAA;AAAA,OAC1C;AAAA,IACF;AAAA,EAEF;AAGA,EAAA,MAAM,gBAAgB,SAAA,IAAa,WAAA;AACnC,EAAA,MAAM,6BAA6B,SAAA,EAAW,cAAA,MAC5C,SAAA,CAAU,qBAAA,IAAyB,UAAA,KAAe,KAAA;AAEpD,EAAA,IAAI,0BAAA,EAA4B;AAC9B,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,GAAG,aAAa,CAAA;;AAAA;AAAA,gBAAA,EAEG,aAAa,QAAQ,CAAA,mCAAA;AAAA,KAC1C;AAAA,EACF;AAEA,EAAA,MAAM,IAAI,KAAA;AAAA,IACR,GAAG,aAAa,CAAA;;AAAA;AAAA;AAAA;AAAA;;AAAA;AAAA,gBAAA,EAMK,aAAa,QAAQ,CAAA;;AAAA;AAAA;AAAA;AAAA;AAAA,aAAA;AAAA,GAM5C;AACF;AAUO,SAAS,sBAAA,CACd,gBACA,SAAA,EACgB;AAChB,EAAA,IAAI,CAAC,cAAA,EAAgB;AACnB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,GAAG,SAAS,CAAA,2GAAA;AAAA,KAEd;AAAA,EACF;AAEA,EAAA,IAAI;AACF,IAAA,OAAO,WAAW,cAAc,CAAA;AAAA,EAClC,SAAS,KAAA,EAAO;AACd,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,CAAA,EAAG,SAAS,CAAA,kCAAA,EAAqC,cAAc,CAAA,qEAAA;AAAA,KAEjE;AAAA,EACF;AACF;AAQO,SAAS,yBACd,MAAA,EACuB;AACvB,EAAA,IAAI;AACF,IAAA,OAAO,sBAAsB,MAAM,CAAA;AAAA,EACrC,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,IAAA;AAAA,EACT;AACF;ACjKO,SAAS,qBACd,UAAA,EACgB;AAEhB,EAAA,IAAI,eAAA,CAAgB,UAAU,CAAA,EAAG;AAC/B,IAAA,OAAO,UAAA;AAAA,EACT;AAGA,EAAA,OAAO,QAAA;AACT;AAcO,SAAS,oBACd,UAAA,EACyB;AACzB,EAAA,MAAM,MAAA,GAAS,qBAAqB,UAAU,CAAA;AAE9C,EAAA,IAAI,WAAW,UAAA,EAAY;AACzB,IAAA,OAAO,WAAW,UAA0B,CAAA;AAAA,EAC9C;AAEA,EAAA,OAAO,UAAA;AACT;AAQO,SAAS,mBAAmB,KAAA,EAAiC;AAClE,EAAA,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,gBAAgB,KAAK,CAAA;AAC5D;AAQO,SAAS,qBAAqB,KAAA,EAAuC;AAC1E,EAAA,OAAO,gBAAgB,KAAK,CAAA;AAC9B;AAgBO,SAAS,iBAAiB,UAAA,EAA2C;AAC1E,EAAA,MAAM,MAAA,GAAS,qBAAqB,UAAU,CAAA;AAE9C,EAAA,IAAI,WAAW,UAAA,EAAY;AACzB,IAAA,OAAO,CAAA,IAAA,EAAO,UAAA,CAAW,UAA0B,CAAA,CAAE,UAAU,CAAA,CAAA;AAAA,EACjE;AAEA,EAAA,OAAO,cAAc,UAAU,CAAA,CAAA;AACjC;AC3FO,SAAS,aAAa,KAAA,EAA2C;AACtE,EAAA,OAAO,KAAA,YAAiB,gBAAA;AAC1B;AAoBO,SAAS,oBAAoB,KAAA,EAA6D;AAC/F,EAAA,OAAO,YAAA,CAAa,KAAK,CAAA,IAAK,KAAA,CAAM,IAAA,KAAS,IAAA;AAC/C;AAgBO,SAAS,uBAAuB,KAAA,EAAiC;AACtE,EAAA,MAAM,OAAA,GAAU,MAAM,OAAA,IAAW,EAAA;AAGjC,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,KAAA,CAAM,sBAAsB,CAAA;AAClD,EAAA,IAAI,KAAA,IAAS,KAAA,CAAM,CAAC,CAAA,EAAG;AACrB,IAAA,OAAO,KAAA,CAAM,CAAC,CAAA,CAAE,IAAA,EAAK;AAAA,EACvB;AAGA,EAAA,MAAM,WAAA,GAAc,OAAA,CAAQ,KAAA,CAAM,iBAAiB,CAAA;AACnD,EAAA,IAAI,WAAA,IAAe,WAAA,CAAY,CAAC,CAAA,EAAG;AACjC,IAAA,OAAO,WAAA,CAAY,CAAC,CAAA,CAAE,IAAA,EAAK;AAAA,EAC7B;AAEA,EAAA,OAAO,SAAA;AACT;AAQO,SAAS,mBAAmB,KAAA,EAA2C;AAC5E,EAAA,IAAI,CAAC,YAAA,CAAa,KAAK,CAAA,EAAG,OAAO,KAAA;AAEjC,EAAA,MAAM,OAAA,GAAA,CAAW,KAAA,CAAM,OAAA,IAAW,EAAA,EAAI,WAAA,EAAY;AAClD,EAAA,OACE,OAAA,CAAQ,QAAA,CAAS,aAAa,CAAA,IAC9B,OAAA,CAAQ,QAAA,CAAS,SAAS,CAAA,IAC1B,OAAA,CAAQ,QAAA,CAAS,aAAa,CAAA,IAC9B,MAAM,IAAA,KAAS,GAAA;AAAA,EACf,MAAM,IAAA,KAAS,GAAA;AAAA,EACf,MAAM,IAAA,KAAS,GAAA;AAAA,EACf,MAAM,IAAA,KAAS,GAAA;AAAA,EACf,MAAM,IAAA,KAAS,GAAA;AAEnB;AAQO,SAAS,8BAA8B,KAAA,EAAyB;AACrE,EAAA,IAAI,CAAC,YAAA,CAAa,KAAK,CAAA,EAAG,OAAO,KAAA;AAEjC,EAAA,MAAM,OAAA,GAAA,CAAW,KAAA,CAAM,OAAA,IAAW,EAAA,EAAI,WAAA,EAAY;AAClD,EAAA,OACE,OAAA,CAAQ,QAAA,CAAS,8DAA8D,CAAA,IAC/E,OAAA,CAAQ,QAAA,CAAS,iDAAiD,CAAA,IAClE,OAAA,CAAQ,QAAA,CAAS,QAAQ,CAAA,IACzB,MAAM,IAAA,KAAS,EAAA;AAEnB;AAQO,SAAS,kBAAkB,KAAA,EAA2C;AAC3E,EAAA,IAAI,CAAC,YAAA,CAAa,KAAK,CAAA,EAAG,OAAO,KAAA;AAEjC,EAAA,OACE,MAAM,IAAA,KAAS,KAAA;AAAA,EACf,MAAM,IAAA,KAAS,KAAA;AAAA,EACf,MAAM,IAAA,KAAS,EAAA;AAAA,EACf,MAAM,IAAA,KAAS,EAAA;AAAA,EACf,MAAM,IAAA,KAAS,CAAA;AAEnB;;;ACnEO,SAAS,sBAAA,CACd,OACA,aAAA,EACwB;AACxB,EAAA,MAAM,aAAA,GAAgB,8BAA8B,KAAK,CAAA;AACzD,EAAA,MAAM,SAAA,GAAY,mBAAmB,KAAK,CAAA;AAC1C,EAAA,MAAM,WAAA,GAAc,kBAAkB,KAAK,CAAA;AAE3C,EAAA,MAAM,SAAA,GAAY,aAAa,CAAC,aAAA;AAEhC,EAAA,MAAM,YAAA,GAAe,eAAe,KAAK,CAAA;AACzC,EAAA,YAAA,CAAa,QAAQ,aAAA,GAAgB,aAAA;AACrC,EAAA,YAAA,CAAa,QAAQ,gBAAA,GAAmB,IAAA;AAExC,EAAA,IAAI,aAAA,EAAe;AACjB,IAAA,YAAA,CAAa,QAAQ,MAAA,GAAS,0BAAA;AAAA,EAChC,WAAW,WAAA,EAAa;AACtB,IAAA,YAAA,CAAa,QAAQ,MAAA,GAAS,kBAAA;AAAA,EAChC,WAAW,SAAA,EAAW;AACpB,IAAA,YAAA,CAAa,QAAQ,MAAA,GAAS,sBAAA;AAAA,EAChC;AAEA,EAAA,OAAO;AAAA,IACL,oBAAoB,SAAA,IAAa,aAAA;AAAA,IACjC,aAAA;AAAA,IACA,SAAA;AAAA,IACA,KAAA,EAAO,YAAA;AAAA,IACP,aAAA,EAAe;AAAA,GACjB;AACF;AA0CO,SAAS,uBAAA,CACd,OACA,OAAA,EACyB;AACzB,EAAA,IAAI,CAAC,mBAAA,CAAoB,KAAK,CAAA,EAAG;AAC/B,IAAA,OAAO;AAAA,MACL,WAAA,EAAa,KAAA;AAAA,MACb,KAAA,EAAO,EAAA;AAAA,MACP,KAAA,EAAO,eAAe,KAAK;AAAA,KAC7B;AAAA,EACF;AAEA,EAAA,MAAM,KAAA,GAAQ,uBAAuB,KAAK,CAAA;AAE1C,EAAA,MAAM,YAAA,GAAe,SAAS,UAAA,IAAc,OAAA,EAAS,SAAS,OAAA,EAAS,IAAA,GACnE,IAAI,qBAAA,CAAsB,OAAA,CAAQ,UAAA,EAAY,QAAQ,KAAA,EAAO,OAAA,CAAQ,MAAM,MAAA,EAAW,EAAE,gBAAgB,KAAA,EAAO,CAAA,GAC/G,cAAA,CAAe,KAAK,CAAA;AAExB,EAAA,OAAO;AAAA,IACL,WAAA,EAAa,IAAA;AAAA,IACb,KAAA;AAAA,IACA,KAAA,EAAO;AAAA,GACT;AACF;AA2CO,SAAS,kBAAA,CACd,OACA,aAAA,EACoB;AACpB,EAAA,MAAM,IAAA,GAAO,iBAAiB,KAAK,CAAA;AAGnC,EAAA,MAAM,SAAA,GACJ,kBAAkB,KAAK,CAAA,IACtB,mBAAmB,KAAK,CAAA,IAAK,CAAC,6BAAA,CAA8B,KAAK,CAAA;AAGpE,EAAA,MAAM,WAAA,GAAc,KAAA,YAAiB,YAAA,GACjC,KAAA,CAAM,eAAc,GACpB,KAAA;AAEJ,EAAA,OAAO;AAAA,IACL,MAAM,IAAA,CAAK,IAAA;AAAA,IACX,QAAQ,IAAA,CAAK,MAAA;AAAA,IACb,SAAS,IAAA,CAAK,OAAA;AAAA,IACd,OAAA,EAAS;AAAA,MACP,GAAG,IAAA,CAAK,OAAA;AAAA,MACR;AAAA,KACF;AAAA,IACA,WAAA;AAAA,IACA;AAAA,GACF;AACF;AAeO,SAAS,gBAAgB,KAAA,EAAwB;AACtD,EAAA,IAAI,iBAAiB,YAAA,EAAc;AAEjC,IAAA,OAAO,KAAA,CAAM,OAAA;AAAA,EACf;AAEA,EAAA,IAAI,mBAAA,CAAoB,KAAK,CAAA,EAAG;AAC9B,IAAA,MAAM,KAAA,GAAQ,uBAAuB,KAAK,CAAA;AAC1C,IAAA,OAAO,sBAAsB,KAAK,CAAA,eAAA,CAAA;AAAA,EACpC;AAEA,EAAA,IAAI,6BAAA,CAA8B,KAAK,CAAA,EAAG;AACxC,IAAA,OAAO,mEAAA;AAAA,EACT;AAEA,EAAA,IAAI,iBAAA,CAAkB,KAAK,CAAA,EAAG;AAC5B,IAAA,OAAO,8CAAA;AAAA,EACT;AAGA,EAAA,OAAO,8BAAA;AACT","file":"index.js","sourcesContent":["/**\n * @classytic/payroll - Logger\n *\n * Pluggable logger abstraction\n * Defaults to console, can be replaced with pino, winston, etc.\n */\n\nimport type { Logger } from '../types.js';\n\n// ============================================================================\n// Default Logger Implementation\n// ============================================================================\n\nconst createConsoleLogger = (): Logger => ({\n info: (message: string, meta?: Record<string, unknown>) => {\n if (meta) {\n console.log(`[Payroll] INFO: ${message}`, meta);\n } else {\n console.log(`[Payroll] INFO: ${message}`);\n }\n },\n error: (message: string, meta?: Record<string, unknown>) => {\n if (meta) {\n console.error(`[Payroll] ERROR: ${message}`, meta);\n } else {\n console.error(`[Payroll] ERROR: ${message}`);\n }\n },\n warn: (message: string, meta?: Record<string, unknown>) => {\n if (meta) {\n console.warn(`[Payroll] WARN: ${message}`, meta);\n } else {\n console.warn(`[Payroll] WARN: ${message}`);\n }\n },\n debug: (message: string, meta?: Record<string, unknown>) => {\n if (process.env.NODE_ENV !== 'production') {\n if (meta) {\n console.log(`[Payroll] DEBUG: ${message}`, meta);\n } else {\n console.log(`[Payroll] DEBUG: ${message}`);\n }\n }\n },\n});\n\n// ============================================================================\n// Logger State\n// ============================================================================\n\nlet currentLogger: Logger = createConsoleLogger();\nlet loggingEnabled = true;\n\n// ============================================================================\n// Logger Functions\n// ============================================================================\n\n/**\n * Get the current logger instance (respects loggingEnabled flag)\n */\nexport function getLogger(): Logger {\n return {\n info: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.info(message, meta);\n },\n error: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.error(message, meta);\n },\n warn: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.warn(message, meta);\n },\n debug: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.debug(message, meta);\n },\n };\n}\n\n/**\n * Set a custom logger instance\n */\nexport function setLogger(logger: Logger): void {\n currentLogger = logger;\n}\n\n/**\n * Reset to default console logger\n */\nexport function resetLogger(): void {\n currentLogger = createConsoleLogger();\n}\n\n/**\n * Create a child logger with prefix (respects loggingEnabled flag)\n */\nexport function createChildLogger(prefix: string): Logger {\n const parent = currentLogger;\n return {\n info: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) parent.info(`[${prefix}] ${message}`, meta);\n },\n error: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) parent.error(`[${prefix}] ${message}`, meta);\n },\n warn: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) parent.warn(`[${prefix}] ${message}`, meta);\n },\n debug: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) parent.debug(`[${prefix}] ${message}`, meta);\n },\n };\n}\n\n/**\n * Create a silent logger (for testing)\n */\nexport function createSilentLogger(): Logger {\n return {\n info: () => {},\n error: () => {},\n warn: () => {},\n debug: () => {},\n };\n}\n\n/**\n * Enable logging globally\n */\nexport function enableLogging(): void {\n loggingEnabled = true;\n}\n\n/**\n * Disable logging globally (useful for production)\n */\nexport function disableLogging(): void {\n loggingEnabled = false;\n}\n\n/**\n * Check if logging is enabled\n */\nexport function isLoggingEnabled(): boolean {\n return loggingEnabled;\n}\n\n// ============================================================================\n// Logger Proxy Object\n// ============================================================================\n\n/**\n * Logger proxy that always delegates to currentLogger\n * Respects global logging enabled/disabled state\n */\nexport const logger: Logger = {\n info: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.info(message, meta);\n },\n error: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.error(message, meta);\n },\n warn: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.warn(message, meta);\n },\n debug: (message: string, meta?: Record<string, unknown>) => {\n if (loggingEnabled) currentLogger.debug(message, meta);\n },\n};\n\nexport default logger;\n\n","/**\n * @classytic/payroll - Date Utilities\n *\n * Pure, composable, testable date operations\n * No side effects, no mutations\n */\n\nimport type { PayPeriodInfo, PaymentFrequency } from '../types.js';\n\n// ============================================================================\n// Date Arithmetic\n// ============================================================================\n\n/**\n * Add days to a date\n */\nexport function addDays(date: Date, days: number): Date {\n const result = new Date(date);\n result.setDate(result.getDate() + days);\n return result;\n}\n\n/**\n * Add months to a date\n */\nexport function addMonths(date: Date, months: number): Date {\n const result = new Date(date);\n result.setMonth(result.getMonth() + months);\n return result;\n}\n\n/**\n * Add years to a date\n */\nexport function addYears(date: Date, years: number): Date {\n const result = new Date(date);\n result.setFullYear(result.getFullYear() + years);\n return result;\n}\n\n/**\n * Subtract days from a date\n */\nexport function subDays(date: Date, days: number): Date {\n return addDays(date, -days);\n}\n\n/**\n * Subtract months from a date\n */\nexport function subMonths(date: Date, months: number): Date {\n return addMonths(date, -months);\n}\n\n// ============================================================================\n// Date Boundaries\n// ============================================================================\n\n/**\n * Get the start of a month\n */\nexport function startOfMonth(date: Date): Date {\n const result = new Date(date);\n result.setDate(1);\n result.setHours(0, 0, 0, 0);\n return result;\n}\n\n/**\n * Get the end of a month\n */\nexport function endOfMonth(date: Date): Date {\n const result = new Date(date);\n result.setMonth(result.getMonth() + 1, 0);\n result.setHours(23, 59, 59, 999);\n return result;\n}\n\n/**\n * Get the start of a year\n */\nexport function startOfYear(date: Date): Date {\n const result = new Date(date);\n result.setMonth(0, 1);\n result.setHours(0, 0, 0, 0);\n return result;\n}\n\n/**\n * Get the end of a year\n */\nexport function endOfYear(date: Date): Date {\n const result = new Date(date);\n result.setMonth(11, 31);\n result.setHours(23, 59, 59, 999);\n return result;\n}\n\n/**\n * Get the start of a day\n */\nexport function startOfDay(date: Date): Date {\n const result = new Date(date);\n result.setHours(0, 0, 0, 0);\n return result;\n}\n\n/**\n * Get the end of a day\n */\nexport function endOfDay(date: Date): Date {\n const result = new Date(date);\n result.setHours(23, 59, 59, 999);\n return result;\n}\n\n// ============================================================================\n// Date Normalization\n// ============================================================================\n\n/**\n * Convert a date to a UTC-based date string for consistent comparison.\n *\n * Unlike `Date.toDateString()` which uses the local timezone, this produces\n * a locale-independent string based on the date's year/month/day components.\n * Use this for holiday set lookups to avoid timezone-dependent mismatches.\n */\nexport function toUTCDateString(date: Date): string {\n const d = new Date(date);\n d.setHours(0, 0, 0, 0);\n return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;\n}\n\n// ============================================================================\n// Date Differences\n// ============================================================================\n\n/**\n * Calculate difference in days between two dates.\n *\n * Normalizes both dates to midnight before computing to avoid\n * inconsistencies from time-of-day differences or DST transitions.\n */\nexport function diffInDays(start: Date, end: Date): number {\n const s = new Date(start);\n const e = new Date(end);\n s.setHours(0, 0, 0, 0);\n e.setHours(0, 0, 0, 0);\n return Math.ceil((e.getTime() - s.getTime()) / (1000 * 60 * 60 * 24));\n}\n\n/**\n * Calculate difference in months between two dates\n */\nexport function diffInMonths(start: Date, end: Date): number {\n const startDate = new Date(start);\n const endDate = new Date(end);\n return (\n (endDate.getFullYear() - startDate.getFullYear()) * 12 +\n (endDate.getMonth() - startDate.getMonth())\n );\n}\n\n/**\n * Calculate difference in years between two dates\n */\nexport function diffInYears(start: Date, end: Date): number {\n return Math.floor(diffInMonths(start, end) / 12);\n}\n\n// Aliases for backwards compatibility\nexport const daysBetween = diffInDays;\nexport const monthsBetween = diffInMonths;\n\n// ============================================================================\n// Day Type Checks\n// ============================================================================\n\n/**\n * Check if date is a weekday (Mon-Fri)\n */\nexport function isWeekday(date: Date): boolean {\n const day = new Date(date).getDay();\n return day >= 1 && day <= 5;\n}\n\n/**\n * Check if date is a weekend (Sat-Sun)\n */\nexport function isWeekend(date: Date): boolean {\n const day = new Date(date).getDay();\n return day === 0 || day === 6;\n}\n\n/**\n * Get day of week (0=Sunday, 6=Saturday)\n */\nexport function getDayOfWeek(date: Date): number {\n return new Date(date).getDay();\n}\n\n/**\n * Get day name\n */\nexport function getDayName(date: Date): string {\n const days = [\n 'Sunday',\n 'Monday',\n 'Tuesday',\n 'Wednesday',\n 'Thursday',\n 'Friday',\n 'Saturday',\n ];\n return days[getDayOfWeek(date)];\n}\n\n// ============================================================================\n// Pay Period Functions\n// ============================================================================\n\n/**\n * Get pay period for a given month and year (monthly periods)\n */\nexport function getPayPeriod(month: number, year: number): PayPeriodInfo {\n const startDate = new Date(year, month - 1, 1);\n return {\n month,\n year,\n startDate: startOfMonth(startDate),\n endDate: endOfMonth(startDate),\n };\n}\n\n/**\n * Get pay period based on payment frequency\n *\n * Creates the correct period boundaries based on the employee's payment frequency:\n * - monthly: full calendar month\n * - bi_weekly: 14 days ending on paymentDate\n * - weekly: 7 days ending on paymentDate\n * - daily/hourly: single day (paymentDate)\n *\n * @param frequency - Payment frequency\n * @param paymentDate - Date of payment (used as end of period for non-monthly)\n * @param month - Month (1-12) for accounting purposes\n * @param year - Year for accounting purposes\n * @returns Pay period with appropriate boundaries\n */\nexport function getPayPeriodForFrequency(\n frequency: PaymentFrequency,\n paymentDate: Date,\n month: number,\n year: number\n): PayPeriodInfo & { workingDays: number } {\n switch (frequency) {\n case 'monthly': {\n const period = getPayPeriod(month, year);\n const workingDays = getWorkingDaysInMonth(year, month);\n return { ...period, workingDays };\n }\n\n case 'bi_weekly': {\n // 14-day period ending on paymentDate\n const endDate = startOfDay(paymentDate);\n const startDate = addDays(endDate, -13); // 14 days total (0-13)\n const workingDays = countWeekdaysInRange(startDate, endDate);\n return { month, year, startDate, endDate, workingDays };\n }\n\n case 'weekly': {\n // 7-day period ending on paymentDate\n const endDate = startOfDay(paymentDate);\n const startDate = addDays(endDate, -6); // 7 days total (0-6)\n const workingDays = countWeekdaysInRange(startDate, endDate);\n return { month, year, startDate, endDate, workingDays };\n }\n\n case 'daily':\n case 'hourly': {\n // Single day period\n const date = startOfDay(paymentDate);\n const workingDays = isWeekday(date) ? 1 : 0;\n return { month, year, startDate: date, endDate: date, workingDays };\n }\n\n default:\n // Fallback to monthly\n return getPayPeriodForFrequency('monthly', paymentDate, month, year);\n }\n}\n\n/**\n * Count weekdays (Mon-Fri) in a date range (inclusive)\n */\nfunction countWeekdaysInRange(start: Date, end: Date): number {\n let count = 0;\n const current = new Date(start);\n while (current <= end) {\n if (isWeekday(current)) {\n count++;\n }\n current.setDate(current.getDate() + 1);\n }\n return count;\n}\n\n/**\n * Get current pay period\n */\nexport function getCurrentPeriod(date = new Date()): { year: number; month: number } {\n const d = new Date(date);\n return {\n year: d.getFullYear(),\n month: d.getMonth() + 1,\n };\n}\n\n/**\n * Get working days in a month\n */\nexport function getWorkingDaysInMonth(year: number, month: number): number {\n const start = new Date(year, month - 1, 1);\n const end = endOfMonth(start);\n let count = 0;\n \n const current = new Date(start);\n while (current <= end) {\n if (isWeekday(current)) {\n count++;\n }\n current.setDate(current.getDate() + 1);\n }\n \n return count;\n}\n\n/**\n * Get total days in a month\n */\nexport function getDaysInMonth(year: number, month: number): number {\n return new Date(year, month, 0).getDate();\n}\n\n// ============================================================================\n// Employment Date Functions\n// ============================================================================\n\n/**\n * Calculate probation end date\n */\nexport function calculateProbationEnd(\n hireDate: Date,\n probationMonths: number\n): Date | null {\n if (!probationMonths || probationMonths <= 0) return null;\n return addMonths(hireDate, probationMonths);\n}\n\n/**\n * Check if employee is on probation\n */\nexport function isOnProbation(\n probationEndDate: Date | null | undefined,\n now = new Date()\n): boolean {\n if (!probationEndDate) return false;\n return now < new Date(probationEndDate);\n}\n\n/**\n * Calculate years of service\n */\nexport function calculateYearsOfService(\n hireDate: Date,\n terminationDate?: Date | null\n): number {\n const end = terminationDate || new Date();\n const days = diffInDays(hireDate, end);\n return Math.max(0, Math.floor((days / 365.25) * 10) / 10);\n}\n\n// ============================================================================\n// Range Functions\n// ============================================================================\n\n/**\n * Check if a date is within a range\n */\nexport function isDateInRange(date: Date, start: Date, end: Date): boolean {\n const checkDate = new Date(date);\n return checkDate >= new Date(start) && checkDate <= new Date(end);\n}\n\n/**\n * Check if an item with effectiveFrom/effectiveTo dates is effective for a given period.\n *\n * Used for filtering allowances, deductions, and other time-bounded compensation items.\n * An item is considered effective if its date range overlaps with the period.\n *\n * @param item - Object with optional effectiveFrom and effectiveTo dates\n * @param periodStart - Start of the period to check\n * @param periodEnd - End of the period to check\n * @returns true if the item is effective during any part of the period\n *\n * @example\n * ```typescript\n * const allowance = { effectiveFrom: new Date('2024-01-01'), effectiveTo: null };\n * const periodStart = new Date('2024-03-01');\n * const periodEnd = new Date('2024-03-31');\n *\n * isEffectiveForPeriod(allowance, periodStart, periodEnd); // true\n * ```\n */\nexport function isEffectiveForPeriod(\n item: { effectiveFrom?: Date | null; effectiveTo?: Date | null },\n periodStart: Date,\n periodEnd: Date\n): boolean {\n const effectiveFrom = item.effectiveFrom ? new Date(item.effectiveFrom) : new Date(0);\n const effectiveTo = item.effectiveTo ? new Date(item.effectiveTo) : new Date('2099-12-31');\n\n // Item is effective if its range overlaps with the period\n return effectiveFrom <= periodEnd && effectiveTo >= periodStart;\n}\n\n/**\n * Get date range for a pay period\n */\nexport function getPayPeriodDateRange(\n month: number,\n year: number\n): { start: Date; end: Date } {\n const period = getPayPeriod(month, year);\n return { start: period.startDate, end: period.endDate };\n}\n\n// ============================================================================\n// Formatting Functions\n// ============================================================================\n\n/**\n * Format date for database storage\n */\nexport function formatDateForDB(date: Date): string {\n if (!date) return '';\n return new Date(date).toISOString();\n}\n\n/**\n * Parse date from database\n */\nexport function parseDBDate(dateString: string): Date | null {\n if (!dateString) return null;\n return new Date(dateString);\n}\n\n/**\n * Format period as string (e.g., \"01/2025\")\n */\nexport function formatPeriod({ month, year }: { month: number; year: number }): string {\n return `${String(month).padStart(2, '0')}/${year}`;\n}\n\n/**\n * Parse period string back to object\n */\nexport function parsePeriod(periodString: string): { month: number; year: number } {\n const [month, year] = periodString.split('/').map(Number);\n return { month, year };\n}\n\n/**\n * Format month name\n */\nexport function getMonthName(month: number): string {\n const months = [\n 'January', 'February', 'March', 'April', 'May', 'June',\n 'July', 'August', 'September', 'October', 'November', 'December',\n ];\n return months[month - 1] || '';\n}\n\n/**\n * Format short month name\n */\nexport function getShortMonthName(month: number): string {\n const months = [\n 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',\n ];\n return months[month - 1] || '';\n}\n\n// ============================================================================\n// Default Export\n// ============================================================================\n\nexport default {\n toUTCDateString,\n addDays,\n addMonths,\n addYears,\n subDays,\n subMonths,\n startOfMonth,\n endOfMonth,\n startOfYear,\n endOfYear,\n startOfDay,\n endOfDay,\n diffInDays,\n diffInMonths,\n diffInYears,\n daysBetween,\n monthsBetween,\n isWeekday,\n isWeekend,\n getDayOfWeek,\n getDayName,\n getPayPeriod,\n getCurrentPeriod,\n getWorkingDaysInMonth,\n getDaysInMonth,\n calculateProbationEnd,\n isOnProbation,\n calculateYearsOfService,\n isDateInRange,\n isEffectiveForPeriod,\n getPayPeriodDateRange,\n formatDateForDB,\n parseDBDate,\n formatPeriod,\n parsePeriod,\n getMonthName,\n getShortMonthName,\n};\n\n","/**\n * Money calculation utilities for payroll compliance\n *\n * Uses banker's rounding (round half to even) to prevent systematic bias\n * in financial calculations, as recommended for payroll systems.\n *\n * PRECISION: All functions default to 2 decimal places (cents/paise precision)\n * to maintain accuracy in payroll calculations. Amounts are stored as floating\n * point numbers with decimal precision (e.g., 1000.50 for $1,000.50).\n *\n * ## Design Decision: Floating Point Storage\n *\n * This module uses floating-point numbers (IEEE 754 double precision) for money\n * storage rather than integer minor units (cents) or MongoDB Decimal128.\n *\n * ### Tradeoffs:\n *\n * **Why floating point:**\n * - Simpler API (developers think in dollars, not cents)\n * - More intuitive JSON serialization (100.50 vs 10050)\n * - Sufficient precision for payroll (15 significant digits)\n * - Banker's rounding mitigates cumulative drift\n *\n * **Why NOT integer cents:**\n * - Would require breaking schema changes\n * - All consumer code needs conversion (amount / 100)\n * - More verbose for reporting/display\n *\n * **Why NOT Decimal128:**\n * - MongoDB-specific, reduces portability\n * - Requires special BSON handling in application code\n * - Overkill for typical payroll amounts (< $10M/employee/year)\n *\n * ### Mitigation:\n * - All calculations go through roundMoney() with banker's rounding\n * - Final amounts always rounded to 2 decimal places before storage\n * - Aggregations use MongoDB $round operator for consistency\n *\n * @see https://en.wikipedia.org/wiki/Banker%27s_rounding\n */\n\n/**\n * Banker's rounding (round half to even) with decimal precision\n *\n * When rounding 0.5, rounds to the nearest even number:\n * - 2.5 → 2 (even)\n * - 3.5 → 4 (even)\n * - 4.5 → 4 (even)\n *\n * This prevents systematic bias in cumulative rounding that occurs\n * with standard rounding (always up), which is critical for payroll compliance.\n *\n * @param value - The number to round\n * @param decimals - Number of decimal places (default: 2 for cent precision)\n * @returns The rounded value\n */\nexport function roundMoney(value: number, decimals = 2): number {\n const multiplier = Math.pow(10, decimals);\n const scaled = value * multiplier;\n const fraction = scaled - Math.floor(scaled);\n\n // If exactly 0.5 (within tolerance), round to nearest even\n // Use 1e-10 tolerance to handle floating-point imprecision\n if (Math.abs(fraction - 0.5) < 1e-10) {\n const floor = Math.floor(scaled);\n const rounded = floor % 2 === 0 ? floor : floor + 1;\n return rounded / multiplier;\n }\n\n // Otherwise use standard rounding\n return Math.round(scaled) / multiplier;\n}\n\n/**\n * Round money with validation for negative values\n *\n * @param value - The number to round\n * @param decimals - Number of decimal places (default: 2 for cent precision)\n * @returns The rounded value (never negative for deductions)\n */\nexport function roundMoneyPositive(value: number, decimals = 2): number {\n return Math.max(0, roundMoney(value, decimals));\n}\n\n/**\n * Calculate percentage of an amount with banker's rounding\n *\n * @param amount - Base amount\n * @param percentage - Percentage (e.g., 10 for 10%)\n * @param decimals - Decimal places to round to (default: 2 for cent precision)\n * @returns Rounded percentage amount\n */\nexport function percentageOf(amount: number, percentage: number, decimals = 2): number {\n return roundMoney((amount * percentage) / 100, decimals);\n}\n\n/**\n * Prorate an amount by a ratio with banker's rounding\n *\n * @param amount - Base amount\n * @param ratio - Proration ratio (0 to 1)\n * @param decimals - Decimal places (default: 2 for cent precision)\n * @returns Prorated amount\n */\nexport function prorateAmount(amount: number, ratio: number, decimals = 2): number {\n return roundMoney(amount * ratio, decimals);\n}\n","/**\n * @classytic/payroll - Calculation Utilities\n *\n * Pure, functional, composable financial calculations\n * No side effects, highly testable\n */\n\nimport type {\n Allowance,\n Deduction,\n Compensation,\n TaxCalculationResult,\n CompensationBreakdownResult,\n} from '../types.js';\nimport { roundMoney } from './money.js';\n\n// ============================================================================\n// Basic Math Operations\n// ============================================================================\n\n/**\n * Sum array of numbers\n */\nexport function sum(numbers: number[]): number {\n return numbers.reduce((total, n) => total + n, 0);\n}\n\n/**\n * Sum by property\n */\nexport function sumBy<T>(items: T[], getter: (item: T) => number): number {\n return items.reduce((total, item) => total + getter(item), 0);\n}\n\n/**\n * Sum allowances\n */\nexport function sumAllowances(allowances: Array<{ amount: number }>): number {\n return sumBy(allowances, (a) => a.amount);\n}\n\n/**\n * Sum deductions\n */\nexport function sumDeductions(deductions: Array<{ amount: number }>): number {\n return sumBy(deductions, (d) => d.amount);\n}\n\n/**\n * ROUNDING POLICY FOR FINANCIAL CALCULATIONS\n *\n * Monetary amounts are stored as floating point numbers in major units with\n * decimal precision (e.g., 1000.50 for $1,000.50 or ₹1,000.50).\n *\n * PRECISION: All calculations preserve 2 decimal places (cent/paise precision)\n * to maintain accuracy required for payroll compliance.\n *\n * Rounding Rules:\n * 1. Banker's Rounding (Round Half to Even): Used for fair rounding over many transactions\n * 2. All intermediate calculations maintain full precision\n * 3. Final amounts rounded to 2 decimals using banker's rounding\n * 4. Tax calculations use banker's rounding for compliance\n *\n * Example:\n * Input: 1000.50 base + 15% tax\n * Calculation: 1000.50 * 0.15 = 150.075 → rounds to 150.08 (banker's rounding to 2 decimals)\n * Result: Tax = 150.08\n *\n * @see https://en.wikipedia.org/wiki/Rounding#Round_half_to_even\n */\n\n/**\n * Banker's Rounding (Round Half to Even) - Integer precision\n *\n * Rounds to the nearest integer using banker's rounding (round half to even).\n * This prevents systematic bias in rounding over many transactions.\n *\n * Uses epsilon check for safe floating-point comparison.\n *\n * Examples:\n * 0.5 → 0 (even)\n * 1.5 → 2 (even)\n * 2.5 → 2 (even)\n * 3.5 → 4 (even)\n *\n * @param value - The number to round\n * @returns Rounded integer\n * @note For money calculations with decimal precision, use `roundMoney()` instead\n */\nexport function bankersRound(value: number): number {\n const floor = Math.floor(value);\n const fraction = value - floor;\n\n // Use epsilon check for safer floating-point comparison\n if (Math.abs(fraction - 0.5) < Number.EPSILON) {\n // If halfway, round to even\n return floor % 2 === 0 ? floor : floor + 1;\n }\n\n // Otherwise use standard rounding\n return Math.round(value);\n}\n\n/**\n * Apply percentage to amount with banker's rounding (2 decimal precision)\n *\n * @param amount - Amount in major units (e.g., dollars, rupees)\n * @param percentage - Percentage to apply (e.g., 15 for 15%)\n * @param decimals - Decimal places for precision (default: 2 for cent precision)\n * @returns Result in major units, properly rounded to 2 decimals\n * @note Uses banker's rounding (round half to even) to preserve cent precision.\n * Equivalent to percentageOf() from money.ts.\n */\nexport function applyPercentage(amount: number, percentage: number, decimals = 2): number {\n // Use centralized roundMoney for consistent banker's rounding across codebase\n const result = (amount * percentage) / 100;\n return roundMoney(result, decimals);\n}\n\n/**\n * Calculate percentage of total\n */\nexport function calculatePercentage(part: number, total: number): number {\n return total > 0 ? bankersRound((part / total) * 100) : 0;\n}\n\n/**\n * Round to decimal places using banker's rounding\n */\nexport function roundTo(value: number, decimals = 2): number {\n const factor = Math.pow(10, decimals);\n return bankersRound(value * factor) / factor;\n}\n\n// ============================================================================\n// Salary Calculations\n// ============================================================================\n\n/**\n * Calculate gross salary from base and allowances\n */\nexport function calculateGross(\n baseAmount: number,\n allowances: Array<{ amount: number }>\n): number {\n return baseAmount + sumAllowances(allowances);\n}\n\n/**\n * Calculate net salary from gross and deductions\n */\nexport function calculateNet(\n gross: number,\n deductions: Array<{ amount: number }>\n): number {\n return Math.max(0, gross - sumDeductions(deductions));\n}\n\n/**\n * Calculate total compensation\n */\nexport function calculateTotalCompensation(\n baseAmount: number,\n allowances: Array<{ amount: number }>,\n deductions: Array<{ amount: number }>\n): { gross: number; net: number; deductions: number } {\n const gross = calculateGross(baseAmount, allowances);\n const totalDeductions = sumDeductions(deductions);\n const net = calculateNet(gross, deductions);\n return { gross, net, deductions: totalDeductions };\n}\n\n// ============================================================================\n// Allowance & Deduction Calculation\n// ============================================================================\n\n/**\n * Calculate allowance amount (handles percentage-based)\n */\nexport function calculateAllowanceAmount(\n allowance: Pick<Allowance, 'amount' | 'isPercentage' | 'value'>,\n baseAmount: number\n): number {\n if (allowance.isPercentage && allowance.value !== undefined) {\n return applyPercentage(baseAmount, allowance.value);\n }\n return allowance.amount;\n}\n\n/**\n * Calculate deduction amount (handles percentage-based)\n */\nexport function calculateDeductionAmount(\n deduction: Pick<Deduction, 'amount' | 'isPercentage' | 'value'>,\n baseAmount: number\n): number {\n if (deduction.isPercentage && deduction.value !== undefined) {\n return applyPercentage(baseAmount, deduction.value);\n }\n return deduction.amount;\n}\n\n/**\n * Calculate all allowances with their actual amounts\n */\nexport function calculateAllowances(\n allowances: Allowance[],\n baseAmount: number\n): Array<Allowance & { calculatedAmount: number }> {\n return allowances.map((allowance) => ({\n ...allowance,\n calculatedAmount: calculateAllowanceAmount(allowance, baseAmount),\n }));\n}\n\n/**\n * Calculate all deductions with their actual amounts\n */\nexport function calculateDeductions(\n deductions: Deduction[],\n baseAmount: number\n): Array<Deduction & { calculatedAmount: number }> {\n return deductions.map((deduction) => ({\n ...deduction,\n calculatedAmount: calculateDeductionAmount(deduction, baseAmount),\n }));\n}\n\n// ============================================================================\n// Compensation Breakdown\n// ============================================================================\n\n/**\n * Calculate full compensation breakdown\n */\nexport function calculateCompensationBreakdown(\n compensation: Pick<Compensation, 'baseAmount' | 'allowances' | 'deductions'>\n): CompensationBreakdownResult {\n const { baseAmount, allowances = [], deductions = [] } = compensation;\n\n const calculatedAllowances = calculateAllowances(allowances, baseAmount);\n const calculatedDeductions = calculateDeductions(deductions, baseAmount);\n\n const grossAmount =\n baseAmount + sumBy(calculatedAllowances, (a) => a.calculatedAmount);\n const netAmount =\n grossAmount - sumBy(calculatedDeductions, (d) => d.calculatedAmount);\n\n return {\n baseAmount,\n allowances: calculatedAllowances,\n deductions: calculatedDeductions,\n grossAmount,\n netAmount: Math.max(0, netAmount),\n };\n}\n\n// ============================================================================\n// Tax Calculations\n// ============================================================================\n\n/**\n * Apply tax brackets to calculate tax\n *\n * Uses banker's rounding for compliance (rounds to 2 decimal places).\n * Consistent with all other money calculations in the system.\n */\nexport function applyTaxBrackets(\n amount: number,\n brackets: Array<{ min: number; max: number; rate: number }>\n): number {\n let tax = 0;\n\n for (const bracket of brackets) {\n if (amount > bracket.min) {\n const taxableAmount = Math.min(amount, bracket.max) - bracket.min;\n tax += taxableAmount * bracket.rate;\n }\n }\n\n // Use roundMoney for consistency with all other money calculations\n return roundMoney(tax);\n}\n\n/**\n * Calculate tax with result\n */\nexport function calculateTax(\n amount: number,\n brackets: Array<{ min: number; max: number; rate: number }>\n): TaxCalculationResult {\n const tax = applyTaxBrackets(amount, brackets);\n return {\n gross: amount,\n tax,\n net: amount - tax,\n };\n}\n\n// ============================================================================\n// Overtime Calculations\n// ============================================================================\n\n/**\n * Calculate overtime pay\n */\nexport function calculateOvertime(\n hourlyRate: number,\n overtimeHours: number,\n multiplier = 1.5\n): number {\n return roundMoney(hourlyRate * overtimeHours * multiplier, 2);\n}\n\n/**\n * Calculate hourly rate from monthly salary\n */\nexport function calculateHourlyRate(\n monthlySalary: number,\n hoursPerMonth = 176 // 44 hours/week * 4 weeks\n): number {\n return roundMoney(monthlySalary / hoursPerMonth, 2);\n}\n\n/**\n * Calculate daily rate from monthly salary\n */\nexport function calculateDailyRate(\n monthlySalary: number,\n daysPerMonth = 22\n): number {\n return roundMoney(monthlySalary / daysPerMonth, 2);\n}\n\n// ============================================================================\n// Default Export\n// ============================================================================\n\nexport default {\n sum,\n sumBy,\n sumAllowances,\n sumDeductions,\n applyPercentage,\n calculatePercentage,\n roundTo,\n calculateGross,\n calculateNet,\n calculateTotalCompensation,\n calculateAllowanceAmount,\n calculateDeductionAmount,\n calculateAllowances,\n calculateDeductions,\n calculateCompensationBreakdown,\n applyTaxBrackets,\n calculateTax,\n calculateOvertime,\n calculateHourlyRate,\n calculateDailyRate,\n};\n\n","/**\n * @classytic/payroll - Configuration & Calculation Utilities\n *\n * DESIGN PRINCIPLES:\n * 1. Accept data, don't manage it\n * 2. Pure functions - easy to test, no side effects\n * 3. Smart defaults that work out of the box\n * 4. Override at operation time when needed\n *\n * The payroll package CALCULATES, it doesn't STORE calendars/holidays.\n * Your app manages that data and passes it when needed.\n */\n\nimport { roundMoney } from '../utils/money.js';\nimport { toUTCDateString } from '../utils/date.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/** Work schedule configuration */\nexport interface WorkSchedule {\n /** Working days (0=Sun, 1=Mon, ..., 6=Sat). Default: Mon-Fri */\n workingDays: number[];\n /** Hours per work day. Default: 8 */\n hoursPerDay: number;\n}\n\n/** Options passed when processing payroll */\nexport interface PayrollProcessingOptions {\n /** Holidays in this period (from YOUR app's holiday model) */\n holidays?: Date[];\n /** Override work schedule for this operation */\n workSchedule?: Partial<WorkSchedule>;\n /** Skip tax calculation */\n skipTax?: boolean;\n /** Skip proration (pay full amount regardless of hire/termination date) */\n skipProration?: boolean;\n /** Skip attendance deduction */\n skipAttendance?: boolean;\n}\n\n/** Working days calculation result */\nexport interface WorkingDaysResult {\n /** Total calendar days in period */\n totalDays: number;\n /** Working days (excluding weekends and holidays) */\n workingDays: number;\n /** Weekend days */\n weekends: number;\n /** Holiday count */\n holidays: number;\n}\n\n/** Proration calculation result */\nexport interface ProrationResult {\n /** Proration ratio (0-1) */\n ratio: number;\n /**\n * Reason for proration:\n * - 'full': Employee worked the entire period (ratio = 1)\n * - 'new_hire': Employee was hired during the period\n * - 'termination': Employee was terminated during the period\n * - 'both': Both hired and terminated within the period\n * - 'not_active': Employee was not active at all during the period (ratio = 0)\n */\n reason: 'full' | 'new_hire' | 'termination' | 'both' | 'not_active';\n /** Whether salary should be prorated */\n isProrated: boolean;\n}\n\n/** Attendance data (from YOUR attendance system) */\nexport interface AttendanceInput {\n /**\n * Expected work days in period.\n * If not provided, derived from employee's workSchedule and period dates.\n */\n expectedDays?: number;\n /** Actual days worked */\n actualDays: number;\n}\n\n// ============================================================================\n// Default Configuration\n// ============================================================================\n\nexport const DEFAULT_WORK_SCHEDULE: WorkSchedule = {\n workingDays: [1, 2, 3, 4, 5], // Monday to Friday\n hoursPerDay: 8,\n};\n\n// ============================================================================\n// Pure Calculation Functions\n// ============================================================================\n\n/**\n * Count working days in a date range\n *\n * @example\n * const result = countWorkingDays(\n * new Date('2024-03-01'),\n * new Date('2024-03-31'),\n * { workingDays: [1,2,3,4,5], holidays: companyHolidays }\n * );\n */\nexport function countWorkingDays(\n startDate: Date,\n endDate: Date,\n options: {\n workingDays?: number[];\n holidays?: Date[];\n } = {}\n): WorkingDaysResult {\n const workDays = options.workingDays || DEFAULT_WORK_SCHEDULE.workingDays;\n const holidaySet = new Set(\n (options.holidays || []).map(d => toUTCDateString(d))\n );\n\n let totalDays = 0;\n let workingDays = 0;\n let holidays = 0;\n let weekends = 0;\n\n const current = new Date(startDate);\n current.setHours(0, 0, 0, 0);\n const end = new Date(endDate);\n end.setHours(0, 0, 0, 0);\n\n while (current <= end) {\n totalDays++;\n const isHoliday = holidaySet.has(toUTCDateString(current));\n const isWorkDay = workDays.includes(current.getDay());\n\n if (isHoliday) {\n holidays++;\n } else if (isWorkDay) {\n workingDays++;\n } else {\n weekends++;\n }\n\n current.setDate(current.getDate() + 1);\n }\n\n return { totalDays, workingDays, weekends, holidays };\n}\n\n/**\n * Calculate proration ratio for partial months\n *\n * @example\n * const proration = calculateProration(\n * employee.hireDate,\n * employee.terminationDate,\n * periodStart,\n * periodEnd\n * );\n */\nexport function calculateProration(\n hireDate: Date,\n terminationDate: Date | null | undefined,\n periodStart: Date,\n periodEnd: Date\n): ProrationResult {\n const hire = new Date(hireDate);\n hire.setHours(0, 0, 0, 0);\n const term = terminationDate ? new Date(terminationDate) : null;\n if (term) term.setHours(0, 0, 0, 0);\n const start = new Date(periodStart);\n start.setHours(0, 0, 0, 0);\n const end = new Date(periodEnd);\n end.setHours(0, 0, 0, 0);\n\n // Employee not active in this period\n if (hire > end || (term && term < start)) {\n return { ratio: 0, reason: 'not_active', isProrated: true };\n }\n\n // Effective dates within the period\n const effectiveStart = hire > start ? hire : start;\n const effectiveEnd = term && term < end ? term : end;\n\n // Calculate days\n const totalDays = Math.ceil((end.getTime() - start.getTime()) / 86400000) + 1;\n const actualDays = Math.ceil((effectiveEnd.getTime() - effectiveStart.getTime()) / 86400000) + 1;\n const ratio = Math.min(1, Math.max(0, actualDays / totalDays));\n\n // Determine reason\n const isNewHire = hire > start;\n const isTermination = term !== null && term < end;\n \n let reason: ProrationResult['reason'] = 'full';\n if (isNewHire && isTermination) {\n reason = 'both';\n } else if (isNewHire) {\n reason = 'new_hire';\n } else if (isTermination) {\n reason = 'termination';\n }\n\n return { ratio, reason, isProrated: ratio < 1 };\n}\n\n// NOTE: calculateAttendanceDeduction has been moved to calculators/attendance.calculator.ts\n// The calculator version uses banker's rounding and returns a detailed result object.\n// Re-export from core/index.ts for backward compatibility.\n\n/**\n * Get pay period dates for a given month\n *\n * @example\n * const period = getPayPeriod(3, 2024); // March 2024\n */\nexport function getPayPeriod(\n month: number,\n year: number,\n payDay = 28\n): { startDate: Date; endDate: Date; payDate: Date } {\n const startDate = new Date(year, month - 1, 1);\n const endDate = new Date(year, month, 0); // Last day of month\n const payDate = new Date(year, month - 1, Math.min(payDay, endDate.getDate()));\n return { startDate, endDate, payDate };\n}\n\n","/**\n * @classytic/payroll - Pro-Rating Calculator\n *\n * Pure functions for salary pro-rating calculations.\n * No database dependencies - can be used client-side!\n *\n * Handles:\n * - Mid-period hires\n * - Mid-period terminations\n * - Working days (not calendar days)\n * - Holidays exclusion\n *\n * @packageDocumentation\n */\n\nimport { countWorkingDays } from '../core/config.js';\nimport { roundMoney } from '../utils/money.js';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Input for pro-rating calculation\n */\nexport interface ProRatingInput {\n /**\n * Employee hire date\n */\n hireDate: Date;\n\n /**\n * Employee termination date (null if still employed)\n */\n terminationDate: Date | null;\n\n /**\n * Start of the salary period\n */\n periodStart: Date;\n\n /**\n * End of the salary period\n */\n periodEnd: Date;\n\n /**\n * Working days of the week using Date.getDay() convention (0=Sunday, 1=Monday, ..., 6=Saturday)\n * @default [1, 2, 3, 4, 5] (Monday-Friday)\n */\n workingDays: number[];\n\n /**\n * Public holidays to exclude from working days\n * @default []\n */\n holidays?: Date[];\n}\n\n/**\n * Result of pro-rating calculation\n */\nexport interface ProRatingResult {\n /**\n * Whether the salary needs to be pro-rated\n */\n isProRated: boolean;\n\n /**\n * Pro-rating ratio (0-1)\n * 1 = full salary, 0.5 = half salary, etc.\n */\n ratio: number;\n\n /**\n * Total working days in the period\n */\n periodWorkingDays: number;\n\n /**\n * Working days the employee was actually employed\n */\n effectiveWorkingDays: number;\n\n /**\n * Effective start date (max of hire date and period start)\n */\n effectiveStart: Date;\n\n /**\n * Effective end date (min of termination date and period end)\n */\n effectiveEnd: Date;\n}\n\n// ============================================================================\n// Pure Functions\n// ============================================================================\n\n/**\n * Calculate pro-rating for mid-period hires/terminations\n *\n * This function uses WORKING DAYS (not calendar days) for accurate pro-rating.\n *\n * @example\n * ```typescript\n * // Employee hired on March 15th, process March salary\n * const result = calculateProRating({\n * hireDate: new Date('2024-03-15'),\n * terminationDate: null,\n * periodStart: new Date('2024-03-01'),\n * periodEnd: new Date('2024-03-31'),\n * workingDays: [1, 2, 3, 4, 5], // Mon-Fri\n * });\n * \n * console.log(result);\n * // {\n * // isProRated: true,\n * // ratio: 0.64, // Worked 14 out of 22 working days\n * // periodWorkingDays: 22,\n * // effectiveWorkingDays: 14\n * // }\n * ```\n *\n * @param input - Pro-rating calculation parameters\n * @returns Pro-rating result with ratio and working days breakdown\n *\n * @pure This function has no side effects and doesn't access external state\n */\nexport function calculateProRating(input: ProRatingInput): ProRatingResult {\n const { hireDate, terminationDate, periodStart, periodEnd, workingDays, holidays = [] } = input;\n\n const hire = new Date(hireDate);\n const termination = terminationDate ? new Date(terminationDate) : null;\n\n // Determine the actual start and end dates for this employee in this period\n const effectiveStart = hire > periodStart ? hire : periodStart;\n const effectiveEnd = termination && termination < periodEnd ? termination : periodEnd;\n\n // If employee wasn't active during this period at all\n if (effectiveStart > periodEnd || (termination && termination < periodStart)) {\n const periodWorkingDays = countWorkingDays(periodStart, periodEnd, { workingDays, holidays }).workingDays;\n return {\n isProRated: true,\n ratio: 0,\n periodWorkingDays,\n effectiveWorkingDays: 0,\n effectiveStart: periodStart,\n effectiveEnd: periodStart, // Effectively zero days\n };\n }\n\n // Calculate working days for the full period\n const periodWorkingDays = countWorkingDays(periodStart, periodEnd, { workingDays, holidays }).workingDays;\n\n // Calculate working days the employee was actually employed\n const effectiveWorkingDays = countWorkingDays(effectiveStart, effectiveEnd, { workingDays, holidays }).workingDays;\n\n // Calculate ratio\n const ratio = periodWorkingDays > 0 \n ? Math.min(1, Math.max(0, effectiveWorkingDays / periodWorkingDays)) \n : 0;\n\n // Is pro-rated if ratio is less than 1\n const isProRated = ratio < 1;\n\n return {\n isProRated,\n ratio,\n periodWorkingDays,\n effectiveWorkingDays,\n effectiveStart,\n effectiveEnd,\n };\n}\n\n/**\n * Calculate pro-rated amount from base amount and ratio\n *\n * @example\n * ```typescript\n * const proRatedSalary = applyProRating(100000, 0.64); // 64000\n * ```\n *\n * @param baseAmount - Original amount\n * @param ratio - Pro-rating ratio (0-1)\n * @returns Pro-rated amount (rounded)\n *\n * @pure No side effects\n */\nexport function applyProRating(baseAmount: number, ratio: number): number {\n return roundMoney(baseAmount * ratio, 2);\n}\n\n/**\n * Check if pro-rating should be applied for a given hire/termination scenario\n *\n * @param hireDate - Employee hire date\n * @param terminationDate - Employee termination date (null if active)\n * @param periodStart - Salary period start\n * @param periodEnd - Salary period end\n * @returns True if pro-rating is needed\n *\n * @pure No side effects\n */\nexport function shouldProRate(\n hireDate: Date,\n terminationDate: Date | null,\n periodStart: Date,\n periodEnd: Date\n): boolean {\n const hire = new Date(hireDate);\n const termination = terminationDate ? new Date(terminationDate) : null;\n\n // Pro-rate if hired after period start\n if (hire > periodStart) return true;\n\n // Pro-rate if terminated before period end\n if (termination && termination < periodEnd) return true;\n\n return false;\n}\n\n","/**\n * @classytic/payroll - Enums\n *\n * Type-safe enum definitions with const assertions\n * Single source of truth for all enum values\n */\n\nimport type {\n EmploymentType,\n EmployeeStatus,\n Department,\n PaymentFrequency,\n PaymentMethod,\n AllowanceType,\n DeductionType,\n PayrollStatus,\n TerminationReason,\n SalaryBand,\n} from './types.js';\n\n// ============================================================================\n// Employment Type\n// ============================================================================\n\nexport const EMPLOYMENT_TYPE = {\n FULL_TIME: 'full_time',\n PART_TIME: 'part_time',\n CONTRACT: 'contract',\n INTERN: 'intern',\n CONSULTANT: 'consultant',\n} as const satisfies Record<string, EmploymentType>;\n\nexport const EMPLOYMENT_TYPE_VALUES = Object.values(EMPLOYMENT_TYPE);\n\nexport function isValidEmploymentType(value: string): value is EmploymentType {\n return EMPLOYMENT_TYPE_VALUES.includes(value as EmploymentType);\n}\n\n// ============================================================================\n// Employee Status\n// ============================================================================\n\nexport const EMPLOYEE_STATUS = {\n ACTIVE: 'active',\n ON_LEAVE: 'on_leave',\n SUSPENDED: 'suspended',\n TERMINATED: 'terminated',\n} as const satisfies Record<string, EmployeeStatus>;\n\nexport const EMPLOYEE_STATUS_VALUES = Object.values(EMPLOYEE_STATUS);\n\nexport function isValidEmployeeStatus(value: string): value is EmployeeStatus {\n return EMPLOYEE_STATUS_VALUES.includes(value as EmployeeStatus);\n}\n\nexport function isActiveStatus(status: EmployeeStatus): boolean {\n return status === EMPLOYEE_STATUS.ACTIVE;\n}\n\nexport function isEmployedStatus(status: EmployeeStatus): boolean {\n return status !== EMPLOYEE_STATUS.TERMINATED;\n}\n\nexport function canReceiveSalaryStatus(status: EmployeeStatus): boolean {\n return status === EMPLOYEE_STATUS.ACTIVE || status === EMPLOYEE_STATUS.ON_LEAVE;\n}\n\n// ============================================================================\n// Department\n// ============================================================================\n\nexport const DEPARTMENT = {\n MANAGEMENT: 'management',\n TRAINING: 'training',\n SALES: 'sales',\n OPERATIONS: 'operations',\n SUPPORT: 'support',\n HR: 'hr',\n MAINTENANCE: 'maintenance',\n MARKETING: 'marketing',\n FINANCE: 'finance',\n IT: 'it',\n} as const satisfies Record<string, Department>;\n\nexport const DEPARTMENT_VALUES = Object.values(DEPARTMENT);\n\nexport function isValidDepartment(value: string): value is Department {\n return DEPARTMENT_VALUES.includes(value as Department);\n}\n\n// ============================================================================\n// Payment Frequency\n// ============================================================================\n\nexport const PAYMENT_FREQUENCY = {\n MONTHLY: 'monthly',\n BI_WEEKLY: 'bi_weekly',\n WEEKLY: 'weekly',\n HOURLY: 'hourly',\n DAILY: 'daily',\n} as const satisfies Record<string, PaymentFrequency>;\n\nexport const PAYMENT_FREQUENCY_VALUES = Object.values(PAYMENT_FREQUENCY);\n\nexport function isValidPaymentFrequency(value: string): value is PaymentFrequency {\n return PAYMENT_FREQUENCY_VALUES.includes(value as PaymentFrequency);\n}\n\n// ============================================================================\n// Payment Method\n// ============================================================================\n\nexport const PAYMENT_METHOD = {\n BANK: 'bank',\n CASH: 'cash',\n MOBILE: 'mobile',\n BKASH: 'bkash',\n NAGAD: 'nagad',\n ROCKET: 'rocket',\n CHECK: 'check',\n} as const satisfies Record<string, PaymentMethod>;\n\nexport const PAYMENT_METHOD_VALUES = Object.values(PAYMENT_METHOD);\n\nexport function isValidPaymentMethod(value: string): value is PaymentMethod {\n return PAYMENT_METHOD_VALUES.includes(value as PaymentMethod);\n}\n\n// ============================================================================\n// Allowance Type\n// ============================================================================\n\nexport const ALLOWANCE_TYPE = {\n HOUSING: 'housing',\n TRANSPORT: 'transport',\n MEAL: 'meal',\n MOBILE: 'mobile',\n MEDICAL: 'medical',\n EDUCATION: 'education',\n BONUS: 'bonus',\n OTHER: 'other',\n} as const satisfies Record<string, AllowanceType>;\n\nexport const ALLOWANCE_TYPE_VALUES = Object.values(ALLOWANCE_TYPE);\n\nexport function isValidAllowanceType(value: string): value is AllowanceType {\n return ALLOWANCE_TYPE_VALUES.includes(value as AllowanceType);\n}\n\n// ============================================================================\n// Deduction Type\n// ============================================================================\n\nexport const DEDUCTION_TYPE = {\n TAX: 'tax',\n LOAN: 'loan',\n ADVANCE: 'advance',\n PROVIDENT_FUND: 'provident_fund',\n INSURANCE: 'insurance',\n ABSENCE: 'absence',\n OTHER: 'other',\n} as const satisfies Record<string, DeductionType>;\n\nexport const DEDUCTION_TYPE_VALUES = Object.values(DEDUCTION_TYPE);\n\nexport function isValidDeductionType(value: string): value is DeductionType {\n return DEDUCTION_TYPE_VALUES.includes(value as DeductionType);\n}\n\n// ============================================================================\n// Payroll Status\n// ============================================================================\n\nexport const PAYROLL_STATUS = {\n PENDING: 'pending',\n PROCESSING: 'processing',\n PAID: 'paid',\n FAILED: 'failed',\n VOIDED: 'voided',\n REVERSED: 'reversed',\n} as const satisfies Record<string, PayrollStatus>;\n\nexport const PAYROLL_STATUS_VALUES = Object.values(PAYROLL_STATUS);\n\nexport function isValidPayrollStatus(value: string): value is PayrollStatus {\n return PAYROLL_STATUS_VALUES.includes(value as PayrollStatus);\n}\n\nexport function isCompletedPayrollStatus(status: PayrollStatus): boolean {\n return status === PAYROLL_STATUS.PAID;\n}\n\nexport function isPendingPayrollStatus(status: PayrollStatus): boolean {\n return status === PAYROLL_STATUS.PENDING || status === PAYROLL_STATUS.PROCESSING;\n}\n\n/** Check if payroll can be voided (not yet paid) */\nexport function isVoidablePayrollStatus(status: PayrollStatus): boolean {\n return status === PAYROLL_STATUS.PENDING ||\n status === PAYROLL_STATUS.PROCESSING ||\n status === PAYROLL_STATUS.FAILED;\n}\n\n/** Check if payroll requires reversal (already paid) */\nexport function requiresReversalPayrollStatus(status: PayrollStatus): boolean {\n return status === PAYROLL_STATUS.PAID;\n}\n\n/** Check if payroll is in a terminal void/reverse state */\nexport function isVoidedOrReversedStatus(status: PayrollStatus): boolean {\n return status === PAYROLL_STATUS.VOIDED || status === PAYROLL_STATUS.REVERSED;\n}\n\n// ============================================================================\n// Termination Reason\n// ============================================================================\n\nexport const TERMINATION_REASON = {\n RESIGNATION: 'resignation',\n RETIREMENT: 'retirement',\n TERMINATION: 'termination',\n CONTRACT_END: 'contract_end',\n MUTUAL_AGREEMENT: 'mutual_agreement',\n OTHER: 'other',\n} as const satisfies Record<string, TerminationReason>;\n\nexport const TERMINATION_REASON_VALUES = Object.values(TERMINATION_REASON);\n\nexport function isValidTerminationReason(value: string): value is TerminationReason {\n return TERMINATION_REASON_VALUES.includes(value as TerminationReason);\n}\n\n// ============================================================================\n// Salary Band\n// ============================================================================\n\nexport const SALARY_BAND = {\n INTERN: 'intern',\n JUNIOR: 'junior',\n MID: 'mid',\n SENIOR: 'senior',\n LEAD: 'lead',\n EXECUTIVE: 'executive',\n CUSTOM: 'custom',\n} as const satisfies Record<string, SalaryBand>;\n\nexport const SALARY_BAND_VALUES = Object.values(SALARY_BAND);\n\nexport function isValidSalaryBand(value: string): value is SalaryBand {\n return SALARY_BAND_VALUES.includes(value as SalaryBand);\n}\n\n// ============================================================================\n// Leave Type\n// ============================================================================\n\nimport type { LeaveType, LeaveRequestStatus } from './types.js';\n\nexport const LEAVE_TYPE = {\n ANNUAL: 'annual',\n SICK: 'sick',\n UNPAID: 'unpaid',\n MATERNITY: 'maternity',\n PATERNITY: 'paternity',\n BEREAVEMENT: 'bereavement',\n COMPENSATORY: 'compensatory',\n OTHER: 'other',\n} as const satisfies Record<string, LeaveType>;\n\nexport const LEAVE_TYPE_VALUES = Object.values(LEAVE_TYPE);\n\nexport function isValidLeaveType(value: string): value is LeaveType {\n return LEAVE_TYPE_VALUES.includes(value as LeaveType);\n}\n\nexport function isPaidLeaveType(type: LeaveType): boolean {\n return type !== LEAVE_TYPE.UNPAID;\n}\n\n// ============================================================================\n// Leave Request Status\n// ============================================================================\n\nexport const LEAVE_REQUEST_STATUS = {\n PENDING: 'pending',\n APPROVED: 'approved',\n REJECTED: 'rejected',\n CANCELLED: 'cancelled',\n} as const satisfies Record<string, LeaveRequestStatus>;\n\nexport const LEAVE_REQUEST_STATUS_VALUES = Object.values(LEAVE_REQUEST_STATUS);\n\nexport function isValidLeaveRequestStatus(value: string): value is LeaveRequestStatus {\n return LEAVE_REQUEST_STATUS_VALUES.includes(value as LeaveRequestStatus);\n}\n\nexport function isPendingLeaveStatus(status: LeaveRequestStatus): boolean {\n return status === LEAVE_REQUEST_STATUS.PENDING;\n}\n\nexport function isApprovedLeaveStatus(status: LeaveRequestStatus): boolean {\n return status === LEAVE_REQUEST_STATUS.APPROVED;\n}\n\n// ============================================================================\n// Tax Type\n// ============================================================================\n\nexport const TAX_TYPE = {\n INCOME_TAX: 'income_tax',\n SOCIAL_SECURITY: 'social_security',\n HEALTH_INSURANCE: 'health_insurance',\n PENSION: 'pension',\n EMPLOYMENT_INSURANCE: 'employment_insurance',\n LOCAL_TAX: 'local_tax',\n OTHER: 'other',\n} as const satisfies Record<string, import('./types.js').TaxType>;\n\nexport const TAX_TYPE_VALUES = Object.values(TAX_TYPE);\n\nexport function isValidTaxType(value: string): value is import('./types.js').TaxType {\n return TAX_TYPE_VALUES.includes(value as import('./types.js').TaxType);\n}\n\n// ============================================================================\n// Tax Status\n// ============================================================================\n\nexport const TAX_STATUS = {\n PENDING: 'pending',\n SUBMITTED: 'submitted',\n PAID: 'paid',\n CANCELLED: 'cancelled',\n} as const satisfies Record<string, import('./types.js').TaxStatus>;\n\nexport const TAX_STATUS_VALUES = Object.values(TAX_STATUS);\n\nexport function isValidTaxStatus(value: string): value is import('./types.js').TaxStatus {\n return TAX_STATUS_VALUES.includes(value as import('./types.js').TaxStatus);\n}\n\nexport function isPendingTaxStatus(status: import('./types.js').TaxStatus): boolean {\n return status === TAX_STATUS.PENDING;\n}\n\nexport function isPaidTaxStatus(status: import('./types.js').TaxStatus): boolean {\n return status === TAX_STATUS.PAID;\n}\n\nexport function isCancelledTaxStatus(status: import('./types.js').TaxStatus): boolean {\n return status === TAX_STATUS.CANCELLED;\n}\n\n// ============================================================================\n// Default Export\n// ============================================================================\n\nexport default {\n EMPLOYMENT_TYPE,\n EMPLOYMENT_TYPE_VALUES,\n EMPLOYEE_STATUS,\n EMPLOYEE_STATUS_VALUES,\n DEPARTMENT,\n DEPARTMENT_VALUES,\n PAYMENT_FREQUENCY,\n PAYMENT_FREQUENCY_VALUES,\n PAYMENT_METHOD,\n PAYMENT_METHOD_VALUES,\n ALLOWANCE_TYPE,\n ALLOWANCE_TYPE_VALUES,\n DEDUCTION_TYPE,\n DEDUCTION_TYPE_VALUES,\n PAYROLL_STATUS,\n PAYROLL_STATUS_VALUES,\n TERMINATION_REASON,\n TERMINATION_REASON_VALUES,\n SALARY_BAND,\n SALARY_BAND_VALUES,\n LEAVE_TYPE,\n LEAVE_TYPE_VALUES,\n LEAVE_REQUEST_STATUS,\n LEAVE_REQUEST_STATUS_VALUES,\n TAX_TYPE,\n TAX_TYPE_VALUES,\n TAX_STATUS,\n TAX_STATUS_VALUES,\n};\n","/**\n * @classytic/payroll - Validation Utilities\n *\n * Fluent, composable, type-safe validation\n * Clear semantics and helpful error messages\n */\n\nimport type {\n EmployeeStatus,\n EmploymentType,\n Compensation,\n BankDetails,\n EmployeeValidationResult,\n} from '../types.js';\nimport {\n EMPLOYEE_STATUS_VALUES,\n EMPLOYMENT_TYPE_VALUES,\n} from '../enums.js';\nimport { diffInMonths } from './date.js';\n\n// ============================================================================\n// Employee Status Validators\n// ============================================================================\n\n/**\n * Check if employee is active\n */\nexport function isActive(employee: { status?: EmployeeStatus }): boolean {\n return employee?.status === 'active';\n}\n\n/**\n * Check if employee is on leave\n */\nexport function isOnLeave(employee: { status?: EmployeeStatus }): boolean {\n return employee?.status === 'on_leave';\n}\n\n/**\n * Check if employee is suspended\n */\nexport function isSuspended(employee: { status?: EmployeeStatus }): boolean {\n return employee?.status === 'suspended';\n}\n\n/**\n * Check if employee is terminated\n */\nexport function isTerminated(employee: { status?: EmployeeStatus }): boolean {\n return employee?.status === 'terminated';\n}\n\n/**\n * Check if employee is employed (not terminated)\n */\nexport function isEmployed(employee: { status?: EmployeeStatus }): boolean {\n return (\n isActive(employee) || isOnLeave(employee) || isSuspended(employee)\n );\n}\n\n/**\n * Check if employee can receive salary\n */\nexport function canReceiveSalary(employee: {\n status?: EmployeeStatus;\n compensation?: { baseAmount?: number };\n}): boolean {\n return (\n (isActive(employee) || isOnLeave(employee)) &&\n (employee.compensation?.baseAmount ?? 0) > 0\n );\n}\n\n/**\n * Check if employment can be updated\n */\nexport function canUpdateEmployment(employee: { status?: EmployeeStatus }): boolean {\n return !isTerminated(employee);\n}\n\n// ============================================================================\n// Compensation Validators\n// ============================================================================\n\n/**\n * Check if employee has valid compensation\n */\nexport function hasCompensation(employee: {\n compensation?: { baseAmount?: number };\n}): boolean {\n return (employee.compensation?.baseAmount ?? 0) > 0;\n}\n\n/**\n * Check if compensation is valid\n */\nexport function isValidCompensation(compensation?: Compensation): boolean {\n return !!(\n compensation?.baseAmount &&\n compensation.baseAmount > 0 &&\n compensation.frequency &&\n compensation.currency\n );\n}\n\n/**\n * Check if bank details are valid\n */\nexport function isValidBankDetails(bankDetails?: BankDetails): boolean {\n return !!(\n bankDetails?.accountNumber &&\n bankDetails.bankName &&\n bankDetails.accountName\n );\n}\n\n// ============================================================================\n// Probation Validators\n// ============================================================================\n\n/**\n * Check if employee is in probation\n */\nexport function isInProbation(\n employee: { probationEndDate?: Date | null },\n now = new Date()\n): boolean {\n if (!employee?.probationEndDate) return false;\n return new Date(employee.probationEndDate) > now;\n}\n\n/**\n * Check if employee has completed probation\n */\nexport function hasCompletedProbation(\n employee: { probationEndDate?: Date | null },\n now = new Date()\n): boolean {\n if (!employee?.probationEndDate) return true;\n return new Date(employee.probationEndDate) <= now;\n}\n\n// ============================================================================\n// Eligibility Validators\n// ============================================================================\n\n/**\n * Check if employee is eligible for bonus\n */\nexport function isEligibleForBonus(\n employee: { status?: EmployeeStatus; hireDate?: Date },\n requiredMonths = 6\n): boolean {\n if (!isActive(employee) || !employee.hireDate) return false;\n const monthsEmployed = diffInMonths(employee.hireDate, new Date());\n return monthsEmployed >= requiredMonths;\n}\n\n/**\n * Check if employee is eligible for payroll\n */\nexport function isEligibleForPayroll(employee: {\n status?: EmployeeStatus;\n compensation?: { baseAmount?: number };\n bankDetails?: BankDetails;\n}): { eligible: boolean; reasons: string[] } {\n const reasons: string[] = [];\n\n if (!isActive(employee) && !isOnLeave(employee)) {\n reasons.push('Employee is not in active or on-leave status');\n }\n\n if (!hasCompensation(employee)) {\n reasons.push('Employee has no valid compensation');\n }\n\n return {\n eligible: reasons.length === 0,\n reasons,\n };\n}\n\n// ============================================================================\n// Field Validators\n// ============================================================================\n\n/**\n * Check if value is required\n */\nexport function required(fieldName: string): (value: unknown) => string | true {\n return (value: unknown) =>\n value !== undefined && value !== null && value !== ''\n ? true\n : `${fieldName} is required`;\n}\n\n/**\n * Check minimum value\n */\nexport function min(\n minValue: number,\n fieldName: string\n): (value: number) => string | true {\n return (value: number) =>\n value >= minValue ? true : `${fieldName} must be at least ${minValue}`;\n}\n\n/**\n * Check maximum value\n */\nexport function max(\n maxValue: number,\n fieldName: string\n): (value: number) => string | true {\n return (value: number) =>\n value <= maxValue ? true : `${fieldName} must not exceed ${maxValue}`;\n}\n\n/**\n * Check value is in range\n */\nexport function inRange(\n minValue: number,\n maxValue: number,\n fieldName: string\n): (value: number) => string | true {\n return (value: number) =>\n value >= minValue && value <= maxValue\n ? true\n : `${fieldName} must be between ${minValue} and ${maxValue}`;\n}\n\n/**\n * Check value is positive\n */\nexport function isPositive(fieldName: string): (value: number) => string | true {\n return (value: number) =>\n value > 0 ? true : `${fieldName} must be positive`;\n}\n\n/**\n * Check value is one of allowed values\n */\nexport function oneOf<T extends string>(\n allowedValues: readonly T[],\n fieldName: string\n): (value: T) => string | true {\n return (value: T) =>\n allowedValues.includes(value)\n ? true\n : `${fieldName} must be one of: ${allowedValues.join(', ')}`;\n}\n\n// ============================================================================\n// Enum Validators\n// ============================================================================\n\n/**\n * Check if status is valid\n */\nexport function isValidStatus(value: string): value is EmployeeStatus {\n return EMPLOYEE_STATUS_VALUES.includes(value as EmployeeStatus);\n}\n\n/**\n * Check if employment type is valid\n */\nexport function isValidEmploymentType(value: string): value is EmploymentType {\n return EMPLOYMENT_TYPE_VALUES.includes(value as EmploymentType);\n}\n\n// ============================================================================\n// Composite Validators\n// ============================================================================\n\n/**\n * Compose multiple validators\n */\nexport function composeValidators<T>(\n ...validators: Array<(value: T, data?: unknown) => string | true>\n): (value: T, data?: unknown) => string | true {\n return (value: T, data?: unknown) => {\n for (const validator of validators) {\n const result = validator(value, data);\n if (result !== true) return result;\n }\n return true;\n };\n}\n\n/**\n * Create a validator from validation functions\n */\nexport function createValidator<T extends Record<string, unknown>>(\n validationFns: Record<string, (value: unknown, data: T) => string | true>\n): (data: T) => EmployeeValidationResult {\n return (data: T) => {\n const errors: string[] = [];\n\n for (const [field, validator] of Object.entries(validationFns)) {\n const result = validator((data as Record<string, unknown>)[field], data);\n if (result !== true) {\n errors.push(result);\n }\n }\n\n return {\n valid: errors.length === 0,\n errors,\n };\n };\n}\n\n/**\n * Validate required fields exist\n */\nexport function hasRequiredFields(\n obj: Record<string, unknown>,\n fields: string[]\n): { valid: boolean; missing: string[] } {\n const missing = fields.filter(\n (field) => obj[field] === undefined || obj[field] === null\n );\n return {\n valid: missing.length === 0,\n missing,\n };\n}\n\n// ============================================================================\n// Plugin Method Validators\n// ============================================================================\n\n/**\n * Check if an object has a specific method (typically from a Mongoose plugin)\n *\n * @param obj - Object to check\n * @param method - Method name to verify\n * @returns true if object has the method as a function\n *\n * @example\n * ```typescript\n * if (hasPluginMethod(employee, 'canReceiveSalary')) {\n * employee.canReceiveSalary();\n * }\n * ```\n */\nexport function hasPluginMethod(obj: unknown, method: string): boolean {\n return typeof obj === 'object' && obj !== null && typeof (obj as Record<string, unknown>)[method] === 'function';\n}\n\n/**\n * Assert that an object has a specific plugin method, throwing an error if not\n *\n * @param obj - Object to check\n * @param method - Method name to verify\n * @param context - Context message for error\n * @throws Error if method is not found\n */\nexport function assertPluginMethod(obj: unknown, method: string, context: string): void {\n if (!hasPluginMethod(obj, method)) {\n throw new Error(\n `Method '${method}' not found on object. Did you forget to apply the appropriate plugin? ` +\n `Context: ${context}`\n );\n }\n}\n\n// ============================================================================\n// Aliases for backwards compatibility\n// ============================================================================\n\nexport const minValue = min;\nexport const maxValue = max;\nexport const isInRange = inRange;\n\n// ============================================================================\n// Default Export\n// ============================================================================\n\nexport default {\n // Status validators\n isActive,\n isOnLeave,\n isSuspended,\n isTerminated,\n isEmployed,\n canReceiveSalary,\n canUpdateEmployment,\n // Compensation validators\n hasCompensation,\n isValidCompensation,\n isValidBankDetails,\n // Probation validators\n isInProbation,\n hasCompletedProbation,\n // Eligibility validators\n isEligibleForBonus,\n isEligibleForPayroll,\n // Field validators\n required,\n min,\n max,\n inRange,\n isPositive,\n oneOf,\n // Enum validators\n isValidStatus,\n isValidEmploymentType,\n // Composite validators\n composeValidators,\n createValidator,\n hasRequiredFields,\n // Plugin method validators\n hasPluginMethod,\n assertPluginMethod,\n};\n\n","/**\n * @classytic/payroll - Query Builders\n *\n * Fluent API for building MongoDB queries\n * Type-safe, chainable, beautiful\n */\n\nimport mongoose, { Types } from 'mongoose';\nimport type {\n ObjectIdLike,\n EmployeeStatus,\n PayrollStatus,\n Department,\n EmploymentType,\n} from '../types.js';\n\n// ============================================================================\n// ObjectId Helpers\n// ============================================================================\n\n/**\n * Convert string or ObjectId to ObjectId\n */\nexport function toObjectId(id: ObjectIdLike): Types.ObjectId {\n if (id instanceof Types.ObjectId) return id;\n return new Types.ObjectId(id);\n}\n\n/**\n * Safely convert to ObjectId (returns null if invalid)\n */\nexport function safeToObjectId(id: unknown): Types.ObjectId | null {\n if (id instanceof Types.ObjectId) return id;\n if (typeof id === 'string' && Types.ObjectId.isValid(id)) {\n return new Types.ObjectId(id);\n }\n return null;\n}\n\n/**\n * Check if value is valid ObjectId\n */\nexport function isValidObjectId(value: unknown): boolean {\n if (value instanceof Types.ObjectId) return true;\n if (typeof value === 'string') return Types.ObjectId.isValid(value);\n return false;\n}\n\n// ============================================================================\n// Base Query Builder\n// ============================================================================\n\nexport class QueryBuilder<T extends Record<string, unknown> = Record<string, unknown>> {\n protected query: T;\n\n constructor(initialQuery: T = {} as T) {\n this.query = { ...initialQuery };\n }\n\n /**\n * Add where condition\n */\n where<K extends string>(field: K, value: unknown): this {\n (this.query as Record<string, unknown>)[field] = value;\n return this;\n }\n\n /**\n * Add $in condition\n */\n whereIn<K extends string>(field: K, values: unknown[]): this {\n (this.query as Record<string, unknown>)[field] = { $in: values };\n return this;\n }\n\n /**\n * Add $nin condition\n */\n whereNotIn<K extends string>(field: K, values: unknown[]): this {\n (this.query as Record<string, unknown>)[field] = { $nin: values };\n return this;\n }\n\n /**\n * Add $gte condition\n */\n whereGte<K extends string>(field: K, value: unknown): this {\n const existing = (this.query as Record<string, Record<string, unknown>>)[field] || {};\n (this.query as Record<string, unknown>)[field] = { ...existing, $gte: value };\n return this;\n }\n\n /**\n * Add $lte condition\n */\n whereLte<K extends string>(field: K, value: unknown): this {\n const existing = (this.query as Record<string, Record<string, unknown>>)[field] || {};\n (this.query as Record<string, unknown>)[field] = { ...existing, $lte: value };\n return this;\n }\n\n /**\n * Add $gt condition\n */\n whereGt<K extends string>(field: K, value: unknown): this {\n const existing = (this.query as Record<string, Record<string, unknown>>)[field] || {};\n (this.query as Record<string, unknown>)[field] = { ...existing, $gt: value };\n return this;\n }\n\n /**\n * Add $lt condition\n */\n whereLt<K extends string>(field: K, value: unknown): this {\n const existing = (this.query as Record<string, Record<string, unknown>>)[field] || {};\n (this.query as Record<string, unknown>)[field] = { ...existing, $lt: value };\n return this;\n }\n\n /**\n * Add between condition\n */\n whereBetween<K extends string>(field: K, start: unknown, end: unknown): this {\n (this.query as Record<string, unknown>)[field] = { $gte: start, $lte: end };\n return this;\n }\n\n /**\n * Add $exists condition\n */\n whereExists<K extends string>(field: K): this {\n (this.query as Record<string, unknown>)[field] = { $exists: true };\n return this;\n }\n\n /**\n * Add $exists: false condition\n */\n whereNotExists<K extends string>(field: K): this {\n (this.query as Record<string, unknown>)[field] = { $exists: false };\n return this;\n }\n\n /**\n * Add $ne condition\n */\n whereNot<K extends string>(field: K, value: unknown): this {\n (this.query as Record<string, unknown>)[field] = { $ne: value };\n return this;\n }\n\n /**\n * Add regex condition\n */\n whereRegex<K extends string>(field: K, pattern: string, flags = 'i'): this {\n (this.query as Record<string, unknown>)[field] = { $regex: pattern, $options: flags };\n return this;\n }\n\n /**\n * Merge another query\n */\n merge(otherQuery: Record<string, unknown>): this {\n this.query = { ...this.query, ...otherQuery } as T;\n return this;\n }\n\n /**\n * Build and return the query\n */\n build(): T {\n return { ...this.query };\n }\n}\n\n// ============================================================================\n// Employee Query Builder\n// ============================================================================\n\nexport class EmployeeQueryBuilder extends QueryBuilder {\n /**\n * Filter by organization\n */\n forOrganization(organizationId: ObjectIdLike): this {\n return this.where('organizationId', toObjectId(organizationId));\n }\n\n /**\n * Filter by user\n */\n forUser(userId: ObjectIdLike): this {\n return this.where('userId', toObjectId(userId));\n }\n\n /**\n * Filter by employeeId (human-readable ID)\n */\n forEmployeeId(employeeId: string): this {\n return this.where('employeeId', employeeId);\n }\n\n /**\n * Filter by email (for guest employees)\n */\n forEmail(email: string): this {\n return this.where('email', email.toLowerCase().trim());\n }\n\n /**\n * Filter guest employees (no userId)\n */\n guestEmployees(): this {\n return this.where('userId', null);\n }\n\n /**\n * Filter user-linked employees (has userId)\n * Uses $exists: true and $ne: null to exclude both missing fields and null values\n */\n userLinkedEmployees(): this {\n return this.where('userId', { $exists: true, $ne: null } as unknown as mongoose.Types.ObjectId);\n }\n\n /**\n * Filter by status(es)\n */\n withStatus(...statuses: EmployeeStatus[]): this {\n if (statuses.length === 1) {\n return this.where('status', statuses[0]);\n }\n return this.whereIn('status', statuses);\n }\n\n /**\n * Filter active employees\n */\n active(): this {\n return this.withStatus('active');\n }\n\n /**\n * Filter employed employees (not terminated)\n */\n employed(): this {\n return this.whereIn('status', ['active', 'on_leave', 'suspended']);\n }\n\n /**\n * Filter terminated employees\n */\n terminated(): this {\n return this.withStatus('terminated');\n }\n\n /**\n * Filter by department\n */\n inDepartment(department: Department | string): this {\n return this.where('department', department);\n }\n\n /**\n * Filter by position\n */\n inPosition(position: string): this {\n return this.where('position', position);\n }\n\n /**\n * Filter by employment type\n */\n withEmploymentType(type: EmploymentType | string): this {\n return this.where('employmentType', type);\n }\n\n /**\n * Filter by hire date (after)\n */\n hiredAfter(date: Date): this {\n return this.whereGte('hireDate', date);\n }\n\n /**\n * Filter by hire date (before)\n */\n hiredBefore(date: Date): this {\n return this.whereLte('hireDate', date);\n }\n\n /**\n * Filter by minimum salary\n */\n withMinSalary(amount: number): this {\n return this.whereGte('compensation.netSalary', amount);\n }\n\n /**\n * Filter by maximum salary\n */\n withMaxSalary(amount: number): this {\n return this.whereLte('compensation.netSalary', amount);\n }\n\n /**\n * Filter by salary range\n */\n withSalaryRange(min: number, max: number): this {\n return this.whereBetween('compensation.netSalary', min, max);\n }\n}\n\n// ============================================================================\n// Payroll Query Builder\n// ============================================================================\n\nexport class PayrollQueryBuilder extends QueryBuilder {\n /**\n * Filter by organization\n */\n forOrganization(organizationId: ObjectIdLike): this {\n return this.where('organizationId', toObjectId(organizationId));\n }\n\n /**\n * Filter by employee\n *\n * Note: PayrollRecord.employeeId is always ObjectId _id\n * If passing a string business ID, resolve to _id first\n */\n forEmployee(employeeId: ObjectIdLike | string): this {\n // For payroll queries, employeeId field is always the ObjectId _id\n // If a string business ID is passed, caller should resolve to _id first\n return this.where('employeeId', toObjectId(employeeId as ObjectIdLike));\n }\n\n /**\n * Filter by period\n */\n forPeriod(month?: number, year?: number): this {\n if (month !== undefined) {\n this.where('period.month', month);\n }\n if (year !== undefined) {\n this.where('period.year', year);\n }\n return this;\n }\n\n /**\n * Filter by status(es)\n */\n withStatus(...statuses: PayrollStatus[]): this {\n if (statuses.length === 1) {\n return this.where('status', statuses[0]);\n }\n return this.whereIn('status', statuses);\n }\n\n /**\n * Filter paid records\n */\n paid(): this {\n return this.withStatus('paid');\n }\n\n /**\n * Filter pending records\n */\n pending(): this {\n return this.whereIn('status', ['pending', 'processing']);\n }\n\n /**\n * Filter by date range\n */\n inDateRange(start: Date, end: Date): this {\n return this.whereBetween('period.payDate', start, end);\n }\n\n /**\n * Filter exported records\n */\n exported(): this {\n return this.where('exported', true);\n }\n\n /**\n * Filter not exported records\n */\n notExported(): this {\n return this.where('exported', false);\n }\n}\n\n// ============================================================================\n// Factory Functions\n// ============================================================================\n\n/**\n * Create employee query builder\n */\nexport function employee(): EmployeeQueryBuilder {\n return new EmployeeQueryBuilder();\n}\n\n/**\n * Create payroll query builder\n */\nexport function payroll(): PayrollQueryBuilder {\n return new PayrollQueryBuilder();\n}\n\n/**\n * Create generic query builder\n */\nexport function createQueryBuilder<T extends Record<string, unknown> = Record<string, unknown>>(\n initialQuery?: T\n): QueryBuilder<T> {\n return new QueryBuilder(initialQuery);\n}\n\n// ============================================================================\n// Convenience Query Builders\n// ============================================================================\n\n/**\n * Build employee query from options\n */\nexport function buildEmployeeQuery(options: {\n organizationId: ObjectIdLike;\n userId?: ObjectIdLike;\n statuses?: EmployeeStatus[];\n department?: Department | string;\n employmentType?: EmploymentType | string;\n}): Record<string, unknown> {\n const builder = employee().forOrganization(options.organizationId);\n\n if (options.userId) {\n builder.forUser(options.userId);\n }\n if (options.statuses) {\n builder.withStatus(...options.statuses);\n }\n if (options.department) {\n builder.inDepartment(options.department);\n }\n if (options.employmentType) {\n builder.withEmploymentType(options.employmentType);\n }\n\n return builder.build();\n}\n\n/**\n * Build payroll query from options\n */\nexport function buildPayrollQuery(options: {\n employeeId?: ObjectIdLike;\n organizationId?: ObjectIdLike;\n month?: number;\n year?: number;\n statuses?: PayrollStatus[];\n}): Record<string, unknown> {\n const builder = payroll();\n\n if (options.organizationId) {\n builder.forOrganization(options.organizationId);\n }\n if (options.employeeId) {\n builder.forEmployee(options.employeeId);\n }\n if (options.month || options.year) {\n builder.forPeriod(options.month, options.year);\n }\n if (options.statuses) {\n builder.withStatus(...options.statuses);\n }\n\n return builder.build();\n}\n\n// ============================================================================\n// Aggregation Pipeline Helpers\n// ============================================================================\n\n/**\n * Build aggregation pipeline from stages\n */\nexport function buildAggregationPipeline(\n ...stages: Array<Record<string, unknown> | undefined | null>\n): Record<string, unknown>[] {\n return stages.filter((stage): stage is Record<string, unknown> => !!stage);\n}\n\n/**\n * Match stage\n */\nexport function matchStage(query: Record<string, unknown>): Record<string, unknown> {\n return { $match: query };\n}\n\n/**\n * Group stage\n */\nexport function groupStage(\n groupBy: string | null,\n aggregations: Record<string, unknown>\n): Record<string, unknown> {\n return {\n $group: {\n _id: groupBy,\n ...aggregations,\n },\n };\n}\n\n/**\n * Sort stage\n */\nexport function sortStage(sortBy: Record<string, 1 | -1>): Record<string, unknown> {\n return { $sort: sortBy };\n}\n\n/**\n * Limit stage\n */\nexport function limitStage(limit: number): Record<string, unknown> {\n return { $limit: limit };\n}\n\n/**\n * Skip stage\n */\nexport function skipStage(skip: number): Record<string, unknown> {\n return { $skip: skip };\n}\n\n/**\n * Project stage\n */\nexport function projectStage(fields: Record<string, unknown>): Record<string, unknown> {\n return { $project: fields };\n}\n\n/**\n * Lookup stage\n */\nexport function lookupStage(options: {\n from: string;\n localField: string;\n foreignField: string;\n as: string;\n}): Record<string, unknown> {\n return { $lookup: options };\n}\n\n/**\n * Unwind stage\n */\nexport function unwindStage(\n path: string,\n options: { preserveNullAndEmptyArrays?: boolean } = {}\n): Record<string, unknown> {\n return { $unwind: { path, ...options } };\n}\n\n// ============================================================================\n// Default Export\n// ============================================================================\n\nexport default {\n toObjectId,\n safeToObjectId,\n isValidObjectId,\n QueryBuilder,\n EmployeeQueryBuilder,\n PayrollQueryBuilder,\n employee,\n payroll,\n createQueryBuilder,\n buildEmployeeQuery,\n buildPayrollQuery,\n buildAggregationPipeline,\n matchStage,\n groupStage,\n sortStage,\n limitStage,\n skipStage,\n projectStage,\n lookupStage,\n unwindStage,\n};\n\n","/**\n * @classytic/payroll - Leave Utilities\n *\n * Pure, composable leave calculation functions\n */\n\nimport type {\n LeaveBalance,\n LeaveType,\n LeaveInitConfig,\n LeaveSummaryResult,\n WorkingDaysOptions,\n} from '../types.js';\nimport { diffInDays, toUTCDateString } from './date.js';\nimport { roundMoney } from './money.js';\n\n// ============================================================================\n// Default Configurations\n// ============================================================================\n\n/** Default leave allocations by type (days per year) */\nexport const DEFAULT_LEAVE_ALLOCATIONS: Record<LeaveType, number> = {\n annual: 20,\n sick: 10,\n unpaid: 0, // Unlimited\n maternity: 90,\n paternity: 10,\n bereavement: 5,\n compensatory: 0,\n other: 0,\n};\n\n/** Default carry-over limits by type (days) */\nexport const DEFAULT_CARRY_OVER: Record<LeaveType, number> = {\n annual: 5,\n sick: 0,\n unpaid: 0,\n maternity: 0,\n paternity: 0,\n bereavement: 0,\n compensatory: 5,\n other: 0,\n};\n\n// ============================================================================\n// Working Days Calculation\n// ============================================================================\n\n/**\n * Calculate working days between two dates\n * Excludes weekends and optionally holidays\n *\n * @example\n * // March 2024: Get working days for a week\n * const days = calculateLeaveDays(\n * new Date('2024-03-01'),\n * new Date('2024-03-08'),\n * { holidays: [new Date('2024-03-05')] }\n * );\n */\nexport function calculateLeaveDays(\n startDate: Date,\n endDate: Date,\n options: WorkingDaysOptions = {}\n): number {\n const {\n workingDays = [1, 2, 3, 4, 5], // Mon-Fri by default\n holidays = [],\n includeEndDate = true,\n } = options;\n\n const holidaySet = new Set(holidays.map((d) => toUTCDateString(d)));\n\n let count = 0;\n const current = new Date(startDate);\n current.setHours(0, 0, 0, 0);\n\n const end = new Date(endDate);\n end.setHours(0, 0, 0, 0);\n\n if (!includeEndDate) {\n end.setDate(end.getDate() - 1);\n }\n\n while (current <= end) {\n const isWorkDay = workingDays.includes(current.getDay());\n const isHoliday = holidaySet.has(toUTCDateString(current));\n\n if (isWorkDay && !isHoliday) {\n count++;\n }\n\n current.setDate(current.getDate() + 1);\n }\n\n return count;\n}\n\n// ============================================================================\n// Balance Checks\n// ============================================================================\n\n/**\n * Check if employee has sufficient leave balance\n *\n * @example\n * if (hasLeaveBalance(employee, 'annual', 5)) {\n * // Can request 5 days annual leave\n * }\n */\nexport function hasLeaveBalance(\n employee: { leaveBalances?: LeaveBalance[] },\n type: LeaveType,\n days: number,\n year = new Date().getFullYear()\n): boolean {\n // Unpaid leave is always allowed\n if (type === 'unpaid') return true;\n\n const balance = getLeaveBalance(employee, type, year);\n if (!balance) return false;\n\n const available =\n balance.allocated + balance.carriedOver - balance.used - balance.pending;\n return available >= days;\n}\n\n/**\n * Get leave balance for a specific type\n */\nexport function getLeaveBalance(\n employee: { leaveBalances?: LeaveBalance[] },\n type: LeaveType,\n year = new Date().getFullYear()\n): LeaveBalance | undefined {\n return employee.leaveBalances?.find((b) => b.type === type && b.year === year);\n}\n\n/**\n * Get all leave balances for a year\n */\nexport function getLeaveBalances(\n employee: { leaveBalances?: LeaveBalance[] },\n year = new Date().getFullYear()\n): LeaveBalance[] {\n return (employee.leaveBalances || []).filter((b) => b.year === year);\n}\n\n/**\n * Calculate available days for a leave type\n */\nexport function getAvailableDays(\n employee: { leaveBalances?: LeaveBalance[] },\n type: LeaveType,\n year = new Date().getFullYear()\n): number {\n // Unpaid leave has no limit\n if (type === 'unpaid') return Infinity;\n\n const balance = getLeaveBalance(employee, type, year);\n if (!balance) return 0;\n\n return Math.max(\n 0,\n balance.allocated + balance.carriedOver - balance.used - balance.pending\n );\n}\n\n// ============================================================================\n// Leave Summary\n// ============================================================================\n\n/**\n * Get comprehensive leave summary for an employee\n *\n * @example\n * const summary = getLeaveSummary(employee, 2024);\n * console.log(`Available: ${summary.totalAvailable} days`);\n * console.log(`Annual: ${summary.byType.annual?.available || 0} days`);\n */\nexport function getLeaveSummary(\n employee: { leaveBalances?: LeaveBalance[] },\n year = new Date().getFullYear()\n): LeaveSummaryResult {\n const balances = getLeaveBalances(employee, year);\n\n const byType = {} as LeaveSummaryResult['byType'];\n let totalAllocated = 0;\n let totalUsed = 0;\n let totalPending = 0;\n\n for (const balance of balances) {\n const available = Math.max(\n 0,\n balance.allocated + balance.carriedOver - balance.used - balance.pending\n );\n byType[balance.type] = {\n allocated: balance.allocated + balance.carriedOver,\n used: balance.used,\n pending: balance.pending,\n available,\n };\n totalAllocated += balance.allocated + balance.carriedOver;\n totalUsed += balance.used;\n totalPending += balance.pending;\n }\n\n return {\n year,\n balances,\n totalAllocated,\n totalUsed,\n totalPending,\n totalAvailable: Math.max(0, totalAllocated - totalUsed - totalPending),\n byType,\n };\n}\n\n// ============================================================================\n// Balance Initialization\n// ============================================================================\n\n/**\n * Initialize leave balances for a new employee\n *\n * @example\n * // Full allocation for employee hired Jan 1st\n * const balances = initializeLeaveBalances(new Date('2024-01-01'));\n *\n * // Pro-rated for mid-year hire\n * const balances = initializeLeaveBalances(new Date('2024-07-01'), {\n * proRateNewHires: true,\n * });\n */\nexport function initializeLeaveBalances(\n hireDate: Date,\n config: LeaveInitConfig = {},\n year = new Date().getFullYear()\n): LeaveBalance[] {\n const {\n defaultAllocations = DEFAULT_LEAVE_ALLOCATIONS,\n proRateNewHires = true,\n fiscalYearStartMonth = 1,\n } = config;\n\n const fiscalYearStart = new Date(year, fiscalYearStartMonth - 1, 1);\n const fiscalYearEnd = new Date(year + 1, fiscalYearStartMonth - 1, 0);\n\n // Calculate proration if hired mid-year\n let prorationRatio = 1;\n if (proRateNewHires && hireDate > fiscalYearStart) {\n const totalDays = diffInDays(fiscalYearStart, fiscalYearEnd);\n const remainingDays = diffInDays(hireDate, fiscalYearEnd);\n prorationRatio = Math.max(0, Math.min(1, remainingDays / totalDays));\n }\n\n const balances: LeaveBalance[] = [];\n\n for (const [type, allocation] of Object.entries(defaultAllocations)) {\n if (allocation > 0) {\n balances.push({\n type: type as LeaveType,\n allocated: Math.round(allocation * prorationRatio),\n used: 0,\n pending: 0,\n carriedOver: 0,\n year,\n });\n }\n }\n\n return balances;\n}\n\n/**\n * Calculate prorated allocation for mid-year hire\n */\nexport function proRateAllocation(\n fullAllocation: number,\n hireDate: Date,\n fiscalYearStartMonth = 1,\n year = new Date().getFullYear()\n): number {\n const fiscalYearStart = new Date(year, fiscalYearStartMonth - 1, 1);\n\n if (hireDate <= fiscalYearStart) {\n return fullAllocation;\n }\n\n const fiscalYearEnd = new Date(year + 1, fiscalYearStartMonth - 1, 0);\n const totalDays = diffInDays(fiscalYearStart, fiscalYearEnd);\n const remainingDays = Math.max(0, diffInDays(hireDate, fiscalYearEnd));\n\n return Math.round((fullAllocation * remainingDays) / totalDays);\n}\n\n// ============================================================================\n// Payroll Integration\n// ============================================================================\n\n/**\n * Calculate unpaid leave deduction for payroll\n *\n * @example\n * const deduction = calculateUnpaidLeaveDeduction(100000, 5, 22);\n * // Daily rate: 100000 / 22 = 4545\n * // Deduction: 4545 * 5 = 22727\n */\nexport function calculateUnpaidLeaveDeduction(\n baseSalary: number,\n unpaidDays: number,\n workingDaysInMonth: number\n): number {\n if (unpaidDays <= 0 || workingDaysInMonth <= 0) return 0;\n\n const dailyRate = roundMoney(baseSalary / workingDaysInMonth, 2);\n return roundMoney(dailyRate * unpaidDays, 2);\n}\n\n/**\n * Get total unpaid leave days from a list of leave requests\n */\nexport function getUnpaidLeaveDays(\n leaveRequests: Array<{ type: LeaveType; days: number; status: string }>,\n status = 'approved'\n): number {\n return leaveRequests\n .filter((r) => r.type === 'unpaid' && r.status === status)\n .reduce((sum, r) => sum + r.days, 0);\n}\n\n// ============================================================================\n// Year-End Processing\n// ============================================================================\n\n/**\n * Calculate carry-over balances for year-end\n *\n * Creates new year balances for ALL leave types from the current year.\n * Types with carry-over limits get their unused balance carried forward.\n * Types without carry-over (or 0 limit) start fresh with 0 carriedOver.\n *\n * @example\n * // With default allocations - creates balances for all types\n * const newBalances = calculateCarryOver(employee.leaveBalances, {\n * annual: 5,\n * compensatory: 3,\n * });\n * // Merge with existing (don't replace entirely)\n * employee.leaveBalances.push(...newBalances);\n *\n * // With custom allocations (org-specific entitlements)\n * const newBalances = calculateCarryOver(employee.leaveBalances, {\n * annual: 5,\n * compensatory: 3,\n * }, {\n * annual: 25, // Custom org policy\n * sick: 15,\n * });\n */\nexport function calculateCarryOver(\n balances: LeaveBalance[],\n maxCarryOver: Partial<Record<LeaveType, number>> = DEFAULT_CARRY_OVER,\n newYearAllocations: Partial<Record<LeaveType, number>> = DEFAULT_LEAVE_ALLOCATIONS\n): LeaveBalance[] {\n if (!balances.length) return [];\n\n const currentYear = balances[0].year;\n const newYear = currentYear + 1;\n\n // Process ALL leave types from current year, not just those with carry-over\n return balances.map((balance) => {\n const available =\n balance.allocated + balance.carriedOver - balance.used - balance.pending;\n const maxForType = maxCarryOver[balance.type] ?? 0;\n // Only carry over if max > 0, otherwise start fresh\n const carryOver = maxForType > 0\n ? Math.min(Math.max(0, available), maxForType)\n : 0;\n\n return {\n type: balance.type,\n allocated: newYearAllocations[balance.type] ?? DEFAULT_LEAVE_ALLOCATIONS[balance.type] ?? 0,\n used: 0,\n pending: 0,\n carriedOver: carryOver,\n year: newYear,\n };\n });\n}\n\n/**\n * Add accrued leave to balances\n */\nexport function accrueLeaveToBalance(\n balances: LeaveBalance[],\n type: LeaveType,\n amount: number,\n year = new Date().getFullYear()\n): LeaveBalance[] {\n const existingIdx = balances.findIndex((b) => b.type === type && b.year === year);\n\n if (existingIdx >= 0) {\n balances[existingIdx].allocated += amount;\n } else {\n balances.push({\n type,\n allocated: amount,\n used: 0,\n pending: 0,\n carriedOver: 0,\n year,\n });\n }\n\n return balances;\n}\n\n// ============================================================================\n// Default Export\n// ============================================================================\n\nexport default {\n DEFAULT_LEAVE_ALLOCATIONS,\n DEFAULT_CARRY_OVER,\n calculateLeaveDays,\n hasLeaveBalance,\n getLeaveBalance,\n getLeaveBalances,\n getAvailableDays,\n getLeaveSummary,\n initializeLeaveBalances,\n proRateAllocation,\n calculateUnpaidLeaveDeduction,\n getUnpaidLeaveDays,\n calculateCarryOver,\n accrueLeaveToBalance,\n};\n","/**\n * @classytic/payroll - Error Handling\n *\n * Custom error classes with error codes and HTTP status\n */\n\nimport type { ErrorCode, HttpError } from '../types.js';\n\n// ============================================================================\n// Base Error Class\n// ============================================================================\n\nexport class PayrollError extends Error implements HttpError {\n readonly code: ErrorCode;\n readonly status: number;\n readonly context: Record<string, unknown>;\n readonly timestamp: Date;\n\n /**\n * Create a PayrollError.\n *\n * Supports BOTH constructor styles for backwards compatibility:\n * - new PayrollError(message, code?, status?, context?)\n * - new PayrollError(code, status, message, context?)\n */\n constructor(\n messageOrCode: string | ErrorCode,\n codeOrStatus: ErrorCode | number = 'PAYROLL_ERROR',\n statusOrMessage: number | string = 500,\n context: Record<string, unknown> = {}\n ) {\n const isLegacySignature = typeof messageOrCode === 'string' && typeof codeOrStatus === 'string';\n\n const message = isLegacySignature ? (messageOrCode as string) : (statusOrMessage as string);\n super(message);\n\n this.name = this.constructor.name;\n this.code = isLegacySignature ? (codeOrStatus as ErrorCode) : (messageOrCode as ErrorCode);\n this.status = isLegacySignature ? (statusOrMessage as number) : (codeOrStatus as number);\n this.context = context ?? {};\n this.timestamp = new Date();\n\n // Maintains proper stack trace for where error was thrown\n Error.captureStackTrace?.(this, this.constructor);\n }\n\n /**\n * Convert error to JSON for API responses (ClockIn-compatible shape)\n */\n toJSON(): Record<string, unknown> {\n return {\n error: {\n type: this.name,\n code: this.code,\n message: this.message,\n status: this.status,\n context: this.context,\n timestamp: this.timestamp.toISOString(),\n },\n };\n }\n\n /**\n * Check if error is operational (expected) vs programmer error\n */\n isOperational(): boolean {\n return true;\n }\n}\n\n// ============================================================================\n// Specific Error Classes\n// ============================================================================\n\n/**\n * Not initialized error\n */\nexport class NotInitializedError extends PayrollError {\n constructor(message = 'Payroll not initialized. Call Payroll.initialize() first.') {\n super(message, 'NOT_INITIALIZED', 500);\n }\n}\n\n/**\n * Employee not found error\n */\nexport class EmployeeNotFoundError extends PayrollError {\n constructor(employeeId?: string, context?: Record<string, unknown>) {\n super(\n employeeId ? `Employee not found: ${employeeId}` : 'Employee not found',\n 'EMPLOYEE_NOT_FOUND',\n 404,\n context ?? {}\n );\n }\n}\n\n/**\n * Invalid employee error\n */\nexport class InvalidEmployeeError extends PayrollError {\n constructor(message: string, context?: Record<string, unknown>) {\n super(message, 'INVALID_EMPLOYEE', 400, context ?? {});\n }\n}\n\n/**\n * Duplicate payroll error\n */\nexport class DuplicatePayrollError extends PayrollError {\n constructor(\n employeeId: string,\n month: number,\n year: number,\n payrollRunType?: string,\n context?: Record<string, unknown>\n ) {\n const runTypeInfo = payrollRunType ? ` (${payrollRunType})` : '';\n super(\n `Payroll already processed for employee ${employeeId} in ${month}/${year}${runTypeInfo}`,\n 'DUPLICATE_PAYROLL',\n 409,\n { employeeId, month, year, payrollRunType, ...context }\n );\n }\n}\n\n/**\n * Validation error\n */\nexport class ValidationError extends PayrollError {\n readonly errors: string[];\n\n constructor(errors: string | string[], context?: Record<string, unknown>) {\n const errorArray = Array.isArray(errors) ? errors : [errors];\n super(errorArray.join(', '), 'VALIDATION_ERROR', 400, context ?? {});\n this.errors = errorArray;\n }\n}\n\n/**\n * Employee terminated error\n */\nexport class EmployeeTerminatedError extends PayrollError {\n constructor(employeeId?: string, context?: Record<string, unknown>) {\n super(\n employeeId\n ? `Cannot perform operation on terminated employee: ${employeeId}`\n : 'Cannot perform operation on terminated employee',\n 'EMPLOYEE_TERMINATED',\n 400,\n context ?? {}\n );\n }\n}\n\n/**\n * Already processed error\n */\nexport class AlreadyProcessedError extends PayrollError {\n constructor(message: string, context?: Record<string, unknown>) {\n super(message, 'ALREADY_PROCESSED', 409, context ?? {});\n }\n}\n\n/**\n * Not eligible error\n */\nexport class NotEligibleError extends PayrollError {\n constructor(message: string, context?: Record<string, unknown>) {\n super(message, 'NOT_ELIGIBLE', 400, context ?? {});\n }\n}\n\n/**\n * Security error (unauthorized access, cross-organization access, etc.)\n */\nexport class SecurityError extends PayrollError {\n constructor(message: string, context?: Record<string, unknown>) {\n super(message, 'SECURITY_ERROR', 403, context ?? {});\n }\n}\n\n// ============================================================================\n// Error Factory\n// ============================================================================\n\n/**\n * Create error from code\n */\nexport function createError(\n code: ErrorCode,\n message: string,\n context?: Record<string, unknown>\n): PayrollError {\n const statusMap: Record<ErrorCode, number> = {\n PAYROLL_ERROR: 500,\n NOT_INITIALIZED: 500,\n EMPLOYEE_NOT_FOUND: 404,\n INVALID_EMPLOYEE: 400,\n DUPLICATE_PAYROLL: 409,\n VOIDED_PAYROLL_REPROCESS: 409,\n VALIDATION_ERROR: 400,\n EMPLOYEE_TERMINATED: 400,\n ALREADY_PROCESSED: 409,\n NOT_ELIGIBLE: 400,\n SECURITY_ERROR: 403,\n EXPORT_NOT_FOUND: 404,\n EXPORT_ORG_MISMATCH: 403,\n };\n\n return new PayrollError(message, code, statusMap[code] || 500, context ?? {});\n}\n\n// ============================================================================\n// Type Guards\n// ============================================================================\n\n/**\n * Check if error is PayrollError\n */\nexport function isPayrollError(error: unknown): error is PayrollError {\n return error instanceof PayrollError;\n}\n\n/**\n * Check if error has specific code\n */\nexport function isErrorCode(error: unknown, code: ErrorCode): boolean {\n return isPayrollError(error) && error.code === code;\n}\n\n/**\n * Extract error info for logging\n */\nexport function extractErrorInfo(error: unknown): {\n code: string;\n status: number;\n message: string;\n context?: Record<string, unknown>;\n} {\n if (isPayrollError(error)) {\n return {\n code: error.code,\n status: error.status,\n message: error.message,\n context: error.context,\n };\n }\n\n if (error instanceof Error) {\n return {\n code: 'PAYROLL_ERROR',\n status: 500,\n message: error.message,\n };\n }\n\n return {\n code: 'PAYROLL_ERROR',\n status: 500,\n message: String(error),\n };\n}\n\n// ============================================================================\n// Error Handling Utilities\n// ============================================================================\n\n/**\n * Wrap async function with error handling\n */\nexport function withErrorHandling<T extends (...args: unknown[]) => Promise<unknown>>(\n fn: T,\n errorHandler?: (error: unknown) => void\n): T {\n return (async (...args: Parameters<T>) => {\n try {\n return await fn(...args);\n } catch (error) {\n if (errorHandler) {\n errorHandler(error);\n }\n throw error;\n }\n }) as T;\n}\n\n/**\n * Convert unknown error to PayrollError\n */\nexport function toPayrollError(error: unknown): PayrollError {\n if (isPayrollError(error)) {\n return error;\n }\n\n if (error instanceof Error) {\n return new PayrollError(error.message);\n }\n\n return new PayrollError(String(error));\n}\n\n// ============================================================================\n// Legacy Alias\n// ============================================================================\n\nexport { PayrollError as HRMError };\n\n","/**\n * @classytic/payroll - Secure Employee Lookup\n *\n * Multi-tenant safe employee lookup utilities\n * Enforces organizationId isolation on all queries\n */\n\nimport type { Model, ClientSession } from 'mongoose';\nimport type { EmployeeDocument, ObjectIdLike } from '../types.js';\nimport { toObjectId, isValidObjectId } from './query-builders.js';\nimport { EmployeeNotFoundError } from '../errors/index.js';\n\n/**\n * Employee ID mode for explicit disambiguation\n *\n * Controls how the employeeId parameter is interpreted:\n * - 'auto': Auto-detect via isValidObjectId() (default)\n * - 'objectId': Force treat as MongoDB _id (ObjectId)\n * - 'businessId': Force treat as business employeeId string\n *\n * @since v2.3.0\n */\nexport type EmployeeIdMode = 'auto' | 'objectId' | 'businessId';\n\n/**\n * Lookup options for secure employee queries\n */\nexport interface SecureEmployeeLookupOptions {\n /**\n * Organization ID (required for multi-tenant isolation)\n * Can be omitted only in single-tenant mode with auto-inject\n */\n organizationId?: ObjectIdLike;\n\n /**\n * Strict multi-tenant mode\n *\n * When true (default), throws if organizationId is not provided.\n * This prevents accidental cross-tenant data leaks.\n *\n * Set to false ONLY for:\n * - Single-tenant applications with auto-inject configured\n * - Migration scripts with explicit security review\n *\n * @default true (secure by default)\n * @since v3.0.0\n */\n strictMultiTenant?: boolean;\n\n /**\n * Employee's Mongoose _id (ObjectId)\n * Use this when you have the database ID\n * Takes priority over employeeId if both are provided\n */\n _id?: ObjectIdLike;\n\n /**\n * Employee identifier (supports both formats):\n * - ObjectId: employee._id (MongoDB document ID)\n * - String: \"EMP-001\" (business identifier)\n *\n * System auto-detects type by default:\n * - If valid ObjectId → queries by _id field\n * - If string → queries by employeeId field\n *\n * Use employeeIdType to override auto-detection if your business\n * employeeIds look like ObjectIds (24 hex characters).\n *\n * Note: If _id parameter is also provided, _id takes priority\n */\n employeeId?: ObjectIdLike | string;\n\n /**\n * Explicit mode hint for employeeId disambiguation\n *\n * - 'auto' (default): Auto-detect via isValidObjectId()\n * - 'objectId': Force treat as MongoDB _id (ObjectId)\n * - 'businessId': Force treat as business employeeId string\n *\n * Use 'businessId' if your employeeIds are 24-hex strings like\n * \"507f1f77bcf86cd799439011\" to prevent ObjectId collision.\n *\n * @default 'auto'\n * @since v2.3.0\n */\n employeeIdMode?: EmployeeIdMode;\n\n /**\n * User ID reference (optional)\n */\n userId?: ObjectIdLike;\n\n /**\n * Email address (optional)\n */\n email?: string;\n\n /**\n * Mongoose session for transactions\n */\n session?: ClientSession;\n\n /**\n * Fields to populate\n */\n populate?: string | string[];\n}\n\n/**\n * Securely find an employee by various identifiers\n *\n * SECURITY: By default (strictMultiTenant=true), this function REQUIRES organizationId\n * and will throw an error if not provided. This prevents cross-tenant data leaks.\n *\n * @param model - Employee model\n * @param options - Lookup options\n * @returns Employee document or throws EmployeeNotFoundError\n * @throws Error if organizationId not provided in strict mode (default)\n *\n * @example\n * // By ObjectId _id (organizationId required)\n * const emp = await findEmployeeSecure(Employee, {\n * _id: employee._id,\n * organizationId: org._id\n * });\n *\n * @example\n * // By string employeeId\n * const emp = await findEmployeeSecure(Employee, {\n * employeeId: \"EMP-001\",\n * organizationId: org._id\n * });\n *\n * @example\n * // By 24-hex business employeeId (force businessId mode)\n * const emp = await findEmployeeSecure(Employee, {\n * employeeId: \"507f1f77bcf86cd799439011\", // Looks like ObjectId!\n * employeeIdMode: 'businessId', // Force treat as business ID\n * organizationId: org._id\n * });\n *\n * @example\n * // Single-tenant mode (explicitly opt-out of strict mode)\n * const emp = await findEmployeeSecure(Employee, {\n * employeeId: \"EMP-001\",\n * strictMultiTenant: false, // Only for single-tenant apps!\n * });\n */\nexport async function findEmployeeSecure<T extends EmployeeDocument>(\n model: Model<T>,\n options: SecureEmployeeLookupOptions\n): Promise<T> {\n const {\n organizationId,\n strictMultiTenant = true, // SECURITY: Default to strict mode to prevent cross-tenant leaks\n _id,\n employeeId,\n employeeIdMode = 'auto',\n userId,\n email,\n session,\n populate,\n } = options;\n\n // Build query with organizationId isolation\n const query: Record<string, unknown> = {};\n\n // STRICT MODE: Enforce organizationId in multi-tenant apps\n if (strictMultiTenant && !organizationId) {\n throw new Error(\n 'findEmployeeSecure requires organizationId in strict multi-tenant mode. ' +\n 'Set strictMultiTenant: false for single-tenant apps.'\n );\n }\n\n // Include organizationId for multi-tenant safety\n if (organizationId) {\n query.organizationId = toObjectId(organizationId);\n }\n\n // Add identifier filters (priority: _id > employeeId > userId > email)\n if (_id) {\n query._id = toObjectId(_id);\n } else if (employeeId !== undefined) {\n // Resolve employeeId mode based on explicit hint or auto-detection\n const shouldTreatAsObjectId =\n employeeIdMode === 'objectId' ||\n (employeeIdMode === 'auto' && isValidObjectId(employeeId));\n\n const shouldTreatAsBusinessId =\n employeeIdMode === 'businessId' ||\n (employeeIdMode === 'auto' && !isValidObjectId(employeeId));\n\n if (shouldTreatAsObjectId) {\n // It's an ObjectId → query by _id field\n query._id = toObjectId(employeeId as ObjectIdLike);\n } else if (shouldTreatAsBusinessId) {\n // It's a string business identifier → query by employeeId field\n query.employeeId = employeeId as string;\n }\n } else if (userId) {\n query.userId = toObjectId(userId);\n } else if (email) {\n // Normalize email (lowercase + trim) to match schema storage format\n query.email = email.toLowerCase().trim();\n } else {\n throw new Error(\n 'findEmployeeSecure requires at least one identifier: _id, employeeId, userId, or email'\n );\n }\n\n // Build Mongoose query\n let mongooseQuery = model.findOne(query);\n\n if (session) {\n mongooseQuery = mongooseQuery.session(session);\n }\n\n if (populate) {\n const fields = Array.isArray(populate) ? populate : [populate];\n for (const field of fields) {\n mongooseQuery = mongooseQuery.populate(field);\n }\n }\n\n const employee = await mongooseQuery;\n\n if (!employee) {\n // Provide helpful error message\n const identifier = _id\n ? `_id=${_id}`\n : employeeId\n ? `employeeId=${employeeId}`\n : userId\n ? `userId=${userId}`\n : `email=${email}`;\n\n throw new EmployeeNotFoundError(\n `Employee not found: ${identifier}${organizationId ? ` in organization ${organizationId}` : ''}`\n );\n }\n\n return employee;\n}\n\n/**\n * Check if an employee exists securely (with org isolation)\n *\n * @param model - Employee model\n * @param options - Lookup options\n * @returns true if employee exists, false otherwise\n */\nexport async function employeeExistsSecure<T extends EmployeeDocument>(\n model: Model<T>,\n options: SecureEmployeeLookupOptions\n): Promise<boolean> {\n try {\n await findEmployeeSecure(model, options);\n return true;\n } catch (error) {\n if (error instanceof EmployeeNotFoundError) {\n return false;\n }\n throw error;\n }\n}\n\n/**\n * Find multiple employees securely (with org isolation)\n *\n * @param model - Employee model\n * @param options - Query options\n * @returns Array of employee documents\n */\nexport async function findEmployeesSecure<T extends EmployeeDocument>(\n model: Model<T>,\n options: {\n organizationId: ObjectIdLike;\n filter?: Record<string, unknown>;\n session?: ClientSession;\n limit?: number;\n skip?: number;\n sort?: Record<string, 1 | -1>;\n }\n): Promise<T[]> {\n const { organizationId, filter = {}, session, limit, skip, sort } = options;\n\n // CRITICAL: Always include organizationId\n const query = {\n organizationId: toObjectId(organizationId),\n ...filter,\n };\n\n let mongooseQuery = model.find(query);\n\n if (session) {\n mongooseQuery = mongooseQuery.session(session);\n }\n\n if (limit) {\n mongooseQuery = mongooseQuery.limit(limit);\n }\n\n if (skip) {\n mongooseQuery = mongooseQuery.skip(skip);\n }\n\n if (sort) {\n mongooseQuery = mongooseQuery.sort(sort);\n }\n\n return mongooseQuery;\n}\n\n/**\n * Validate organizationId is provided (unless single-tenant with auto-inject)\n *\n * @param organizationId - Organization ID to validate\n * @param operation - Operation name for error message\n * @throws Error if organizationId is missing\n */\nexport function requireOrganizationId(\n organizationId: ObjectIdLike | undefined,\n operation: string\n): void {\n if (!organizationId) {\n throw new Error(\n `${operation} requires organizationId. ` +\n 'In multi-tenant mode, you must explicitly provide organizationId. ' +\n 'In single-tenant mode, ensure autoInject is enabled in configuration.'\n );\n }\n}\n","/**\n * @classytic/payroll - Organization Resolution Utility\n *\n * Smart organization ID resolution with priority chain:\n * 1. Explicit parameter (highest priority)\n * 2. Context.organizationId (from middleware/auth)\n * 3. Single-tenant config (if autoInject enabled)\n * 4. Error (if none found in multi-tenant mode)\n */\n\nimport type { ObjectIdLike, OperationContext } from '../types.js';\nimport type { Container } from '../core/container.js';\nimport { toObjectId } from './query-builders.js';\nimport { Types } from 'mongoose';\n\n/**\n * Container-like interface for organization resolution\n * Only requires the methods we need, allowing any generic Container type\n */\nexport interface ContainerLike {\n isSingleTenant(): boolean;\n getSingleTenantConfig(): { organizationId?: ObjectIdLike; autoInject?: boolean } | null;\n getOrganizationId(): string | null;\n}\n\n/**\n * Organization resolution parameters\n */\nexport interface ResolveOrganizationIdParams {\n /**\n * Explicitly provided organizationId (highest priority)\n */\n explicit?: ObjectIdLike;\n\n /**\n * Operation context with possible organizationId\n */\n context?: OperationContext;\n\n /**\n * Container for single-tenant config access\n * Accepts any Container with any generic types\n */\n container?: ContainerLike;\n\n /**\n * Operation name for error messages\n */\n operation?: string;\n}\n\n/**\n * Smart organization ID resolution\n *\n * Priority Chain:\n * 1. Explicit param (highest)\n * 2. Context.organizationId (middleware/auth)\n * 3. Single-tenant config (if autoInject enabled)\n * 4. Error (if none found)\n *\n * @param params - Resolution parameters\n * @returns Resolved ObjectId\n * @throws Error if organizationId cannot be resolved\n *\n * @example\n * // Explicit param wins\n * const orgId = resolveOrganizationId({\n * explicit: org._id,\n * context: { organizationId: other._id },\n * operation: 'processSalary'\n * });\n * // Returns: org._id\n *\n * @example\n * // Context fallback\n * const orgId = resolveOrganizationId({\n * context: { organizationId: org._id },\n * operation: 'processSalary'\n * });\n * // Returns: org._id from context\n *\n * @example\n * // Single-tenant auto-inject\n * const orgId = resolveOrganizationId({\n * container: singleTenantContainer,\n * operation: 'processSalary'\n * });\n * // Returns: organizationId from container config\n */\nexport function resolveOrganizationId(\n params: ResolveOrganizationIdParams\n): Types.ObjectId {\n const { explicit, context, container, operation } = params;\n\n // 1. Explicit param wins\n if (explicit) {\n return toObjectId(explicit);\n }\n\n // 2. Context from middleware/auth\n if (context?.organizationId) {\n return toObjectId(context.organizationId);\n }\n\n // 3. Single-tenant auto-inject\n // FIX: Check if single-tenant mode is enabled AND autoInject is not disabled\n if (container?.isSingleTenant()) {\n const singleTenantConfig = container.getSingleTenantConfig();\n const autoInjectEnabled = singleTenantConfig?.autoInject !== false; // default: true\n\n if (autoInjectEnabled) {\n const orgId = container.getOrganizationId();\n if (orgId) {\n return toObjectId(orgId);\n }\n // Single-tenant with autoInject but no organizationId configured\n const operationName = operation || 'Operation';\n throw new Error(\n `${operationName}: Single-tenant mode with autoInject enabled requires organizationId in configuration.\\n\\n` +\n 'Fix by configuring organizationId:\\n' +\n ' const payroll = createPayrollInstance()\\n' +\n ' .withModels({ ... })\\n' +\n ' .forSingleTenant({ organizationId: YOUR_ORG_ID, autoInject: true })\\n' +\n ' .build();\\n\\n' +\n 'Or provide organizationId explicitly in each operation:\\n' +\n ` await payroll.${operation || 'method'}({ organizationId: org._id, ... });`\n );\n }\n // autoInject is explicitly disabled - fall through to require explicit orgId\n }\n\n // 4. Error - no organizationId found (multi-tenant or single-tenant with autoInject: false)\n const operationName = operation || 'Operation';\n const isSingleTenantNoAutoInject = container?.isSingleTenant() &&\n container.getSingleTenantConfig()?.autoInject === false;\n\n if (isSingleTenantNoAutoInject) {\n throw new Error(\n `${operationName} requires organizationId (autoInject is disabled in single-tenant config).\\n\\n` +\n 'Provide organizationId explicitly:\\n' +\n ` await payroll.${operation || 'method'}({ organizationId: org._id, ... });`\n );\n }\n\n throw new Error(\n `${operationName} requires organizationId in multi-tenant mode.\\n\\n` +\n 'Options:\\n' +\n '1. Provide it explicitly in parameters\\n' +\n '2. Pass it via context (from middleware/auth)\\n' +\n '3. Enable single-tenant mode with autoInject\\n\\n' +\n 'Example (multi-tenant):\\n' +\n ` await payroll.${operation || 'method'}({ organizationId: org._id, ... });\\n\\n` +\n 'Example (single-tenant):\\n' +\n ' const payroll = createPayrollInstance()\\n' +\n ' .withModels({ ... })\\n' +\n ' .forSingleTenant({ organizationId: myOrg._id, autoInject: true })\\n' +\n ' .build();'\n );\n}\n\n/**\n * Validate that organizationId is present\n *\n * @param organizationId - Organization ID to validate\n * @param operation - Operation name for error message\n * @returns ObjectId if valid\n * @throws Error if organizationId is missing or invalid\n */\nexport function validateOrganizationId(\n organizationId: ObjectIdLike | undefined,\n operation: string\n): Types.ObjectId {\n if (!organizationId) {\n throw new Error(\n `${operation} requires organizationId. ` +\n 'Provide it explicitly, via context, or enable single-tenant mode with autoInject.'\n );\n }\n\n try {\n return toObjectId(organizationId);\n } catch (error) {\n throw new Error(\n `${operation} received invalid organizationId: ${organizationId}. ` +\n 'Must be a valid ObjectId, ObjectId string, or ObjectId-like object.'\n );\n }\n}\n\n/**\n * Try to resolve organizationId without throwing\n *\n * @param params - Resolution parameters\n * @returns ObjectId if resolved, null otherwise\n */\nexport function tryResolveOrganizationId(\n params: ResolveOrganizationIdParams\n): Types.ObjectId | null {\n try {\n return resolveOrganizationId(params);\n } catch {\n return null;\n }\n}\n","/**\n * @classytic/payroll - Employee Identity Helper\n *\n * Dual identity system for employee lookups:\n * - MongoDB ObjectId (_id field)\n * - Business string identifier (employeeId field like \"EMP-001\")\n *\n * Auto-detects identifier type and builds appropriate queries\n */\n\nimport type { ObjectIdLike } from '../types.js';\nimport { Types } from 'mongoose';\nimport { isValidObjectId, toObjectId } from './query-builders.js';\n\n/**\n * Employee identifier type\n */\nexport type EmployeeIdType = 'objectId' | 'string';\n\n/**\n * Employee query filter\n */\nexport interface EmployeeQueryFilter {\n _id?: Types.ObjectId;\n employeeId?: string;\n organizationId: Types.ObjectId;\n}\n\n/**\n * Detect if employeeId is ObjectId or string\n *\n * @param employeeId - Employee identifier (ObjectId or string)\n * @returns 'objectId' if valid ObjectId, 'string' otherwise\n *\n * @example\n * detectEmployeeIdType(employee._id)\n * // Returns: 'objectId'\n *\n * @example\n * detectEmployeeIdType(\"EMP-001\")\n * // Returns: 'string'\n */\nexport function detectEmployeeIdType(\n employeeId: ObjectIdLike | string\n): EmployeeIdType {\n // Check if it's a valid ObjectId\n if (isValidObjectId(employeeId)) {\n return 'objectId';\n }\n\n // Otherwise treat as string business identifier\n return 'string';\n}\n\n// NOTE: buildEmployeeQuery has been removed from this file.\n// Use buildEmployeeQuery from query-builders.ts instead, which has a more flexible API.\n// The removed function duplicated functionality and was intentionally not exported.\n\n/**\n * Normalize employee identifier to consistent format\n *\n * Converts ObjectIdLike to ObjectId, keeps strings as-is\n *\n * @param employeeId - Employee identifier\n * @returns Normalized ObjectId or string\n */\nexport function normalizeEmployeeId(\n employeeId: ObjectIdLike | string\n): Types.ObjectId | string {\n const idType = detectEmployeeIdType(employeeId);\n\n if (idType === 'objectId') {\n return toObjectId(employeeId as ObjectIdLike);\n }\n\n return employeeId as string;\n}\n\n/**\n * Check if value is a string employee ID (not ObjectId)\n *\n * @param value - Value to check\n * @returns true if string employeeId, false if ObjectId\n */\nexport function isStringEmployeeId(value: unknown): value is string {\n return typeof value === 'string' && !isValidObjectId(value);\n}\n\n/**\n * Check if value is an ObjectId employee identifier\n *\n * @param value - Value to check\n * @returns true if ObjectId, false otherwise\n */\nexport function isObjectIdEmployeeId(value: unknown): value is ObjectIdLike {\n return isValidObjectId(value);\n}\n\n/**\n * Format employee identifier for display\n *\n * @param employeeId - Employee identifier\n * @returns Human-readable string\n *\n * @example\n * formatEmployeeId(employee._id)\n * // Returns: \"_id=507f1f77bcf86cd799439011\"\n *\n * @example\n * formatEmployeeId(\"EMP-001\")\n * // Returns: \"employeeId=EMP-001\"\n */\nexport function formatEmployeeId(employeeId: ObjectIdLike | string): string {\n const idType = detectEmployeeIdType(employeeId);\n\n if (idType === 'objectId') {\n return `_id=${toObjectId(employeeId as ObjectIdLike).toString()}`;\n }\n\n return `employeeId=${employeeId}`;\n}\n","/**\r\n * @classytic/payroll - Type Guards\r\n *\r\n * Type-safe utilities for runtime type checking,\r\n * especially for error handling and MongoDB errors.\r\n *\r\n * @module @classytic/payroll/utils/type-guards\r\n */\r\n\r\nimport { MongoServerError } from 'mongodb';\r\nimport type { EmployeeDocument } from '../types.js';\r\n\r\n/**\r\n * Check if error is a MongoDB error\r\n *\r\n * @param error - Unknown error object\r\n * @returns True if MongoDB error\r\n *\r\n * @example\r\n * ```typescript\r\n * try {\r\n * await model.create(data);\r\n * } catch (error) {\r\n * if (isMongoError(error)) {\r\n * console.log('MongoDB error code:', error.code);\r\n * }\r\n * }\r\n * ```\r\n */\r\nexport function isMongoError(error: unknown): error is MongoServerError {\r\n return error instanceof MongoServerError;\r\n}\r\n\r\n/**\r\n * Check if error is a MongoDB duplicate key error (E11000)\r\n *\r\n * @param error - Unknown error object\r\n * @returns True if duplicate key error\r\n *\r\n * @example\r\n * ```typescript\r\n * try {\r\n * await PayrollModel.create(record);\r\n * } catch (error) {\r\n * if (isDuplicateKeyError(error)) {\r\n * const field = parseDuplicateKeyError(error);\r\n * throw new DuplicatePayrollError(`Duplicate ${field}`);\r\n * }\r\n * }\r\n * ```\r\n */\r\nexport function isDuplicateKeyError(error: unknown): error is MongoServerError & { code: 11000 } {\r\n return isMongoError(error) && error.code === 11000;\r\n}\r\n\r\n/**\r\n * Parse field name from MongoDB duplicate key error\r\n *\r\n * @param error - MongoDB duplicate key error\r\n * @returns Field name that caused the duplicate, or 'unknown'\r\n *\r\n * @example\r\n * ```typescript\r\n * if (isDuplicateKeyError(error)) {\r\n * const field = parseDuplicateKeyError(error);\r\n * // field might be 'employeeId', 'email', etc.\r\n * }\r\n * ```\r\n */\r\nexport function parseDuplicateKeyError(error: MongoServerError): string {\r\n const message = error.message || '';\r\n\r\n // MongoDB error message format: \"E11000 duplicate key error collection: ... dup key: { fieldName: ... }\"\r\n const match = message.match(/dup key: \\{ ([^:]+):/);\r\n if (match && match[1]) {\r\n return match[1].trim();\r\n }\r\n\r\n // Fallback: try to extract from errmsg\r\n const errmsgMatch = message.match(/index: ([^_]+)_/);\r\n if (errmsgMatch && errmsgMatch[1]) {\r\n return errmsgMatch[1].trim();\r\n }\r\n\r\n return 'unknown';\r\n}\r\n\r\n/**\r\n * Check if error is a MongoDB transaction error\r\n *\r\n * @param error - Unknown error object\r\n * @returns True if transaction-related error\r\n */\r\nexport function isTransactionError(error: unknown): error is MongoServerError {\r\n if (!isMongoError(error)) return false;\r\n\r\n const message = (error.message || '').toLowerCase();\r\n return (\r\n message.includes('transaction') ||\r\n message.includes('session') ||\r\n message.includes('replica set') ||\r\n error.code === 251 || // NoSuchTransaction\r\n error.code === 225 || // TransactionTooOld\r\n error.code === 244 || // TransactionAborted\r\n error.code === 256 || // TransactionCommitted\r\n error.code === 257 // TransactionCoordinatorSteppingDown\r\n );\r\n}\r\n\r\n/**\r\n * Check if MongoDB transactions are unsupported (standalone server)\r\n *\r\n * @param error - Unknown error object\r\n * @returns True if error indicates transactions are not supported\r\n */\r\nexport function isTransactionUnsupportedError(error: unknown): boolean {\r\n if (!isMongoError(error)) return false;\r\n\r\n const message = (error.message || '').toLowerCase();\r\n return (\r\n message.includes('transaction numbers are only allowed on a replica set member') ||\r\n message.includes('transactions are only supported on replica sets') ||\r\n message.includes('mongos') ||\r\n error.code === 20 // IllegalOperation\r\n );\r\n}\r\n\r\n/**\r\n * Check if error is a connection error\r\n *\r\n * @param error - Unknown error object\r\n * @returns True if connection error\r\n */\r\nexport function isConnectionError(error: unknown): error is MongoServerError {\r\n if (!isMongoError(error)) return false;\r\n\r\n return (\r\n error.code === 11600 || // InterruptedAtShutdown\r\n error.code === 11602 || // InterruptedDueToReplStateChange\r\n error.code === 91 || // ShutdownInProgress\r\n error.code === 89 || // NetworkTimeout\r\n error.code === 6 // HostUnreachable\r\n );\r\n}\r\n\r\n/**\r\n * Type guard: Check if employee is a guest employee (no userId)\r\n *\r\n * @param employee - Employee document\r\n * @returns True if guest employee\r\n *\r\n * @example\r\n * ```typescript\r\n * if (isGuestEmployee(employee)) {\r\n * // Handle guest-specific logic\r\n * console.log('Guest employee:', employee.email);\r\n * }\r\n * ```\r\n */\r\nexport function isGuestEmployee(employee: EmployeeDocument): boolean {\r\n return !employee.userId;\r\n}\r\n\r\n/**\r\n * Type guard: Check if employee has a user ID\r\n *\r\n * @param employee - Employee document\r\n * @returns True if employee has userId (not guest)\r\n */\r\nexport function hasUserId(employee: EmployeeDocument): employee is EmployeeDocument & { userId: NonNullable<EmployeeDocument['userId']> } {\r\n return !!employee.userId;\r\n}\r\n\r\n/**\r\n * Type guard: Check if employee has a customer ID (from populated userId)\r\n *\r\n * @param employee - Employee document\r\n * @returns True if customer ID exists\r\n */\r\nexport function hasCustomerId(employee: EmployeeDocument): boolean {\r\n return !!employee.userId;\r\n}\r\n\r\n/**\r\n * Check if error is a validation error\r\n *\r\n * @param error - Unknown error object\r\n * @returns True if Mongoose/MongoDB validation error\r\n */\r\nexport function isValidationError(error: unknown): error is Error & { name: 'ValidationError' } {\r\n return error instanceof Error && error.name === 'ValidationError';\r\n}\r\n\r\n/**\r\n * Type guard for Error instances\r\n *\r\n * @param error - Unknown value\r\n * @returns True if Error instance\r\n */\r\nexport function isError(error: unknown): error is Error {\r\n return error instanceof Error;\r\n}\r\n\r\n/**\r\n * Safe error message extraction\r\n *\r\n * @param error - Unknown error object\r\n * @returns Error message or generic fallback\r\n *\r\n * @example\r\n * ```typescript\r\n * try {\r\n * await dangerousOperation();\r\n * } catch (error) {\r\n * logger.error(getErrorMessage(error));\r\n * }\r\n * ```\r\n */\r\nexport function getErrorMessage(error: unknown): string {\r\n if (isError(error)) {\r\n return error.message;\r\n }\r\n\r\n if (typeof error === 'string') {\r\n return error;\r\n }\r\n\r\n if (error && typeof error === 'object' && 'message' in error) {\r\n return String(error.message);\r\n }\r\n\r\n return 'Unknown error occurred';\r\n}\r\n","/**\r\n * @classytic/payroll - Error Helpers\r\n *\r\n * Higher-level error handling utilities that combine type guards\r\n * and error classes for common payroll error scenarios.\r\n *\r\n * Uses:\r\n * - `../errors/index.js` for PayrollError classes\r\n * - `./type-guards.js` for MongoDB error type checking\r\n *\r\n * @module @classytic/payroll/utils/error-helpers\r\n */\r\n\r\nimport {\r\n PayrollError,\r\n DuplicatePayrollError,\r\n extractErrorInfo,\r\n toPayrollError,\r\n} from '../errors/index.js';\r\nimport {\r\n isDuplicateKeyError,\r\n parseDuplicateKeyError,\r\n isTransactionError,\r\n isTransactionUnsupportedError,\r\n isConnectionError,\r\n getErrorMessage,\r\n} from './type-guards.js';\r\n\r\n// ============================================================================\r\n// Transaction Error Handling\r\n// ============================================================================\r\n\r\nexport interface TransactionErrorResult {\r\n /** Whether the error is transaction-related */\r\n isTransactionError: boolean;\r\n /** Whether transactions are unsupported (standalone MongoDB) */\r\n isUnsupported: boolean;\r\n /** Whether the operation can be safely retried */\r\n retryable: boolean;\r\n /** Wrapped PayrollError with context */\r\n error: PayrollError;\r\n /** Original error for logging */\r\n originalError: unknown;\r\n}\r\n\r\n/**\r\n * Handle transaction-specific errors with categorization and retry guidance.\r\n *\r\n * Categorizes transaction errors into:\r\n * - **Unsupported**: Standalone MongoDB without replica set (non-retryable, graceful fallback)\r\n * - **Transient**: Network issues, coordinator changes (retryable)\r\n * - **Connection**: Server unreachable, shutdown (retryable after delay)\r\n * - **Other**: Unknown transaction errors (non-retryable)\r\n *\r\n * @param error - Unknown error from a transaction operation\r\n * @param operationName - Name of the operation for context (e.g., 'processSalary')\r\n * @returns Structured error result with retry guidance\r\n *\r\n * @example\r\n * ```typescript\r\n * try {\r\n * await processWithTransaction(session);\r\n * } catch (error) {\r\n * const result = handleTransactionError(error, 'processSalary');\r\n * if (result.isUnsupported) {\r\n * // Fall back to non-transactional processing\r\n * await processWithoutTransaction();\r\n * } else if (result.retryable) {\r\n * // Retry the operation\r\n * await retry(() => processWithTransaction(session));\r\n * } else {\r\n * throw result.error;\r\n * }\r\n * }\r\n * ```\r\n */\r\nexport function handleTransactionError(\r\n error: unknown,\r\n operationName: string\r\n): TransactionErrorResult {\r\n const isUnsupported = isTransactionUnsupportedError(error);\r\n const isTxError = isTransactionError(error);\r\n const isConnError = isConnectionError(error);\r\n\r\n const retryable = isTxError && !isUnsupported;\r\n\r\n const wrappedError = toPayrollError(error);\r\n wrappedError.context.operationName = operationName;\r\n wrappedError.context.transactionError = true;\r\n\r\n if (isUnsupported) {\r\n wrappedError.context.reason = 'transactions_unsupported';\r\n } else if (isConnError) {\r\n wrappedError.context.reason = 'connection_error';\r\n } else if (isTxError) {\r\n wrappedError.context.reason = 'transaction_conflict';\r\n }\r\n\r\n return {\r\n isTransactionError: isTxError || isUnsupported,\r\n isUnsupported,\r\n retryable,\r\n error: wrappedError,\r\n originalError: error,\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Duplicate Key Error Handling\r\n// ============================================================================\r\n\r\nexport interface DuplicateKeyErrorResult {\r\n /** Whether the error is a duplicate key error */\r\n isDuplicate: boolean;\r\n /** The field name that caused the duplicate */\r\n field: string;\r\n /** Wrapped DuplicatePayrollError or PayrollError */\r\n error: PayrollError;\r\n}\r\n\r\n/**\r\n * Handle MongoDB duplicate key errors with field extraction.\r\n *\r\n * Converts raw MongoDB E11000 errors into structured DuplicatePayrollError\r\n * or PayrollError with the duplicate field identified.\r\n *\r\n * @param error - Unknown error from a create/update operation\r\n * @param context - Additional context for the error\r\n * @returns Structured duplicate key error result\r\n *\r\n * @example\r\n * ```typescript\r\n * try {\r\n * await PayrollRecord.create(data);\r\n * } catch (error) {\r\n * const result = handleDuplicateKeyError(error, {\r\n * employeeId: 'EMP-001',\r\n * month: 1,\r\n * year: 2024,\r\n * });\r\n * if (result.isDuplicate) {\r\n * throw result.error; // DuplicatePayrollError with field info\r\n * }\r\n * throw error; // Re-throw non-duplicate errors\r\n * }\r\n * ```\r\n */\r\nexport function handleDuplicateKeyError(\r\n error: unknown,\r\n context?: { employeeId?: string; month?: number; year?: number }\r\n): DuplicateKeyErrorResult {\r\n if (!isDuplicateKeyError(error)) {\r\n return {\r\n isDuplicate: false,\r\n field: '',\r\n error: toPayrollError(error),\r\n };\r\n }\r\n\r\n const field = parseDuplicateKeyError(error);\r\n\r\n const wrappedError = context?.employeeId && context?.month && context?.year\r\n ? new DuplicatePayrollError(context.employeeId, context.month, context.year, undefined, { duplicateField: field })\r\n : toPayrollError(error);\r\n\r\n return {\r\n isDuplicate: true,\r\n field,\r\n error: wrappedError,\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// General Payroll Error Handling\r\n// ============================================================================\r\n\r\nexport interface PayrollErrorResult {\r\n /** Error code for programmatic handling */\r\n code: string;\r\n /** HTTP status code */\r\n status: number;\r\n /** Human-readable error message */\r\n message: string;\r\n /** Additional context */\r\n context: Record<string, unknown>;\r\n /** Whether the error is operational (expected) vs programmer error */\r\n operational: boolean;\r\n /** Whether the operation can be retried */\r\n retryable: boolean;\r\n}\r\n\r\n/**\r\n * Handle any payroll operation error and produce a structured result.\r\n *\r\n * Combines error classification from type-guards with PayrollError\r\n * extraction to produce a unified error result suitable for API responses,\r\n * logging, or retry decisions.\r\n *\r\n * @param error - Unknown error from any payroll operation\r\n * @param operationName - Name of the operation for context\r\n * @returns Structured error result\r\n *\r\n * @example\r\n * ```typescript\r\n * try {\r\n * await payroll.processSalary(params);\r\n * } catch (error) {\r\n * const result = handlePayrollError(error, 'processSalary');\r\n * logger.error(result.message, { code: result.code, context: result.context });\r\n * res.status(result.status).json({ error: result });\r\n * }\r\n * ```\r\n */\r\nexport function handlePayrollError(\r\n error: unknown,\r\n operationName: string\r\n): PayrollErrorResult {\r\n const info = extractErrorInfo(error);\r\n\r\n // Determine if retryable\r\n const retryable =\r\n isConnectionError(error) ||\r\n (isTransactionError(error) && !isTransactionUnsupportedError(error));\r\n\r\n // Determine if operational\r\n const operational = error instanceof PayrollError\r\n ? error.isOperational()\r\n : false;\r\n\r\n return {\r\n code: info.code,\r\n status: info.status,\r\n message: info.message,\r\n context: {\r\n ...info.context,\r\n operationName,\r\n },\r\n operational,\r\n retryable,\r\n };\r\n}\r\n\r\n// ============================================================================\r\n// Error Message Formatting\r\n// ============================================================================\r\n\r\n/**\r\n * Format error for user-facing display.\r\n *\r\n * Strips internal details and produces a clean message suitable\r\n * for API responses or UI display.\r\n *\r\n * @param error - Unknown error\r\n * @returns User-safe error message\r\n */\r\nexport function formatUserError(error: unknown): string {\r\n if (error instanceof PayrollError) {\r\n // PayrollErrors have controlled messages safe for users\r\n return error.message;\r\n }\r\n\r\n if (isDuplicateKeyError(error)) {\r\n const field = parseDuplicateKeyError(error);\r\n return `A record with this ${field} already exists`;\r\n }\r\n\r\n if (isTransactionUnsupportedError(error)) {\r\n return 'Database does not support transactions. Please use a replica set.';\r\n }\r\n\r\n if (isConnectionError(error)) {\r\n return 'Database connection error. Please try again.';\r\n }\r\n\r\n // Generic fallback - don't expose internal details\r\n return 'An unexpected error occurred';\r\n}\r\n"]}
|