{"version":3,"sources":["../../src/shared/utils.ts","../../src/shared/constants.ts","../../src/shared/enums.ts","../../src/server/database/entities.ts","../../src/server/services/events.ts","../../src/server/services/ics.ts","../../src/server/validation.ts"],"sourcesContent":["/**\n * @file Shared utility functions for the calendar library.\n * These utilities are safe to use in both client and server environments.\n */\n\n/**\n * Formats a date in Australian format (dd/mmm/yyyy).\n * Uses UTC date to avoid timezone issues in tests.\n */\nexport function formatDateAustralian(date: Date): string {\n  const months = [\n    'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',\n    'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'\n  ];\n  \n  const day = date.getUTCDate().toString().padStart(2, '0');\n  const month = months[date.getUTCMonth()];\n  const year = date.getUTCFullYear();\n  \n  return `${day}/${month}/${year}`;\n}\n\n/**\n * Calculates the duration between two dates in minutes.\n */\nexport function getDurationMinutes(start: Date, end: Date): number {\n  return Math.round((end.getTime() - start.getTime()) / (1000 * 60));\n}\n\n/**\n * Checks if two dates are on the same day.\n */\nexport function isSameDay(date1: Date, date2: Date): boolean {\n  return date1.toDateString() === date2.toDateString();\n}\n\n/**\n * Gets the start of week (Monday) for a given date.\n */\nexport function getStartOfWeek(date: Date): Date {\n  const result = new Date(date);\n  const day = result.getDay();\n  const diff = result.getDate() - day + (day === 0 ? -6 : 1); // Adjust when day is Sunday\n  result.setDate(diff);\n  result.setHours(0, 0, 0, 0);\n  return result;\n}\n\n/**\n * Gets the end of week (Sunday) for a given date.\n */\nexport function getEndOfWeek(date: Date): Date {\n  const result = new Date(date);\n  const day = result.getDay();\n  const diff = result.getDate() - day + (day === 0 ? 0 : 7); // Adjust when day is Sunday\n  result.setDate(diff);\n  result.setHours(23, 59, 59, 999);\n  return result;\n}\n\n/**\n * Validates an email address.\n */\nexport function isValidEmail(email: string): boolean {\n  const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n  return emailRegex.test(email);\n}\n\n/**\n * Validates a phone number (basic validation).\n */\nexport function isValidPhone(phone: string): boolean {\n  // Basic validation for Australian phone numbers\n  const phoneRegex = /^(\\+61|0)[2-9]\\d{8}$/;\n  return phoneRegex.test(phone.replace(/\\s/g, ''));\n}\n\n/**\n * Generates a unique ID (simple implementation for client-side).\n * Note: For production, use a proper UUID library on the server.\n */\nexport function generateTempId(): string {\n  return `temp_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;\n} ","/**\n * @file Shared constants for the calendar library.\n * These constants are safe to use in both client and server environments.\n */\n\n/**\n * Default currency for financial calculations.\n */\nexport const DEFAULT_CURRENCY = 'AUD';\n\n/**\n * Maximum event duration in hours.\n */\nexport const MAX_EVENT_DURATION_HOURS = 24;\n\n/**\n * Maximum number of recurrence occurrences.\n */\nexport const MAX_RECURRENCE_OCCURRENCES = 365;\n\n/**\n * Default event duration in minutes.\n */\nexport const DEFAULT_EVENT_DURATION_MINUTES = 60;\n\n/**\n * Maximum field lengths for validation.\n */\nexport const MAX_LENGTHS = {\n  EVENT_TITLE: 200,\n  EVENT_DESCRIPTION: 1000,\n  CALENDAR_NAME: 100,\n  CALENDAR_DESCRIPTION: 500,\n  VENUE_NAME: 200,\n  VENUE_ADDRESS: 300,\n  VENUE_CITY: 100,\n  CONTACT_NAME: 200,\n  CONTACT_ROLE: 100,\n  NOTES: 1000,\n  INCOME_DESCRIPTION: 200,\n  EXPENSE_DESCRIPTION: 200,\n  RECEIPT_PATH: 500\n} as const;\n\n/**\n * Date format patterns for different locales.\n */\nexport const DATE_FORMATS = {\n  AUSTRALIAN: 'DD/MM/YYYY',\n  AMERICAN: 'MM/DD/YYYY',\n  ISO: 'YYYY-MM-DD'\n} as const;\n\n/**\n * Time format patterns.\n */\nexport const TIME_FORMATS = {\n  TWELVE_HOUR: '12h',\n  TWENTY_FOUR_HOUR: '24h'\n} as const;\n\n/**\n * Default calendar view options.\n */\nexport const CALENDAR_VIEWS = {\n  DAY: 'day',\n  WEEK: 'week',\n  MONTH: 'month',\n  LIST: 'list'\n} as const;\n\n/**\n * Default start of week (0 = Sunday, 1 = Monday, etc.)\n */\nexport const DEFAULT_START_OF_WEEK = 1; // Monday\n\n/**\n * Australian states and territories.\n */\nexport const AUSTRALIAN_STATES = {\n  NSW: 'New South Wales',\n  VIC: 'Victoria',\n  QLD: 'Queensland',\n  WA: 'Western Australia',\n  SA: 'South Australia',\n  TAS: 'Tasmania',\n  ACT: 'Australian Capital Territory',\n  NT: 'Northern Territory'\n} as const;\n\n/**\n * Common currency codes.\n */\nexport const CURRENCIES = {\n  AUD: 'Australian Dollar',\n  USD: 'US Dollar',\n  EUR: 'Euro',\n  GBP: 'British Pound',\n  CAD: 'Canadian Dollar',\n  NZD: 'New Zealand Dollar'\n} as const;\n\n/**\n * Validation patterns.\n */\nexport const VALIDATION_PATTERNS = {\n  EMAIL: /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/,\n  PHONE_AU: /^(\\+61|0)[2-478](?:[ -]?[0-9]){8}$/,\n  CURRENCY_CODE: /^[A-Z]{3}$/,\n  HEX_COLOR: /^#[0-9A-F]{6}$/i,\n  URL: /^https?:\\/\\/.+/\n} as const;\n\n/**\n * API configuration constants.\n */\nexport const API_CONFIG = {\n  DEFAULT_PAGE_SIZE: 20,\n  MAX_PAGE_SIZE: 100,\n  REQUEST_TIMEOUT: 30000, // 30 seconds\n  RETRY_ATTEMPTS: 3\n} as const;\n\n/**\n * File upload constraints.\n */\nexport const FILE_UPLOAD = {\n  MAX_SIZE_MB: 10,\n  ALLOWED_TYPES: ['image/jpeg', 'image/png', 'image/gif', 'application/pdf'],\n  ALLOWED_EXTENSIONS: ['.jpg', '.jpeg', '.png', '.gif', '.pdf']\n} as const; ","/**\n * @file Shared enums for the calendar library.\n * These enums are safe to use in both client and server environments.\n */\n\n/**\n * Event types for musician-specific functionality.\n */\nexport const EVENT_TYPES = {\n  GIG: 'gig',\n  LESSON: 'lesson',\n  AUDITION: 'audition',\n  PRACTICE: 'practice',\n  REHEARSAL: 'rehearsal',\n  RECORDING: 'recording',\n  MEETING: 'meeting'\n} as const;\n\nexport type EventType = typeof EVENT_TYPES[keyof typeof EVENT_TYPES];\n\n/**\n * Payment status options.\n */\nexport const PAYMENT_STATUS = {\n  PENDING: 'Pending',\n  PAID: 'Paid',\n  OVERDUE: 'Overdue',\n  CANCELLED: 'Cancelled'\n} as const;\n\nexport type PaymentStatus = typeof PAYMENT_STATUS[keyof typeof PAYMENT_STATUS];\n\n/**\n * Event status options.\n */\nexport const EVENT_STATUS = {\n  CONFIRMED: 'Confirmed',\n  TENTATIVE: 'Tentative',\n  CANCELLED: 'Cancelled',\n  COMPLETED: 'Completed'\n} as const;\n\nexport type EventStatus = typeof EVENT_STATUS[keyof typeof EVENT_STATUS];\n\n/**\n * Calendar types.\n */\nexport const CALENDAR_TYPES = {\n  INDIVIDUAL: 'individual',\n  GROUP: 'group',\n  SHARED: 'shared'\n} as const;\n\nexport type CalendarType = typeof CALENDAR_TYPES[keyof typeof CALENDAR_TYPES];\n\n/**\n * Student levels for lessons.\n */\nexport const STUDENT_LEVELS = {\n  BEGINNER: 'Beginner',\n  INTERMEDIATE: 'Intermediate',\n  ADVANCED: 'Advanced',\n  PROFESSIONAL: 'Professional'\n} as const;\n\nexport type StudentLevel = typeof STUDENT_LEVELS[keyof typeof STUDENT_LEVELS];\n\n/**\n * Difficulty levels for pieces/repertoire.\n */\nexport const DIFFICULTY_LEVELS = {\n  EASY: 'Easy',\n  MEDIUM: 'Medium',\n  HARD: 'Hard',\n  EXPERT: 'Expert'\n} as const;\n\nexport type DifficultyLevel = typeof DIFFICULTY_LEVELS[keyof typeof DIFFICULTY_LEVELS];\n\n/**\n * Common musical genres.\n */\nexport const GENRES = {\n  CLASSICAL: 'Classical',\n  JAZZ: 'Jazz',\n  ROCK: 'Rock',\n  POP: 'Pop',\n  BLUES: 'Blues',\n  COUNTRY: 'Country',\n  FOLK: 'Folk',\n  ELECTRONIC: 'Electronic',\n  WORLD: 'World Music',\n  OTHER: 'Other'\n} as const;\n\nexport type Genre = typeof GENRES[keyof typeof GENRES];\n\n/**\n * Common instruments.\n */\nexport const INSTRUMENTS = {\n  PIANO: 'Piano',\n  GUITAR: 'Guitar',\n  VIOLIN: 'Violin',\n  DRUMS: 'Drums',\n  BASS: 'Bass',\n  SAXOPHONE: 'Saxophone',\n  TRUMPET: 'Trumpet',\n  FLUTE: 'Flute',\n  CELLO: 'Cello',\n  VOICE: 'Voice',\n  OTHER: 'Other'\n} as const;\n\nexport type Instrument = typeof INSTRUMENTS[keyof typeof INSTRUMENTS]; ","/**\n * @file TypeORM entities for the calendar library.\n * These entities are for server-side use only and require Node.js/TypeORM.\n */\n\nimport { Entity, PrimaryGeneratedColumn, Column, ManyToOne, OneToMany, CreateDateColumn, UpdateDateColumn, JoinColumn } from 'typeorm';\n\n/**\n * Represents a calendar.\n */\n@Entity()\nexport class Calendar {\n  @PrimaryGeneratedColumn('uuid')\n  id!: string;\n\n  @Column()\n  name!: string;\n\n  @Column({ nullable: true })\n  description?: string;\n\n  /**\n   * The type of calendar.\n   * 'individual' - A personal calendar for a single user.\n   * 'group' - A shared calendar for a team or band.\n   */\n  @Column()\n  type!: string;\n}\n\n/**\n * Represents a venue or location for events.\n */\n@Entity()\nexport class Venue {\n  @PrimaryGeneratedColumn('uuid')\n  id!: string;\n\n  @Column()\n  name!: string;\n\n  @Column({ nullable: true })\n  address?: string;\n\n  @Column({ nullable: true })\n  city?: string;\n\n  @Column({ nullable: true })\n  state?: string;\n\n  @Column({ nullable: true })\n  country?: string;\n\n  @Column({ nullable: true })\n  website?: string;\n\n  @Column({ nullable: true })\n  contactName?: string;\n\n  @Column({ nullable: true })\n  contactEmail?: string;\n\n  @Column({ nullable: true })\n  contactPhone?: string;\n\n  @Column({ nullable: true })\n  notes?: string;\n}\n\n/**\n * Represents a person or organization (student, band member, promoter, etc.)\n */\n@Entity()\nexport class Contact {\n  @PrimaryGeneratedColumn('uuid')\n  id!: string;\n\n  @Column()\n  name!: string;\n\n  @Column({ nullable: true })\n  email?: string;\n\n  @Column({ nullable: true })\n  phone?: string;\n\n  @Column({ nullable: true })\n  role?: string; // 'student', 'band-member', 'promoter', 'sound-engineer', etc.\n\n  @Column({ nullable: true })\n  notes?: string;\n}\n\n/**\n * Represents a calendar event with musician-specific capabilities.\n */\n@Entity()\nexport class Event {\n  @PrimaryGeneratedColumn('uuid')\n  id!: string;\n\n  @Column()\n  summary!: string;\n\n  @Column({ nullable: true })\n  description?: string;\n\n  @Column()\n  start!: Date;\n\n  @Column()\n  end!: Date;\n\n  /**\n   * An RFC 5545 recurrence rule string.\n   * @see https://icalendar.org/rrule-tool.html\n   * @example 'FREQ=WEEKLY;BYDAY=MO,WE,FR;UNTIL=20251231T235959Z'\n   */\n  @Column({ nullable: true })\n  recurrenceRule?: string;\n\n  /**\n   * Event type: 'gig', 'lesson', 'audition', 'practice', 'rehearsal', 'recording', 'meeting', etc.\n   */\n  @Column()\n  type!: string;\n\n  // Musician-specific fields\n  @Column({ nullable: true })\n  genre?: string; // 'Jazz', 'Classical', 'Rock', etc.\n\n  @Column({ nullable: true })\n  instrument?: string; // Primary instrument for this event\n\n  @Column({ nullable: true })\n  difficulty?: string; // 'Beginner', 'Intermediate', 'Advanced', 'Professional'\n\n  @Column({ nullable: true })\n  repertoire?: string; // Songs/pieces to be performed or practiced\n\n  @Column({ nullable: true })\n  setList?: string; // JSON string of songs in order\n\n  @Column({ nullable: true })\n  equipmentNeeded?: string; // JSON array of required equipment\n\n  @Column({ nullable: true })\n  dresscode?: string; // 'Formal', 'Casual', 'Black tie', etc.\n\n  @Column({ nullable: true })\n  soundcheckTime?: Date; // For gigs\n\n  @Column({ nullable: true })\n  loadInTime?: Date; // For gigs\n\n  @Column({ nullable: true })\n  paymentStatus?: string; // 'Pending', 'Paid', 'Overdue', 'Cancelled'\n\n  @Column({ nullable: true })\n  paymentDueDate?: Date;\n\n  @Column({ nullable: true })\n  studentLevel?: string; // For lessons: 'Grade 1', 'Grade 8', 'Diploma', etc.\n\n  @Column({ nullable: true })\n  lessonFocus?: string; // For lessons: 'Technique', 'Theory', 'Repertoire', etc.\n\n  @Column({ nullable: true })\n  auditionPiece?: string; // For auditions\n\n  @Column({ nullable: true })\n  auditionRequirements?: string; // For auditions\n\n  @Column({ nullable: true })\n  practiceGoals?: string; // For practice sessions\n\n  @Column({ nullable: true })\n  rehearsalNotes?: string; // For rehearsals\n\n  @Column({ nullable: true })\n  status?: string; // 'Confirmed', 'Tentative', 'Cancelled', 'Completed'\n\n  @CreateDateColumn()\n  createdAt!: Date;\n\n  @UpdateDateColumn()\n  updatedAt!: Date;\n\n  // Relationships\n  @ManyToOne(() => Calendar)\n  @JoinColumn()\n  calendar!: Calendar;\n\n  @ManyToOne(() => Venue, { nullable: true })\n  @JoinColumn()\n  venue?: Venue;\n\n  @ManyToOne(() => Contact, { nullable: true })\n  @JoinColumn()\n  primaryContact?: Contact; // Main contact for this event (student, promoter, etc.)\n\n  @OneToMany(() => EventIncome, (income) => income.event, { cascade: true })\n  income?: EventIncome[];\n\n  @OneToMany(() => EventExpense, (expense) => expense.event, { cascade: true })\n  expenses?: EventExpense[];\n}\n\n/**\n * Represents income for an event (gig fees, lesson payments, etc.)\n */\n@Entity()\nexport class EventIncome {\n  @PrimaryGeneratedColumn('uuid')\n  id!: string;\n\n  @Column()\n  description!: string; // 'Door charge', 'Flat fee', 'Merch sales', 'Tips', etc.\n\n  @Column('decimal', { precision: 10, scale: 2 })\n  amount!: number;\n\n  @Column({ default: 'AUD' })\n  currency!: string;\n\n  @Column({ nullable: true })\n  notes?: string;\n\n  @ManyToOne(() => Event, (event) => event.income)\n  @JoinColumn()\n  event!: Event;\n}\n\n/**\n * Represents expenses for an event (gear hire, travel, food, etc.)\n */\n@Entity()\nexport class EventExpense {\n  @PrimaryGeneratedColumn('uuid')\n  id!: string;\n\n  @Column()\n  description!: string; // 'Gear hire', 'Food & bev', 'Marketing', 'Travel', etc.\n\n  @Column('decimal', { precision: 10, scale: 2 })\n  amount!: number;\n\n  @Column({ default: 'AUD' })\n  currency!: string;\n\n  @Column({ nullable: true })\n  receipt?: string; // Path to receipt image/file\n\n  @Column({ nullable: true })\n  notes?: string;\n\n  @ManyToOne(() => Event, (event) => event.expenses)\n  @JoinColumn()\n  event!: Event;\n} ","/**\n * @file Server-side event services for the calendar library.\n * These services handle all database operations and business logic for events.\n */\n\nimport { DataSource, Between, Repository } from 'typeorm';\nimport { Event, Calendar, EventIncome, EventExpense, Venue, Contact } from '../database/entities';\nimport type { Changes, FinancialSummary } from '../../shared/types';\n\nlet db: DataSource;\nlet eventRepository: Repository<Event>;\nlet calendarRepository: Repository<Calendar>;\nlet incomeRepository: Repository<EventIncome>;\nlet expenseRepository: Repository<EventExpense>;\nlet venueRepository: Repository<Venue>;\nlet contactRepository: Repository<Contact>;\n\n/**\n * Initializes the database connection and repositories.\n */\nexport function initDb(dataSource: DataSource): DataSource {\n  db = dataSource;\n  eventRepository = dataSource.getRepository(Event);\n  calendarRepository = dataSource.getRepository(Calendar);\n  incomeRepository = dataSource.getRepository(EventIncome);\n  expenseRepository = dataSource.getRepository(EventExpense);\n  venueRepository = dataSource.getRepository(Venue);\n  contactRepository = dataSource.getRepository(Contact);\n  return db;\n}\n\nfunction getDb(): DataSource {\n  if (!db) {\n    throw new Error('Database has not been initialized. Call initDb() first.');\n  }\n  return db;\n}\n\n/**\n * Fetches changes to events since a given date for sync purposes.\n */\nexport async function fetchChanges(calendarId: string, since: Date): Promise<Changes> {\n  const eventRepository = getDb().getRepository(Event);\n\n  const updated = await eventRepository.find({\n    where: {\n      calendar: { id: calendarId },\n      updatedAt: Between(since, new Date()),\n    },\n    relations: ['calendar', 'venue', 'primaryContact'],\n  });\n\n  // TypeORM doesn't have a built-in way to track deletions,\n  // so we assume the consumer application handles this, possibly with a soft-delete mechanism.\n  // For this implementation, we'll return an empty array for deleted events.\n  const deleted: string[] = [];\n\n  return {\n    updated: updated.map(event => ({\n      id: event.id,\n      summary: event.summary,\n      description: event.description,\n      start: event.start,\n      end: event.end,\n      recurrenceRule: event.recurrenceRule,\n      type: event.type,\n      genre: event.genre,\n      instrument: event.instrument,\n      difficulty: event.difficulty,\n      repertoire: event.repertoire,\n      setList: event.setList,\n      equipmentNeeded: event.equipmentNeeded,\n      dresscode: event.dresscode,\n      soundcheckTime: event.soundcheckTime,\n      loadInTime: event.loadInTime,\n      paymentStatus: event.paymentStatus,\n      paymentDueDate: event.paymentDueDate,\n      studentLevel: event.studentLevel,\n      lessonFocus: event.lessonFocus,\n      auditionPiece: event.auditionPiece,\n      auditionRequirements: event.auditionRequirements,\n      practiceGoals: event.practiceGoals,\n      rehearsalNotes: event.rehearsalNotes,\n      status: event.status,\n      createdAt: event.createdAt,\n      updatedAt: event.updatedAt,\n      calendarId: event.calendar.id,\n      venueId: event.venue?.id,\n      primaryContactId: event.primaryContact?.id,\n    })),\n    deleted,\n  };\n}\n\n/**\n * Creates a new event.\n */\nexport async function createEvent(\n  calendarId: string,\n  eventData: Omit<Event, 'id' | 'updatedAt' | 'createdAt' | 'calendar'>\n): Promise<Event> {\n  const eventRepository = getDb().getRepository(Event);\n  const calendarRepository = getDb().getRepository(Calendar);\n  \n  const calendar = await calendarRepository.findOneBy({ id: calendarId });\n  if (!calendar) {\n    throw new Error(`Calendar with ID \"${calendarId}\" not found.`);\n  }\n\n  const newEvent = eventRepository.create({\n    ...eventData,\n    calendar,\n  });\n  return eventRepository.save(newEvent);\n}\n\n/**\n * Updates an existing event.\n */\nexport async function updateEvent(\n  eventId: string,\n  updates: Partial<Omit<Event, 'id' | 'calendar' | 'createdAt' | 'updatedAt'>>\n): Promise<Event> {\n  const eventRepository = getDb().getRepository(Event);\n  \n  await eventRepository.update(eventId, updates);\n  const updatedEvent = await eventRepository.findOneBy({ id: eventId });\n  \n  if (!updatedEvent) {\n    throw new Error(`Event with ID \"${eventId}\" not found.`);\n  }\n  \n  return updatedEvent;\n}\n\n/**\n * Deletes an event.\n */\nexport async function deleteEvent(eventId: string): Promise<void> {\n  const eventRepository = getDb().getRepository(Event);\n  await eventRepository.delete({ id: eventId });\n}\n\n/**\n * Gets all events for a calendar.\n */\nexport async function getEventsByCalendar(calendarId: string): Promise<Event[]> {\n  const eventRepository = getDb().getRepository(Event);\n  return eventRepository.find({\n    where: { calendar: { id: calendarId } },\n    relations: ['calendar', 'venue', 'primaryContact', 'income', 'expenses'],\n  });\n}\n\n/**\n * Gets events by type for a calendar.\n */\nexport async function getEventsByType(calendarId: string, type: string): Promise<Event[]> {\n  const eventRepository = getDb().getRepository(Event);\n  return eventRepository.find({\n    where: { \n      calendar: { id: calendarId },\n      type: type\n    },\n    relations: ['calendar', 'venue', 'primaryContact', 'income', 'expenses'],\n    order: { start: 'ASC' }\n  });\n}\n\n/**\n * Gets upcoming gigs for a calendar.\n */\nexport async function getUpcomingGigs(calendarId: string, limit: number = 10): Promise<Event[]> {\n  const eventRepository = getDb().getRepository(Event);\n  const now = new Date();\n  \n  return eventRepository.find({\n    where: { \n      calendar: { id: calendarId },\n      type: 'gig',\n      start: Between(now, new Date(now.getTime() + 365 * 24 * 60 * 60 * 1000)) // Next year\n    },\n    relations: ['calendar', 'venue', 'primaryContact', 'income', 'expenses'],\n    order: { start: 'ASC' },\n    take: limit\n  });\n}\n\n/**\n * Gets student lessons for a specific student contact.\n */\nexport async function getStudentLessons(calendarId: string, studentContactId: string): Promise<Event[]> {\n  const eventRepository = getDb().getRepository(Event);\n  \n  return eventRepository.find({\n    where: { \n      calendar: { id: calendarId },\n      type: 'lesson',\n      primaryContact: { id: studentContactId }\n    },\n    relations: ['calendar', 'venue', 'primaryContact', 'income', 'expenses'],\n    order: { start: 'ASC' }\n  });\n}\n\n/**\n * Adds income to an event.\n */\nexport async function addEventIncome(eventId: string, income: Omit<EventIncome, 'id' | 'event'>): Promise<EventIncome> {\n  const event = await eventRepository.findOne({ where: { id: eventId } });\n  if (!event) {\n    throw new Error(`Event with id ${eventId} not found`);\n  }\n\n  const eventIncome = incomeRepository.create({\n    ...income,\n    event\n  });\n\n  return await incomeRepository.save(eventIncome);\n}\n\n/**\n * Adds expense to an event.\n */\nexport async function addEventExpense(eventId: string, expense: Omit<EventExpense, 'id' | 'event'>): Promise<EventExpense> {\n  const event = await eventRepository.findOne({ where: { id: eventId } });\n  if (!event) {\n    throw new Error(`Event with id ${eventId} not found`);\n  }\n\n  const eventExpense = expenseRepository.create({\n    ...expense,\n    event\n  });\n\n  return await expenseRepository.save(eventExpense);\n}\n\n/**\n * Gets all income for an event.\n */\nexport async function getEventIncome(eventId: string): Promise<EventIncome[]> {\n  return await incomeRepository.find({\n    where: { event: { id: eventId } }\n  });\n}\n\n/**\n * Gets all expenses for an event.\n */\nexport async function getEventExpenses(eventId: string): Promise<EventExpense[]> {\n  return await expenseRepository.find({\n    where: { event: { id: eventId } }\n  });\n}\n\n/**\n * Calculates total income for an event.\n */\nexport async function calculateEventIncome(eventId: string): Promise<number> {\n  const income = await getEventIncome(eventId);\n  return income.reduce((total, item) => total + Number(item.amount), 0);\n}\n\n/**\n * Calculates total expenses for an event.\n */\nexport async function calculateEventExpenses(eventId: string): Promise<number> {\n  const expenses = await getEventExpenses(eventId);\n  return expenses.reduce((total, item) => total + Number(item.amount), 0);\n}\n\n/**\n * Calculates net profit/loss for an event.\n */\nexport async function calculateEventProfit(eventId: string): Promise<number> {\n  const totalIncome = await calculateEventIncome(eventId);\n  const totalExpenses = await calculateEventExpenses(eventId);\n  return totalIncome - totalExpenses;\n}\n\n/**\n * Gets financial summary for multiple events.\n */\nexport async function getFinancialSummary(eventIds: string[]): Promise<FinancialSummary> {\n  let totalIncome = 0;\n  let totalExpenses = 0;\n\n  for (const eventId of eventIds) {\n    totalIncome += await calculateEventIncome(eventId);\n    totalExpenses += await calculateEventExpenses(eventId);\n  }\n\n  const netProfit = totalIncome - totalExpenses;\n  const eventCount = eventIds.length;\n  const averageProfitPerEvent = eventCount > 0 ? netProfit / eventCount : 0;\n\n  return {\n    totalIncome,\n    totalExpenses,\n    netProfit,\n    eventCount,\n    averageProfitPerEvent,\n  };\n}\n\n/**\n * Updates event income.\n */\nexport async function updateEventIncome(incomeId: string, updates: Partial<EventIncome>): Promise<EventIncome> {\n  await incomeRepository.update(incomeId, updates);\n  const updatedIncome = await incomeRepository.findOneBy({ id: incomeId });\n  \n  if (!updatedIncome) {\n    throw new Error(`Income with ID \"${incomeId}\" not found.`);\n  }\n  \n  return updatedIncome;\n}\n\n/**\n * Updates event expense.\n */\nexport async function updateEventExpense(expenseId: string, updates: Partial<EventExpense>): Promise<EventExpense> {\n  await expenseRepository.update(expenseId, updates);\n  const updatedExpense = await expenseRepository.findOneBy({ id: expenseId });\n  \n  if (!updatedExpense) {\n    throw new Error(`Expense with ID \"${expenseId}\" not found.`);\n  }\n  \n  return updatedExpense;\n}\n\n/**\n * Deletes event income.\n */\nexport async function deleteEventIncome(incomeId: string): Promise<void> {\n  await incomeRepository.delete({ id: incomeId });\n}\n\n/**\n * Deletes event expense.\n */\nexport async function deleteEventExpense(expenseId: string): Promise<void> {\n  await expenseRepository.delete({ id: expenseId });\n}\n\n/**\n * Creates a new venue.\n */\nexport async function createVenue(venue: Omit<Venue, 'id'>): Promise<Venue> {\n  const newVenue = venueRepository.create(venue);\n  return await venueRepository.save(newVenue);\n}\n\n/**\n * Creates a new contact.\n */\nexport async function createContact(contact: Omit<Contact, 'id'>): Promise<Contact> {\n  const newContact = contactRepository.create(contact);\n  return await contactRepository.save(newContact);\n} ","/**\n * @file Server-side ICS generation service for the calendar library.\n * This service handles iCalendar feed generation and requires Node.js.\n */\n\nimport ical from 'ical-generator';\nimport { getEventsByCalendar } from './events';\nimport type { Calendar } from '../database/entities';\n\n/**\n * Generates an iCalendar (.ics) feed for a given calendar.\n * This is a server-side operation that requires Node.js.\n *\n * @param calendar The calendar object.\n * @param calendarId The ID of the calendar to generate the feed for.\n * @returns A promise that resolves with the iCalendar feed as a string.\n */\nexport async function generateIcs(calendar: Calendar, calendarId: string): Promise<string> {\n  const cal = ical({ \n    name: calendar.name,\n    description: calendar.description,\n    timezone: 'Australia/Sydney' // Default to Australian timezone\n  });\n  \n  const events = await getEventsByCalendar(calendarId);\n  \n  events.forEach(event => {\n    const icalEvent = cal.createEvent({\n      start: event.start,\n      end: event.end,\n      summary: event.summary,\n      description: event.description,\n      location: event.venue ? [\n        event.venue.name,\n        event.venue.address,\n        event.venue.city,\n        event.venue.state,\n        event.venue.country\n      ].filter(Boolean).join(', ') : undefined,\n    });\n\n    // Add musician-specific properties as extended properties\n    if (event.type) {\n      icalEvent.x('X-EVENT-TYPE', event.type);\n    }\n    \n    if (event.genre) {\n      icalEvent.x('X-GENRE', event.genre);\n    }\n    \n    if (event.instrument) {\n      icalEvent.x('X-INSTRUMENT', event.instrument);\n    }\n    \n    if (event.paymentStatus) {\n      icalEvent.x('X-PAYMENT-STATUS', event.paymentStatus);\n    }\n    \n    if (event.status) {\n      icalEvent.x('X-STATUS', event.status);\n    }\n\n    // Add contact information if available\n    if (event.primaryContact) {\n      icalEvent.x('X-CONTACT-NAME', event.primaryContact.name);\n      if (event.primaryContact.email) {\n        icalEvent.x('X-CONTACT-EMAIL', event.primaryContact.email);\n      }\n      if (event.primaryContact.phone) {\n        icalEvent.x('X-CONTACT-PHONE', event.primaryContact.phone);\n      }\n    }\n\n    // Add recurrence rule if present\n    if (event.recurrenceRule) {\n      icalEvent.repeating(event.recurrenceRule);\n    }\n  });\n\n  return cal.toString();\n}\n\n/**\n * Generates an ICS feed for a specific event type (e.g., only gigs).\n */\nexport async function generateIcsByType(\n  calendar: Calendar, \n  calendarId: string, \n  eventType: string\n): Promise<string> {\n  const cal = ical({ \n    name: `${calendar.name} - ${eventType.charAt(0).toUpperCase() + eventType.slice(1)}s`,\n    description: `${eventType} events from ${calendar.name}`,\n    timezone: 'Australia/Sydney'\n  });\n  \n  const events = await getEventsByCalendar(calendarId);\n  const filteredEvents = events.filter(event => event.type === eventType);\n  \n  filteredEvents.forEach(event => {\n    const icalEvent = cal.createEvent({\n      start: event.start,\n      end: event.end,\n      summary: event.summary,\n      description: event.description,\n      location: event.venue ? [\n        event.venue.name,\n        event.venue.address,\n        event.venue.city,\n        event.venue.state,\n        event.venue.country\n      ].filter(Boolean).join(', ') : undefined,\n    });\n\n    // Add extended properties\n    icalEvent.x('X-EVENT-TYPE', event.type);\n    \n    if (event.genre) icalEvent.x('X-GENRE', event.genre);\n    if (event.instrument) icalEvent.x('X-INSTRUMENT', event.instrument);\n    if (event.paymentStatus) icalEvent.x('X-PAYMENT-STATUS', event.paymentStatus);\n    if (event.status) icalEvent.x('X-STATUS', event.status);\n    \n    if (event.primaryContact) {\n      icalEvent.x('X-CONTACT-NAME', event.primaryContact.name);\n      if (event.primaryContact.email) icalEvent.x('X-CONTACT-EMAIL', event.primaryContact.email);\n      if (event.primaryContact.phone) icalEvent.x('X-CONTACT-PHONE', event.primaryContact.phone);\n    }\n\n    if (event.recurrenceRule) {\n      icalEvent.repeating(event.recurrenceRule);\n    }\n  });\n\n  return cal.toString();\n}\n\n/**\n * Generates an ICS feed for upcoming events only (next 30 days).\n */\nexport async function generateUpcomingIcs(calendar: Calendar, calendarId: string): Promise<string> {\n  const cal = ical({ \n    name: `${calendar.name} - Upcoming Events`,\n    description: `Upcoming events from ${calendar.name}`,\n    timezone: 'Australia/Sydney'\n  });\n  \n  const events = await getEventsByCalendar(calendarId);\n  const now = new Date();\n  const thirtyDaysFromNow = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000);\n  \n  const upcomingEvents = events.filter(event => \n    event.start >= now && event.start <= thirtyDaysFromNow\n  );\n  \n  upcomingEvents.forEach(event => {\n    const icalEvent = cal.createEvent({\n      start: event.start,\n      end: event.end,\n      summary: event.summary,\n      description: event.description,\n      location: event.venue ? [\n        event.venue.name,\n        event.venue.address,\n        event.venue.city,\n        event.venue.state,\n        event.venue.country\n      ].filter(Boolean).join(', ') : undefined,\n    });\n\n    // Add extended properties\n    icalEvent.x('X-EVENT-TYPE', event.type);\n    \n    if (event.genre) icalEvent.x('X-GENRE', event.genre);\n    if (event.instrument) icalEvent.x('X-INSTRUMENT', event.instrument);\n    if (event.paymentStatus) icalEvent.x('X-PAYMENT-STATUS', event.paymentStatus);\n    if (event.status) icalEvent.x('X-STATUS', event.status);\n    \n    if (event.primaryContact) {\n      icalEvent.x('X-CONTACT-NAME', event.primaryContact.name);\n      if (event.primaryContact.email) icalEvent.x('X-CONTACT-EMAIL', event.primaryContact.email);\n      if (event.primaryContact.phone) icalEvent.x('X-CONTACT-PHONE', event.primaryContact.phone);\n    }\n\n    if (event.recurrenceRule) {\n      icalEvent.repeating(event.recurrenceRule);\n    }\n  });\n\n  return cal.toString();\n} ","/**\n * @file Server-side validation for the calendar library.\n * These validations can include database checks and are more comprehensive than client-side validation.\n */\n\nimport { DataSource } from 'typeorm';\nimport { Event, Calendar, Venue, Contact } from './database/entities';\nimport { isValidEmail, isValidPhone } from '../shared/utils';\nimport { EVENT_TYPES, PAYMENT_STATUS, EVENT_STATUS } from '../shared/enums';\n\nexport interface ServerValidationResult {\n  isValid: boolean;\n  errors: Record<string, string[]>;\n  warnings?: Record<string, string[]>;\n}\n\n/**\n * Validates event data with database checks.\n */\nexport async function validateEventWithDb(\n  data: Partial<Event>, \n  dataSource: DataSource,\n  eventId?: string\n): Promise<ServerValidationResult> {\n  const errors: Record<string, string[]> = {};\n  const warnings: Record<string, string[]> = {};\n\n  // Basic validation (same as client-side)\n  if (!data.summary || data.summary.trim().length === 0) {\n    errors.summary = ['Event title is required'];\n  } else if (data.summary.length > 200) {\n    errors.summary = ['Event title must not exceed 200 characters'];\n  }\n\n  if (!data.start) {\n    errors.start = ['Start date/time is required'];\n  }\n\n  if (!data.end) {\n    errors.end = ['End date/time is required'];\n  }\n\n  if (data.start && data.end) {\n    if (data.start >= data.end) {\n      errors.end = ['End time must be after start time'];\n    }\n    \n    const durationHours = (data.end.getTime() - data.start.getTime()) / (1000 * 60 * 60);\n    if (durationHours > 24) {\n      errors.end = ['Event duration cannot exceed 24 hours'];\n    }\n  }\n\n  if (!data.type) {\n    errors.type = ['Event type is required'];\n  } else if (!Object.values(EVENT_TYPES).includes(data.type as any)) {\n    errors.type = ['Invalid event type'];\n  }\n\n  // Database-specific validations\n  if (data.calendar) {\n    const calendarRepo = dataSource.getRepository(Calendar);\n    const calendar = await calendarRepo.findOneBy({ id: data.calendar.id });\n    if (!calendar) {\n      errors.calendar = ['Selected calendar does not exist'];\n    }\n  }\n\n  if (data.venue) {\n    const venueRepo = dataSource.getRepository(Venue);\n    const venue = await venueRepo.findOneBy({ id: data.venue.id });\n    if (!venue) {\n      errors.venue = ['Selected venue does not exist'];\n    }\n  }\n\n  if (data.primaryContact) {\n    const contactRepo = dataSource.getRepository(Contact);\n    const contact = await contactRepo.findOneBy({ id: data.primaryContact.id });\n    if (!contact) {\n      errors.primaryContact = ['Selected contact does not exist'];\n    }\n  }\n\n  // Check for overlapping events (warning, not error)\n  if (data.start && data.end && data.calendar) {\n    const eventRepo = dataSource.getRepository(Event);\n    const overlappingEvents = await eventRepo\n      .createQueryBuilder('event')\n      .where('event.calendarId = :calendarId', { calendarId: data.calendar.id })\n      .andWhere('event.start < :end', { end: data.end })\n      .andWhere('event.end > :start', { start: data.start })\n      .andWhere(eventId ? 'event.id != :eventId' : '1=1', { eventId })\n      .getMany();\n\n    if (overlappingEvents.length > 0) {\n      warnings.schedule = [`This event overlaps with ${overlappingEvents.length} other event(s)`];\n    }\n  }\n\n  // Musician-specific business logic validations\n  if (data.type === EVENT_TYPES.LESSON) {\n    if (!data.primaryContact) {\n      errors.primaryContact = ['Student contact is required for lessons'];\n    }\n    \n    if (!data.studentLevel) {\n      warnings.studentLevel = ['Student level is recommended for lessons'];\n    }\n  }\n\n  if (data.type === EVENT_TYPES.GIG) {\n    if (!data.venue) {\n      warnings.venue = ['Venue is recommended for gigs'];\n    }\n    \n    if (!data.paymentStatus) {\n      warnings.paymentStatus = ['Payment status is recommended for gigs'];\n    }\n  }\n\n  if (data.paymentStatus === PAYMENT_STATUS.OVERDUE && data.paymentDueDate) {\n    const now = new Date();\n    if (data.paymentDueDate > now) {\n      errors.paymentStatus = ['Payment cannot be overdue if due date is in the future'];\n    }\n  }\n\n  return {\n    isValid: Object.keys(errors).length === 0,\n    errors,\n    warnings: Object.keys(warnings).length > 0 ? warnings : undefined\n  };\n}\n\n/**\n * Validates calendar data with database checks.\n */\nexport async function validateCalendarWithDb(\n  data: Partial<Calendar>,\n  dataSource: DataSource,\n  calendarId?: string\n): Promise<ServerValidationResult> {\n  const errors: Record<string, string[]> = {};\n\n  if (!data.name || data.name.trim().length === 0) {\n    errors.name = ['Calendar name is required'];\n  } else if (data.name.length > 100) {\n    errors.name = ['Calendar name must not exceed 100 characters'];\n  }\n\n  if (data.description && data.description.length > 500) {\n    errors.description = ['Description must not exceed 500 characters'];\n  }\n\n  if (!data.type) {\n    errors.type = ['Calendar type is required'];\n  } else if (!['individual', 'group'].includes(data.type)) {\n    errors.type = ['Calendar type must be either \"individual\" or \"group\"'];\n  }\n\n  // Check for duplicate calendar names (warning)\n  if (data.name) {\n    const calendarRepo = dataSource.getRepository(Calendar);\n    const existingCalendar = await calendarRepo\n      .createQueryBuilder('calendar')\n      .where('LOWER(calendar.name) = LOWER(:name)', { name: data.name })\n      .andWhere(calendarId ? 'calendar.id != :calendarId' : '1=1', { calendarId })\n      .getOne();\n\n    if (existingCalendar) {\n      errors.name = ['A calendar with this name already exists'];\n    }\n  }\n\n  return {\n    isValid: Object.keys(errors).length === 0,\n    errors\n  };\n}\n\n/**\n * Validates venue data with database checks.\n */\nexport async function validateVenueWithDb(\n  data: Partial<Venue>,\n  dataSource: DataSource,\n  venueId?: string\n): Promise<ServerValidationResult> {\n  const errors: Record<string, string[]> = {};\n\n  if (!data.name || data.name.trim().length === 0) {\n    errors.name = ['Venue name is required'];\n  } else if (data.name.length > 200) {\n    errors.name = ['Venue name must not exceed 200 characters'];\n  }\n\n  if (data.contactEmail && !isValidEmail(data.contactEmail)) {\n    errors.contactEmail = ['Contact email must be a valid email address'];\n  }\n\n  if (data.contactPhone && !isValidPhone(data.contactPhone)) {\n    errors.contactPhone = ['Contact phone must be a valid Australian phone number'];\n  }\n\n  if (data.website && !/^https?:\\/\\/.+/.test(data.website)) {\n    errors.website = ['Website must be a valid URL starting with http:// or https://'];\n  }\n\n  // Check for duplicate venue names in the same city (warning)\n  if (data.name && data.city) {\n    const venueRepo = dataSource.getRepository(Venue);\n    const existingVenue = await venueRepo\n      .createQueryBuilder('venue')\n      .where('LOWER(venue.name) = LOWER(:name)', { name: data.name })\n      .andWhere('LOWER(venue.city) = LOWER(:city)', { city: data.city })\n      .andWhere(venueId ? 'venue.id != :venueId' : '1=1', { venueId })\n      .getOne();\n\n    if (existingVenue) {\n      errors.name = ['A venue with this name already exists in this city'];\n    }\n  }\n\n  return {\n    isValid: Object.keys(errors).length === 0,\n    errors\n  };\n}\n\n/**\n * Validates contact data with database checks.\n */\nexport async function validateContactWithDb(\n  data: Partial<Contact>,\n  dataSource: DataSource,\n  contactId?: string\n): Promise<ServerValidationResult> {\n  const errors: Record<string, string[]> = {};\n\n  if (!data.name || data.name.trim().length === 0) {\n    errors.name = ['Contact name is required'];\n  } else if (data.name.length > 200) {\n    errors.name = ['Contact name must not exceed 200 characters'];\n  }\n\n  if (data.email && !isValidEmail(data.email)) {\n    errors.email = ['Email must be a valid email address'];\n  }\n\n  if (data.phone && !isValidPhone(data.phone)) {\n    errors.phone = ['Phone must be a valid Australian phone number'];\n  }\n\n  // Check for duplicate email addresses (error)\n  if (data.email) {\n    const contactRepo = dataSource.getRepository(Contact);\n    const existingContact = await contactRepo\n      .createQueryBuilder('contact')\n      .where('LOWER(contact.email) = LOWER(:email)', { email: data.email })\n      .andWhere(contactId ? 'contact.id != :contactId' : '1=1', { contactId })\n      .getOne();\n\n    if (existingContact) {\n      errors.email = ['A contact with this email address already exists'];\n    }\n  }\n\n  return {\n    isValid: Object.keys(errors).length === 0,\n    errors\n  };\n}\n\n/**\n * Validates business rules for event scheduling.\n */\nexport async function validateEventScheduling(\n  eventData: Partial<Event>,\n  dataSource: DataSource\n): Promise<ServerValidationResult> {\n  const errors: Record<string, string[]> = {};\n  const warnings: Record<string, string[]> = {};\n\n  if (!eventData.start || !eventData.end || !eventData.calendar) {\n    return { isValid: true, errors: {} }; // Skip if basic data is missing\n  }\n\n  const eventRepo = dataSource.getRepository(Event);\n\n  // Check for double-booking (same time, same calendar)\n  const conflictingEvents = await eventRepo\n    .createQueryBuilder('event')\n    .where('event.calendarId = :calendarId', { calendarId: eventData.calendar.id })\n    .andWhere('event.start < :end', { end: eventData.end })\n    .andWhere('event.end > :start', { start: eventData.start })\n    .getMany();\n\n  if (conflictingEvents.length > 0) {\n    errors.schedule = ['This time slot conflicts with existing events'];\n  }\n\n  // Check for reasonable scheduling patterns\n  const dayOfWeek = eventData.start.getDay();\n  const hour = eventData.start.getHours();\n\n  // Warn about unusual scheduling\n  if (eventData.type === EVENT_TYPES.LESSON) {\n    if (dayOfWeek === 0 || dayOfWeek === 6) { // Weekend\n      warnings.schedule = ['Weekend lessons are unusual - please confirm this is correct'];\n    }\n    \n    if (hour < 8 || hour > 20) { // Very early or late\n      warnings.schedule = ['Lessons outside 8am-8pm are unusual - please confirm this is correct'];\n    }\n  }\n\n  if (eventData.type === EVENT_TYPES.GIG) {\n    if (hour < 10 || hour > 23) { // Very early or very late\n      warnings.schedule = ['Gig times outside 10am-11pm are unusual - please confirm this is correct'];\n    }\n  }\n\n  return {\n    isValid: Object.keys(errors).length === 0,\n    errors,\n    warnings: Object.keys(warnings).length > 0 ? warnings : undefined\n  };\n} "],"mappings":"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","names":["formatDateAustralian","date","months","day","getUTCDate","toString","padStart","month","getUTCMonth","year","getUTCFullYear","getDurationMinutes","start","end","Math","round","getTime","isSameDay","date1","date2","toDateString","getStartOfWeek","result","Date","getDay","diff","getDate","setDate","setHours","getEndOfWeek","isValidEmail","email","emailRegex","test","isValidPhone","phone","phoneRegex","replace","generateTempId","now","random","substr","DEFAULT_CURRENCY","MAX_EVENT_DURATION_HOURS","MAX_RECURRENCE_OCCURRENCES","DEFAULT_EVENT_DURATION_MINUTES","MAX_LENGTHS","EVENT_TITLE","EVENT_DESCRIPTION","CALENDAR_NAME","CALENDAR_DESCRIPTION","VENUE_NAME","VENUE_ADDRESS","VENUE_CITY","CONTACT_NAME","CONTACT_ROLE","NOTES","INCOME_DESCRIPTION","EXPENSE_DESCRIPTION","RECEIPT_PATH","DATE_FORMATS","AUSTRALIAN","AMERICAN","ISO","TIME_FORMATS","TWELVE_HOUR","TWENTY_FOUR_HOUR","CALENDAR_VIEWS","DAY","WEEK","MONTH","LIST","DEFAULT_START_OF_WEEK","AUSTRALIAN_STATES","NSW","VIC","QLD","WA","SA","TAS","ACT","NT","CURRENCIES","AUD","USD","EUR","GBP","CAD","NZD","VALIDATION_PATTERNS","EMAIL","PHONE_AU","CURRENCY_CODE","HEX_COLOR","URL","API_CONFIG","DEFAULT_PAGE_SIZE","MAX_PAGE_SIZE","REQUEST_TIMEOUT","RETRY_ATTEMPTS","FILE_UPLOAD","MAX_SIZE_MB","ALLOWED_TYPES","ALLOWED_EXTENSIONS","EVENT_TYPES","GIG","LESSON","AUDITION","PRACTICE","REHEARSAL","RECORDING","MEETING","PAYMENT_STATUS","PENDING","PAID","OVERDUE","CANCELLED","EVENT_STATUS","CONFIRMED","TENTATIVE","COMPLETED","CALENDAR_TYPES","INDIVIDUAL","GROUP","SHARED","STUDENT_LEVELS","BEGINNER","INTERMEDIATE","ADVANCED","PROFESSIONAL","DIFFICULTY_LEVELS","EASY","MEDIUM","HARD","EXPERT","GENRES","CLASSICAL","JAZZ","ROCK","POP","BLUES","COUNTRY","FOLK","ELECTRONIC","WORLD","OTHER","INSTRUMENTS","PIANO","GUITAR","VIOLIN","DRUMS","BASS","SAXOPHONE","TRUMPET","FLUTE","CELLO","VOICE","Entity","PrimaryGeneratedColumn","Column","ManyToOne","OneToMany","CreateDateColumn","UpdateDateColumn","JoinColumn","Calendar","id","name","description","type","nullable","Venue","address","city","state","country","website","contactName","contactEmail","contactPhone","notes","Contact","email","phone","role","Event","summary","start","end","recurrenceRule","genre","instrument","difficulty","repertoire","setList","equipmentNeeded","dresscode","soundcheckTime","loadInTime","paymentStatus","paymentDueDate","studentLevel","lessonFocus","auditionPiece","auditionRequirements","practiceGoals","rehearsalNotes","status","createdAt","updatedAt","calendar","venue","primaryContact","income","expenses","EventIncome","event","cascade","EventExpense","expense","amount","currency","precision","scale","default","receipt","Between","db","eventRepository","calendarRepository","incomeRepository","expenseRepository","venueRepository","contactRepository","initDb","dataSource","getRepository","Event","Calendar","EventIncome","EventExpense","Venue","Contact","getDb","Error","fetchChanges","calendarId","since","updated","find","where","calendar","id","updatedAt","Between","Date","relations","deleted","map","event","summary","description","start","end","recurrenceRule","type","genre","instrument","difficulty","repertoire","setList","equipmentNeeded","dresscode","soundcheckTime","loadInTime","paymentStatus","paymentDueDate","studentLevel","lessonFocus","auditionPiece","auditionRequirements","practiceGoals","rehearsalNotes","status","createdAt","venueId","venue","primaryContactId","primaryContact","createEvent","eventData","findOneBy","newEvent","create","save","updateEvent","eventId","updates","update","updatedEvent","deleteEvent","delete","getEventsByCalendar","getEventsByType","order","getUpcomingGigs","limit","now","getTime","take","getStudentLessons","studentContactId","addEventIncome","income","findOne","eventIncome","addEventExpense","expense","eventExpense","getEventIncome","getEventExpenses","calculateEventIncome","reduce","total","item","Number","amount","calculateEventExpenses","expenses","calculateEventProfit","totalIncome","totalExpenses","getFinancialSummary","eventIds","netProfit","eventCount","length","averageProfitPerEvent","updateEventIncome","incomeId","updatedIncome","updateEventExpense","expenseId","updatedExpense","deleteEventIncome","deleteEventExpense","createVenue","newVenue","createContact","contact","newContact","ical","generateIcs","calendar","calendarId","cal","ical","name","description","timezone","events","getEventsByCalendar","forEach","event","icalEvent","createEvent","start","end","summary","location","venue","address","city","state","country","filter","Boolean","join","undefined","type","x","genre","instrument","paymentStatus","status","primaryContact","email","phone","recurrenceRule","repeating","toString","generateIcsByType","eventType","charAt","toUpperCase","slice","filteredEvents","generateUpcomingIcs","now","Date","thirtyDaysFromNow","getTime","upcomingEvents","validateEventWithDb","data","dataSource","eventId","errors","warnings","summary","trim","length","start","end","durationHours","getTime","type","Object","values","EVENT_TYPES","includes","calendar","calendarRepo","getRepository","Calendar","findOneBy","id","venue","venueRepo","Venue","primaryContact","contactRepo","Contact","contact","eventRepo","Event","overlappingEvents","createQueryBuilder","where","calendarId","andWhere","getMany","schedule","LESSON","studentLevel","GIG","paymentStatus","PAYMENT_STATUS","OVERDUE","paymentDueDate","now","Date","isValid","keys","undefined","validateCalendarWithDb","name","description","existingCalendar","getOne","validateVenueWithDb","venueId","contactEmail","isValidEmail","contactPhone","isValidPhone","website","test","city","existingVenue","validateContactWithDb","contactId","email","phone","existingContact","validateEventScheduling","eventData","conflictingEvents","dayOfWeek","getDay","hour","getHours"]}