{"version":3,"sources":["jsdelivr-header.js","/npm/@moxus/intervals-mcp@1.0.7/src/index.ts","/npm/@moxus/intervals-mcp@1.0.7/src/server.ts","/npm/@moxus/intervals-mcp@1.0.7/src/api.ts","/npm/@moxus/intervals-mcp@1.0.7/src/schemas.ts","/npm/@moxus/intervals-mcp@1.0.7/src/utils.ts"],"names":[],"mappings":"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","file":"/npm/@moxus/intervals-mcp@1.0.7/dist/index.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/@moxus/intervals-mcp@1.0.7/dist/index.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","#!/usr/bin/env node\nimport { Command } from 'commander';\nimport dotenv from 'dotenv';\nimport { startServer } from './server.js';\nimport { IntervalsClient } from './api.js';\n\ndotenv.config({ quiet: true });\n\nconst program = new Command();\n\nprogram\n  .name('intervals-mcp')\n  .description('MCP Server for Intervals.icu API')\n  .version(process.env.VERSION || '0.0.0');\n\n// Helper to check env vars\nfunction getEnvConfig() {\n  const athleteId = process.env.INTERVALS_ATHLETE_ID;\n  const apiKey = process.env.INTERVALS_API_KEY;\n\n  if (!athleteId || !apiKey) {\n    console.error(\n      'Error: INTERVALS_ATHLETE_ID and INTERVALS_API_KEY must be set in environment variables (or .env file).',\n    );\n    process.exit(1);\n  }\n  return { athleteId, apiKey };\n}\n\nprogram\n  .command('verify')\n  .description('Verify the setup by fetching the athlete profile.')\n  .action(async () => {\n    const { athleteId, apiKey } = getEnvConfig();\n    console.log(`Verifying connection for Athlete ID: ${athleteId}...`);\n\n    const client = new IntervalsClient(athleteId, apiKey);\n    try {\n      const profile = await client.getAthleteProfile();\n      console.log('✅ Connection Successful!');\n      console.log(`Athlete: ${profile.firstname} ${profile.lastname} (${profile.id})`);\n      console.log(`City: ${profile.city}, ${profile.country}`);\n    } catch (error: any) {\n      console.error('❌ Connection Failed.');\n      console.error(error.message);\n      process.exit(1);\n    }\n  });\n\n// Default action: Start MCP Server\nprogram.action(async () => {\n  const { athleteId, apiKey } = getEnvConfig();\n  try {\n    // Note: MCP SDK usually logs to stderr so it doesn't interfere with stdio transport\n    await startServer(athleteId, apiKey);\n  } catch (error) {\n    console.error('Failed to start MCP server:', error);\n    process.exit(1);\n  }\n});\n\nprogram.parse(process.argv);\n","import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport { z } from 'zod';\nimport { IntervalsClient } from './api.js';\nimport { removeNulls } from './utils.js';\nimport {\n  DateRangeSchema,\n  CreateEventSchema,\n  UpdateEventSchema,\n  CreateWorkoutSchema,\n} from './schemas.js';\n\nexport async function startServer(athleteId: string, apiKey: string) {\n  const server = new McpServer({\n    name: 'intervals-mcp',\n    version: '1.0.0',\n  });\n\n  const client = new IntervalsClient(athleteId, apiKey);\n\n  // --- Tools ---\n\n  // 1. Get Athlete Profile\n  server.tool(\n    'get_athlete_profile',\n    'Get the profile of the configured athlete.',\n    {}, // No args\n    async () => {\n      const profile = await client.getAthleteProfile();\n      return {\n        content: [{ type: 'text', text: JSON.stringify(removeNulls(profile), null, 2) }],\n      };\n    },\n  );\n\n  // 2. List Activities\n  server.tool(\n    'list_activities',\n    'List activities for the athlete within a date range.',\n    {\n      oldest: z\n        .string()\n        .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n        .describe('Start date (YYYY-MM-DD)'),\n      newest: z\n        .string()\n        .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n        .describe('End date (YYYY-MM-DD)'),\n    },\n    async ({ oldest, newest }) => {\n      const activities = await client.getActivities(oldest, newest);\n      return {\n        content: [{ type: 'text', text: JSON.stringify(removeNulls(activities), null, 2) }],\n      };\n    },\n  );\n\n  // 3. Get Single Activity\n  server.tool(\n    'get_activity',\n    'Get details of a specific activity by ID.',\n    {\n      id: z.string().describe('The Activity ID'),\n    },\n    async ({ id }) => {\n      const activity = await client.getActivity(id);\n      return {\n        content: [{ type: 'text', text: JSON.stringify(removeNulls(activity), null, 2) }],\n      };\n    },\n  );\n\n  // 4. List Wellness Data\n  server.tool(\n    'list_wellness',\n    'List wellness data (sleep, fatigue, etc.) for the athlete within a date range.',\n    {\n      oldest: z\n        .string()\n        .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n        .describe('Start date (YYYY-MM-DD)'),\n      newest: z\n        .string()\n        .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n        .describe('End date (YYYY-MM-DD)'),\n    },\n    async ({ oldest, newest }) => {\n      const wellness = await client.getWellness(oldest, newest);\n      return {\n        content: [{ type: 'text', text: JSON.stringify(removeNulls(wellness), null, 2) }],\n      };\n    },\n  );\n\n  // 5. List Workouts (Library)\n  server.tool('list_workouts', \"List all workouts in the athlete's library.\", {}, async () => {\n    const workouts = await client.getWorkouts();\n    return {\n      content: [{ type: 'text', text: JSON.stringify(removeNulls(workouts), null, 2) }],\n    };\n  });\n\n  // 6. Create Workout\n  server.tool(\n    'create_workout',\n    'Create a new workout in the library.',\n    {\n      name: z.string().describe('Workout Name'),\n      description: z\n        .string()\n        .optional()\n        .describe(\n          \"Workout steps/description text. MUST follow Intervals.icu builder syntax. For repeating steps (e.g. 5x), insert an empty line before and after the block (e.g. '\\\\n\\\\n5x\\\\n- 3m Z5\\\\n- 3m Z1\\\\n\\\\n'). Use \\\\n for new lines.\",\n        ),\n      folder_id: z.number().int().optional().describe('Folder ID to place the workout in'),\n      type: z.string().optional().describe('Sport type (Ride, Run, etc.)'),\n      indoor: z.boolean().optional(),\n    },\n    async (args) => {\n      const workout = await client.createWorkout(args);\n      return {\n        content: [{ type: 'text', text: JSON.stringify(removeNulls(workout), null, 2) }],\n      };\n    },\n  );\n\n  // 7. List Events (Calendar)\n  server.tool(\n    'list_events',\n    'List calendar events (planned workouts, notes, etc.) within a date range.',\n    {\n      oldest: z\n        .string()\n        .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n        .describe('Start date (YYYY-MM-DD)'),\n      newest: z\n        .string()\n        .regex(/^\\d{4}-\\d{2}-\\d{2}$/)\n        .describe('End date (YYYY-MM-DD)'),\n    },\n    async ({ oldest, newest }) => {\n      const events = await client.getEvents(oldest, newest);\n      return {\n        content: [{ type: 'text', text: JSON.stringify(removeNulls(events), null, 2) }],\n      };\n    },\n  );\n\n  // 8. Create Event\n  server.tool(\n    'create_event',\n    'Create a new event on the calendar (must be in the future).',\n    {\n      start_date_local: z\n        .string()\n        .regex(/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$/)\n        .describe('Start DateTime (ISO-8601 Local)'),\n      name: z.string().optional(),\n      category: z\n        .enum([\n          'WORKOUT',\n          'RACE_A',\n          'RACE_B',\n          'RACE_C',\n          'NOTE',\n          'PLAN',\n          'HOLIDAY',\n          'SICK',\n          'INJURED',\n          'SET_EFTP',\n          'FITNESS_DAYS',\n          'SEASON_START',\n          'TARGET',\n          'SET_FITNESS',\n        ])\n        .optional(),\n      description: z\n        .string()\n        .optional()\n        .describe(\n          \"Description/Notes. If category=WORKOUT, this MUST follow Intervals.icu builder syntax. For repeating steps (e.g. 5x), insert an empty line before and after the block (e.g. '\\\\n\\\\n5x\\\\n- 3m Z5\\\\n- 3m Z1\\\\n\\\\n'). Otherwise, it is free text.\",\n        ),\n      type: z.string().optional(),\n    },\n    async (args) => {\n      const event = await client.createEvent(args);\n      return {\n        content: [{ type: 'text', text: JSON.stringify(removeNulls(event), null, 2) }],\n      };\n    },\n  );\n\n  // 9. Update Event\n  server.tool(\n    'update_event',\n    'Update a future event on the calendar.',\n    {\n      id: z.number().int().describe('Event ID'),\n      start_date_local: z\n        .string()\n        .regex(/^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$/)\n        .optional(),\n      name: z.string().optional(),\n      description: z\n        .string()\n        .optional()\n        .describe(\n          \"Description/Notes. If category=WORKOUT, this MUST follow Intervals.icu builder syntax. For repeating steps (e.g. 5x), insert an empty line before and after the block (e.g. '\\\\n\\\\n5x\\\\n- 3m Z5\\\\n- 3m Z1\\\\n\\\\n'). Otherwise, it is free text.\",\n        ),\n      category: z\n        .enum([\n          'WORKOUT',\n          'RACE_A',\n          'RACE_B',\n          'RACE_C',\n          'NOTE',\n          'PLAN',\n          'HOLIDAY',\n          'SICK',\n          'INJURED',\n          'SET_EFTP',\n          'FITNESS_DAYS',\n          'SEASON_START',\n          'TARGET',\n          'SET_FITNESS',\n        ])\n        .optional(),\n    },\n    async (args) => {\n      const { id, ...updateData } = args;\n      const event = await client.updateEvent(id, updateData);\n      return {\n        content: [{ type: 'text', text: JSON.stringify(removeNulls(event), null, 2) }],\n      };\n    },\n  );\n\n  // 10. Delete Event\n  server.tool(\n    'delete_future_event',\n    'Delete a future event from the calendar.',\n    {\n      id: z.number().int().describe('Event ID'),\n    },\n    async ({ id }) => {\n      const result = await client.deleteEvent(id);\n      return {\n        content: [{ type: 'text', text: JSON.stringify(removeNulls(result), null, 2) }],\n      };\n    },\n  );\n\n  // Connect to transport\n  const transport = new StdioServerTransport();\n  await server.connect(transport);\n}\n","import axios, { AxiosInstance, AxiosError } from 'axios';\nimport {\n  ActivitySchema,\n  AthleteProfileSchema,\n  EventSchema,\n  WellnessSchema,\n  WorkoutSchema,\n  CreateEventSchema,\n  UpdateEventSchema,\n  CreateWorkoutSchema,\n} from './schemas.js';\nimport { z } from 'zod';\n\nconst API_BASE_URL = 'https://intervals.icu/api/v1';\n\nexport class IntervalsClient {\n  private axios: AxiosInstance;\n  private athleteId: string;\n\n  constructor(athleteId: string, apiKey: string) {\n    this.athleteId = athleteId;\n    this.axios = axios.create({\n      baseURL: API_BASE_URL,\n      auth: {\n        username: 'API_KEY',\n        password: apiKey,\n      },\n    });\n  }\n\n  // --- Helper to handle API errors ---\n  private handleError(error: unknown, context: string): never {\n    if (axios.isAxiosError(error)) {\n      const status = error.response?.status;\n      const data = error.response?.data;\n      const message =\n        data && typeof data === 'object' && 'error' in data ? (data as any).error : error.message;\n      throw new Error(`Intervals.icu API Error [${context}]: ${status} - ${message}`);\n    }\n    throw error;\n  }\n\n  // --- Athlete ---\n  async getAthleteProfile() {\n    try {\n      // If the token is valid, this returns the athlete profile associated with the token\n      // OR the one specified in the path if the token has access.\n      // We use the ID from env.\n      const response = await this.axios.get(`/athlete/${this.athleteId}`);\n      return AthleteProfileSchema.parse(response.data);\n    } catch (error) {\n      this.handleError(error, 'getAthleteProfile');\n    }\n  }\n\n  // --- Activities ---\n  async getActivities(oldest: string, newest: string) {\n    try {\n      const response = await this.axios.get(`/athlete/${this.athleteId}/activities`, {\n        params: { oldest, newest },\n      });\n      return z.array(ActivitySchema).parse(response.data);\n    } catch (error) {\n      this.handleError(error, 'getActivities');\n    }\n  }\n\n  async getActivity(activityId: string) {\n    try {\n      const response = await this.axios.get(`/activity/${activityId}`);\n      return ActivitySchema.parse(response.data);\n    } catch (error) {\n      this.handleError(error, 'getActivity');\n    }\n  }\n\n  // --- Wellness ---\n  async getWellness(oldest: string, newest: string) {\n    try {\n      const response = await this.axios.get(`/athlete/${this.athleteId}/wellness.json`, {\n        // .json ext might be required or supported\n        params: { oldest, newest },\n      });\n      return z.array(WellnessSchema).parse(response.data);\n    } catch (error) {\n      this.handleError(error, 'getWellness');\n    }\n  }\n\n  // --- Library (Workouts) ---\n  async getWorkouts() {\n    try {\n      const response = await this.axios.get(`/athlete/${this.athleteId}/workouts`);\n      return z.array(WorkoutSchema).parse(response.data);\n    } catch (error) {\n      this.handleError(error, 'getWorkouts');\n    }\n  }\n\n  async createWorkout(workout: z.infer<typeof CreateWorkoutSchema>) {\n    try {\n      const response = await this.axios.post(`/athlete/${this.athleteId}/workouts`, workout);\n      return WorkoutSchema.parse(response.data);\n    } catch (error) {\n      this.handleError(error, 'createWorkout');\n    }\n  }\n\n  // --- Events (Calendar) ---\n  async getEvents(oldest: string, newest: string) {\n    try {\n      const response = await this.axios.get(`/athlete/${this.athleteId}/events`, {\n        params: { oldest, newest },\n      });\n      return z.array(EventSchema).parse(response.data);\n    } catch (error) {\n      this.handleError(error, 'getEvents');\n    }\n  }\n\n  async createEvent(event: z.infer<typeof CreateEventSchema>) {\n    // Check if start_date_local is in the future is technically not required by API,\n    // but the prompt says \"modifying past events should not be possible\".\n    // Creating a past event is technically \"modifying the past\", so I will block it.\n    const eventDate = new Date(event.start_date_local);\n    const now = new Date();\n    // Allow small buffer? No, strict \"future only\" for safety.\n    // Actually, creating a note for today is fine. Creating something for yesterday is questionable.\n    // Let's check if date < today (start of day).\n    // Better: Check if date is before now.\n    if (eventDate < now) {\n      throw new Error('Cannot create events in the past.');\n    }\n\n    try {\n      const response = await this.axios.post(`/athlete/${this.athleteId}/events`, event);\n      return EventSchema.parse(response.data);\n    } catch (error) {\n      this.handleError(error, 'createEvent');\n    }\n  }\n\n  async updateEvent(id: number, event: z.infer<typeof UpdateEventSchema>) {\n    // 1. Fetch existing event to check date\n    let existingEvent;\n    try {\n      const events = await this.axios.get(`/athlete/${this.athleteId}/events/${id}`);\n      existingEvent = EventSchema.parse(events.data);\n    } catch (e) {\n      this.handleError(e, `fetchEventForUpdate-${id}`);\n    }\n\n    if (!existingEvent) {\n      throw new Error('Event not found');\n    }\n\n    const eventDate = new Date(existingEvent.start_date_local);\n    const now = new Date();\n\n    if (eventDate < now) {\n      throw new Error('Cannot modify past events.');\n    }\n\n    // 2. Perform update\n    try {\n      const response = await this.axios.put(`/athlete/${this.athleteId}/events/${id}`, event);\n      return EventSchema.parse(response.data);\n    } catch (error) {\n      this.handleError(error, 'updateEvent');\n    }\n  }\n\n  async deleteEvent(id: number) {\n    // 1. Fetch existing event to check date\n    let existingEvent;\n    try {\n      const events = await this.axios.get(`/athlete/${this.athleteId}/events/${id}`);\n      existingEvent = EventSchema.parse(events.data);\n    } catch (e) {\n      this.handleError(e, `fetchEventForDelete-${id}`);\n    }\n\n    if (!existingEvent) {\n      throw new Error('Event not found');\n    }\n\n    const eventDate = new Date(existingEvent.start_date_local);\n    const now = new Date();\n\n    if (eventDate < now) {\n      throw new Error('Cannot delete past events.');\n    }\n\n    try {\n      await this.axios.delete(`/athlete/${this.athleteId}/events/${id}`);\n      return { success: true, id };\n    } catch (error) {\n      this.handleError(error, 'deleteEvent');\n    }\n  }\n}\n","import { z } from 'zod';\n\n// Helper to allow null or undefined (since API might return null)\nconst nullableString = z.string().nullable().optional();\nconst nullableNumber = z.number().nullable().optional();\nconst nullableInt = z.number().int().nullable().optional();\nconst nullableBoolean = z.boolean().nullable().optional();\n\n// --- Athlete ---\nexport const AthleteProfileSchema = z.object({\n  id: z.string(),\n  name: nullableString,\n  firstname: nullableString,\n  lastname: nullableString,\n  email: nullableString,\n  timezone: nullableString,\n  city: nullableString,\n  country: nullableString,\n  sex: nullableString,\n  profile_medium: nullableString,\n});\n\n// --- Activity ---\nexport const ActivitySchema = z\n  .object({\n    id: z.string(),\n    start_date_local: z.string(), // ISO-8601\n    name: nullableString,\n    // The 'type' field is actually not guaranteed or might be null/undefined in some cases?\n    // Wait, the API docs say 'type' is a string enum, but looking at the errors:\n    // \"path\": [4, \"type\"], \"received\": \"undefined\", \"message\": \"Required\"\n    // It seems some activities in the array DO NOT have a 'type'.\n    // Looking at the dump, there are objects like:\n    // { \"id\": \"10526317718\", \"icu_athlete_id\": \"i13287\", \"start_date_local\": \"...\", \"source\": \"STRAVA\", \"_note\": \"...\" }\n    // These are \"Hidden\" or stub activities where 'type' is missing.\n    // We need to handle this union type (Activity | Hidden) or just make type optional.\n    type: nullableString,\n    distance: nullableNumber, // meters\n    moving_time: nullableInt, // seconds\n    elapsed_time: nullableInt,\n    total_elevation_gain: nullableNumber,\n    average_heartrate: nullableInt,\n    max_heartrate: nullableInt,\n    average_speed: nullableNumber, // m/s\n    average_watts: nullableInt,\n    weighted_average_watts: nullableInt,\n    normalized_power: nullableInt,\n    kilojoules: nullableNumber,\n    device_name: nullableString,\n    description: nullableString,\n    source: nullableString,\n    trainer: nullableBoolean,\n    commute: nullableBoolean,\n  })\n  .passthrough(); // Allow extra fields to avoid strict issues if I missed some\n\n// --- Wellness ---\nexport const WellnessSchema = z\n  .object({\n    id: z.string(), // Date ISO-8601 YYYY-MM-DD\n    updated: nullableString, // Timestamp\n    weight: nullableNumber,\n    restingHR: nullableInt,\n    hrv: nullableNumber,\n    sleepSecs: nullableInt,\n    sleepScore: nullableNumber,\n    sleepQuality: nullableInt,\n    fatigue: nullableInt, // 1-4?\n    stress: nullableInt,\n    mood: nullableInt,\n    motivation: nullableInt,\n    injury: nullableInt,\n    soreness: nullableInt,\n    comments: nullableString,\n    ctl: nullableNumber,\n    atl: nullableNumber,\n    rampRate: nullableNumber,\n  })\n  .passthrough();\n\n// --- Workout (Library) ---\nexport const WorkoutSchema = z\n  .object({\n    id: nullableInt, // Optional for new workouts\n    folder_id: nullableInt,\n    name: z.string(),\n    description: nullableString, // The workout steps text\n    type: nullableString, // Ride, Run, etc.\n    indoor: nullableBoolean,\n    distance: nullableNumber,\n    moving_time: nullableInt, // Estimated time\n    tags: z.array(z.string()).nullable().optional(),\n  })\n  .passthrough();\n\n// --- Event (Calendar) ---\nexport const EventSchema = z\n  .object({\n    id: nullableInt,\n    category: z.string().nullable().optional(), // Relax enum to string to avoid issues with new categories\n    start_date_local: z.string(), // ISO-8601\n    end_date_local: nullableString,\n    name: nullableString,\n    description: nullableString, // Workout text or note\n    type: nullableString, // Sport type e.g. Ride, Run\n    moving_time: nullableInt,\n    distance: nullableNumber,\n    color: nullableString,\n    indoor: nullableBoolean,\n    not_on_fitness_chart: nullableBoolean,\n    show_as_note: nullableBoolean,\n    url: nullableString,\n    athlete_id: nullableString,\n  })\n  .passthrough();\n\n// --- Input/Output Schemas for MCP Tools ---\n\n// Filter schemas\nexport const DateRangeSchema = z.object({\n  oldest: z\n    .string()\n    .regex(/^\\d{4}-\\d{2}-\\d{2}$/, 'Format must be YYYY-MM-DD')\n    .optional(),\n  newest: z\n    .string()\n    .regex(/^\\d{4}-\\d{2}-\\d{2}$/, 'Format must be YYYY-MM-DD')\n    .optional(),\n});\n\n// Create/Update schemas\nexport const CreateEventSchema = z.object({\n  start_date_local: z\n    .string()\n    .regex(\n      /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$/,\n      'Format must be ISO-8601 Local DateTime (YYYY-MM-DDTHH:mm:ss)',\n    ),\n  category: z.string().optional(),\n  name: z.string().optional(),\n  description: z.string().optional(),\n  type: z.string().optional(),\n  moving_time: z.number().int().optional(),\n  distance: z.number().optional(),\n  color: z.string().optional(),\n  indoor: z.boolean().optional(),\n  not_on_fitness_chart: z.boolean().optional(),\n  show_as_note: z.boolean().optional(),\n  url: z.string().optional(),\n});\n\nexport const UpdateEventSchema = z.object({\n  id: z.number().int(), // Required for update\n  start_date_local: z\n    .string()\n    .regex(\n      /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}$/,\n      'Format must be ISO-8601 Local DateTime (YYYY-MM-DDTHH:mm:ss)',\n    )\n    .optional(),\n  category: z.string().optional(),\n  name: z.string().optional(),\n  description: z.string().optional(),\n  type: z.string().optional(),\n  moving_time: z.number().int().optional(),\n  distance: z.number().optional(),\n  color: z.string().optional(),\n  indoor: z.boolean().optional(),\n  not_on_fitness_chart: z.boolean().optional(),\n  show_as_note: z.boolean().optional(),\n  url: z.string().optional(),\n});\n\nexport const CreateWorkoutSchema = z.object({\n  name: z.string(),\n  description: z.string().optional(),\n  folder_id: z.number().int().optional(),\n  type: z.string().optional(),\n  indoor: z.boolean().optional(),\n  distance: z.number().optional(),\n  moving_time: z.number().int().optional(),\n  tags: z.array(z.string()).optional(),\n});\n","/**\n * Recursively removes null values from an object or array.\n * - null -> undefined (so JSON.stringify removes the key from objects)\n * - Objects -> recursively processed\n * - Arrays -> recursively processed (elements becoming undefined appear as null in JSON)\n * - Primitives / Dates -> preserved\n */\nexport function removeNulls(obj: any): any {\n  if (obj === null) {\n    return undefined;\n  }\n\n  if (typeof obj !== 'object') {\n    return obj;\n  }\n\n  if (obj instanceof Date) {\n    return obj;\n  }\n\n  if (Array.isArray(obj)) {\n    return obj.map((v) => removeNulls(v));\n  }\n\n  const newObj: any = {};\n  for (const key in obj) {\n    if (Object.prototype.hasOwnProperty.call(obj, key)) {\n      const val = removeNulls(obj[key]);\n      if (val !== undefined) {\n        newObj[key] = val;\n      }\n    }\n  }\n  return newObj;\n}\n"]}