{"version":3,"sources":["../src/polly-fetch.ts","../src/types.ts","../package.json","../src/client.ts","../src/services/youtube.ts","../src/services/web.ts","../src/services/transcript.ts","../src/index.ts"],"names":["polly_fetch_default","crossFetch","SupadataError","error","package_default","USER_AGENT","BaseClient","config","endpoint","params","method","url","queryParams","key","value","item","body","options","response","contentType","errorData","YouTubeService","jobId","min","max","operation","WebService","request","pages","nextUrl","TranscriptService","Supadata"],"mappings":"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","file":"index.mjs","sourcesContent":["import crossFetch from 'cross-fetch';\n\nexport default fetch || crossFetch","export interface TranscriptChunk {\n  text: string;\n  offset: number;\n  duration: number;\n  lang: string;\n}\n\nexport interface Transcript {\n  content: TranscriptChunk[] | string;\n  lang: string;\n  availableLangs: string[];\n}\n\nexport interface TranslatedTranscript {\n  content: TranscriptChunk[] | string;\n  lang: string;\n}\n\nexport interface Scrape {\n  url: string;\n  content: string;\n  name: string;\n  description: string;\n  ogUrl: string;\n  countCharacters: number;\n  urls: string[];\n}\n\nexport interface SiteMap {\n  urls: string[];\n}\n\nexport interface CrawlRequest {\n  url: string;\n  limit?: number;\n}\n\nexport interface CrawlJob {\n  status: 'scraping' | 'completed' | 'failed' | 'cancelled';\n  pages?: Scrape[];\n  next?: string;\n}\n\nexport interface SupadataConfig {\n  apiKey: string;\n  baseUrl?: string;\n}\n\nexport class SupadataError extends Error {\n  error:\n    | 'invalid-request'\n    | 'internal-error'\n    | 'transcript-unavailable'\n    | 'not-found'\n    | 'unauthorized'\n    | 'upgrade-required'\n    | 'limit-exceeded';\n  details: string;\n  documentationUrl: string;\n\n  constructor(error: {\n    error: SupadataError['error'];\n    message?: string;\n    details?: string;\n    documentationUrl?: string;\n  }) {\n    super(error.message || 'An unexpected error occurred');\n    this.error = error.error || 'internal-error';\n    this.details = error.details || 'An unexpected error occurred';\n    this.documentationUrl = error.documentationUrl || '';\n    this.name = 'SupadataError';\n  }\n}\n\nexport interface YoutubeVideo {\n  id: string;\n  title: string;\n  description: string;\n  duration: number;\n  channel: {\n    id: string;\n    name: string;\n  };\n  tags: string[];\n  thumbnail: string;\n  uploadDate: string;\n  viewCount: number;\n  likeCount: number;\n  transcriptLanguages: string[];\n}\n\nexport interface YoutubeChannel {\n  id: string;\n  name: string;\n  handle: string;\n  description: string;\n  subscriberCount: number;\n  videoCount: number;\n  thumbnail: string;\n  banner: string;\n}\n\nexport interface YoutubePlaylist {\n  id: string;\n  title: string;\n  videoCount: number;\n  viewCount: number;\n  lastUpdated: string;\n  description: string;\n  thumbnail: string;\n}\n\nexport interface YoutubeBatchSource {\n  videoIds?: string[];\n  playlistId?: string;\n  channelId?: string;\n  limit?: number;\n}\n\nexport interface YoutubeTranscriptBatchRequest extends YoutubeBatchSource {\n  lang?: string;\n  text?: boolean;\n}\n\nexport interface YoutubeVideoBatchRequest extends YoutubeBatchSource {}\n\nexport interface JobId {\n  jobId: string;\n}\n\nexport interface YoutubeBatchJob extends JobId {}\n\nexport type JobStatus = 'queued' | 'active' | 'completed' | 'failed';\n\nexport type YoutubeBatchJobStatus = JobStatus;\n\nexport interface YoutubeBatchResultItem {\n  videoId: string;\n  transcript?: Transcript;\n  video?: YoutubeVideo;\n  errorCode?: string;\n}\n\nexport interface YoutubeBatchStats {\n  total: number;\n  succeeded: number;\n  failed: number;\n}\n\nexport interface YoutubeBatchResults {\n  status: YoutubeBatchJobStatus;\n  results?: YoutubeBatchResultItem[];\n  stats?: YoutubeBatchStats;\n  completedAt?: string;\n}\n\nexport type TranscriptOrJobId = Transcript | JobId;\n\nexport interface JobResult<T = any> {\n  status: JobStatus;\n  result?: T | null;\n  error?: {\n    error: SupadataError['error'];\n    message: string;\n    details: string;\n    documentationUrl?: string;\n  } | null;\n}\n\n// YouTube Search Types\nexport type YoutubeSearchUploadDate =\n  | 'all'\n  | 'hour'\n  | 'today'\n  | 'week'\n  | 'month'\n  | 'year';\nexport type YoutubeSearchType =\n  | 'all'\n  | 'video'\n  | 'channel'\n  | 'playlist'\n  | 'movie';\nexport type YoutubeSearchDuration = 'all' | 'short' | 'medium' | 'long';\nexport type YoutubeSearchSortBy = 'relevance' | 'rating' | 'date' | 'views';\n\nexport interface YoutubeSearchParams {\n  query: string;\n  uploadDate?: YoutubeSearchUploadDate;\n  type?: YoutubeSearchType;\n  duration?: YoutubeSearchDuration;\n  sortBy?: YoutubeSearchSortBy;\n  features?: string[];\n  limit?: number;\n  nextPageToken?: string;\n}\n\nexport interface YoutubeSearchVideoResult {\n  type: 'video';\n  id: string;\n  title: string;\n  description: string;\n  thumbnail: string;\n  duration: number;\n  viewCount: number;\n  uploadDate: string;\n  channel: {\n    id: string;\n    name: string;\n  };\n}\n\nexport interface YoutubeSearchChannelResult {\n  type: 'channel';\n  id: string;\n  name: string;\n  handle: string;\n  description: string;\n  thumbnail: string;\n  subscriberCount: number;\n  videoCount: number;\n}\n\nexport interface YoutubeSearchPlaylistResult {\n  type: 'playlist';\n  id: string;\n  title: string;\n  description: string;\n  thumbnail: string;\n  videoCount: number;\n  channel: {\n    id: string;\n    name: string;\n  };\n}\n\nexport type YoutubeSearchResult =\n  | YoutubeSearchVideoResult\n  | YoutubeSearchChannelResult\n  | YoutubeSearchPlaylistResult;\n\nexport interface YoutubeSearchResponse {\n  query: string;\n  results: YoutubeSearchResult[];\n  totalResults: number;\n  nextPageToken?: string;\n}\n","{\n  \"name\": \"@supadata/js\",\n  \"version\": \"1.3.1\",\n  \"description\": \"TypeScript / JavaScript SDK for Supadata API\",\n  \"homepage\": \"https://supadata.ai\",\n  \"repository\": \"https://github.com/supadata-ai/js\",\n  \"main\": \"./dist/index.cjs\",\n  \"module\": \"./dist/index.mjs\",\n  \"types\": \"./dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"import\": \"./dist/index.mjs\",\n      \"require\": \"./dist/index.cjs\",\n      \"default\": \"./dist/index.mjs\"\n    }\n  },\n  \"scripts\": {\n    \"dev\": \"tsup --watch\",\n    \"build\": \"tsup\",\n    \"test\": \"node --experimental-vm-modules node_modules/jest/bin/jest.js\",\n    \"prepare\": \"npm run build\",\n    \"format\": \"prettier --write \\\"src/**/*.{js,ts}\\\"\",\n    \"format:check\": \"prettier --check \\\"src/**/*.{js,ts}\\\"\"\n  },\n  \"files\": [\n    \"dist\",\n    \"README.md\"\n  ],\n  \"keywords\": [\n    \"supadata\",\n    \"api\",\n    \"sdk\",\n    \"typescript\",\n    \"youtube\",\n    \"transcript\",\n    \"web scraping\"\n  ],\n  \"author\": \"Supadata AI\",\n  \"license\": \"MIT\",\n  \"engines\": {\n    \"node\": \">=18\"\n  },\n  \"dependencies\": {\n    \"cross-fetch\": \"^4.0.0\"\n  },\n  \"devDependencies\": {\n    \"@types/jest\": \"^29.5.14\",\n    \"@types/node\": \"^22.10.10\",\n    \"jest\": \"^29.7.0\",\n    \"jest-fetch-mock\": \"^3.0.3\",\n    \"prettier\": \"^3.4.2\",\n    \"ts-jest\": \"^29.2.5\",\n    \"typescript\": \"^5.7.3\",\n    \"tsup\": \"^8.3.6\"\n  }\n}","import fetch from './polly-fetch';\nimport { SupadataConfig, SupadataError } from './types.js';\n// @ts-expect-error: Non-TS import for version from package.json\nimport pkg from '../package.json';\n\nconst USER_AGENT = `supadata-js/${pkg.version}`;\n\nexport class BaseClient {\n  protected config: SupadataConfig;\n\n  constructor(config: SupadataConfig) {\n    this.config = config;\n  }\n\n  protected async fetch<T>(\n    endpoint: string,\n    params: Record<string, any> = {},\n    method: 'GET' | 'POST' = 'GET'\n  ): Promise<T> {\n    const baseUrl = this.config.baseUrl || 'https://api.supadata.ai/v1';\n    let url = `${baseUrl}${\n      endpoint.startsWith('/') ? endpoint : `/${endpoint}`\n    }`;\n\n    if (method === 'GET' && Object.keys(params).length > 0) {\n      const queryParams = new URLSearchParams();\n      Object.entries(params).forEach(([key, value]) => {\n        if (value !== undefined && value !== null) {\n          if (Array.isArray(value)) {\n            value.forEach((item) => queryParams.append(key, String(item)));\n          } else {\n            queryParams.append(key, String(value));\n          }\n        }\n      });\n      url += `?${queryParams.toString()}`;\n    }\n\n    return this.fetchUrl<T>(url, method, params);\n  }\n\n  protected async fetchUrl<T>(\n    url: string,\n    method: 'GET' | 'POST' = 'GET',\n    body?: Record<string, any>\n  ): Promise<T> {\n    const options: RequestInit = {\n      method,\n      headers: {\n        'x-api-key': this.config.apiKey,\n        'Content-Type': 'application/json',\n        'User-Agent': USER_AGENT,\n      },\n    };\n\n    if (method === 'POST' && body) {\n      options.body = JSON.stringify(body);\n    }\n\n    const response = await fetch(url, options);\n\n    const contentType = response.headers.get('content-type');\n\n    if (!response.ok) {\n      // Handle standard API errors\n      if (contentType?.includes('application/json')) {\n        const errorData = await response.json();\n        throw new SupadataError(errorData);\n      } else {\n        // Fallback for unexpected non-JSON errors\n        throw new SupadataError({\n          error: 'internal-error',\n          message: 'Unexpected error response format',\n          details: await response.text(),\n        });\n      }\n    }\n\n    try {\n      if (!contentType?.includes('application/json')) {\n        throw new SupadataError({\n          error: 'internal-error',\n          message: 'Invalid response format',\n          details: 'Expected JSON response but received different content type',\n        });\n      }\n\n      return (await response.json()) as T;\n    } catch (error) {\n      throw new SupadataError({\n        error: 'internal-error',\n        message: 'Failed to parse response',\n        details: error instanceof Error ? error.message : 'Unknown error',\n      });\n    }\n  }\n}\n","import { BaseClient } from '../client.js';\nimport {\n  SupadataError,\n  Transcript,\n  TranslatedTranscript,\n  YoutubeBatchJob,\n  YoutubeBatchResults,\n  YoutubeChannel,\n  YoutubePlaylist,\n  YoutubeTranscriptBatchRequest,\n  YoutubeVideo,\n  YoutubeVideoBatchRequest,\n  YoutubeSearchParams,\n  YoutubeSearchResponse,\n} from '../types.js';\n\n/**\n * Ensures exactly one property from the specified keys is provided.\n * @example\n * // Valid: { url: \"...\" } or { videoId: \"...\" }\n * // Invalid: {} or { url: \"...\", videoId: \"...\" }\n */\ntype ExactlyOne<T, Keys extends keyof T> = {\n  [K in Keys]: { [P in K]-?: T[P] } & { [P in Exclude<Keys, K>]?: never };\n}[Keys] &\n  Omit<T, Keys>;\n\nexport type TranscriptParams = {\n  lang?: string;\n  text?: boolean;\n} & ExactlyOne<{ videoId: string; url: string }, 'videoId' | 'url'>;\n\nexport interface TranslateParams extends Omit<TranscriptParams, 'lang'> {\n  lang: string;\n}\n\nexport interface ResourceParams {\n  id: string;\n}\n\nexport interface ChannelVideosParams extends ResourceParams {\n  limit?: number;\n  type?: 'video' | 'short' | 'live' | 'all';\n}\n\nexport interface PlaylistVideosParams extends ResourceParams {\n  limit?: number;\n}\n\nexport interface VideoIds {\n  videoIds: string[];\n  shortIds: string[];\n  liveIds: string[];\n}\n\nexport class YouTubeService extends BaseClient {\n  /**\n   * Handles YouTube Transcript operations.\n   */\n  transcript = Object.assign(\n    /**\n     * Fetches a transcript for a YouTube video.\n     * @param params - Parameters for fetching the transcript\n     * @param params.videoId - The YouTube video ID (mutually exclusive with url)\n     * @param params.url - The YouTube video URL (mutually exclusive with videoId)\n     * @param params.lang - The language code for the transcript (optional)\n     * @param params.text - Whether to return only the text content (optional)\n     * @returns A promise that resolves to a Transcript object\n     */\n    async (params: TranscriptParams): Promise<Transcript> => {\n      return this.fetch<Transcript>('/youtube/transcript', params);\n    },\n    {\n      /**\n       * Batch fetches transcripts for multiple YouTube videos.\n       * @param params - Parameters for the transcript batch job\n       * @param params.videoIds - Array of YouTube video IDs to fetch transcripts for\n       * @param params.lang - The language code for the transcripts (optional)\n       * @param params.limit - Maximum number of videos to process (optional, default: 10, max: 5000)\n       * @param params.text - Whether to return only the text content (optional)\n       * @returns A promise that resolves to a YoutubeBatchJob object with the job ID\n       */\n      batch: async (\n        params: YoutubeTranscriptBatchRequest\n      ): Promise<YoutubeBatchJob> => {\n        this.validateLimit(params, 1, 5000, 'batch operation');\n        return this.fetch<YoutubeBatchJob>(\n          '/youtube/transcript/batch',\n          params,\n          'POST'\n        );\n      },\n    }\n  );\n\n  /**\n   * Handles YouTube video operations.\n   */\n  video = Object.assign(\n    /**\n     * Fetches a YouTube video based on the provided parameters.\n     * @param params - The parameters required to fetch the YouTube video\n     * @param params.id - The YouTube video ID\n     * @returns A promise that resolves to a YoutubeVideo object\n     */\n    async (params: ResourceParams): Promise<YoutubeVideo> => {\n      return this.fetch<YoutubeVideo>('/youtube/video', params);\n    },\n    {\n      /**\n       * Batch fetches metadata for multiple YouTube videos.\n       * @param params - Parameters for the video metadata batch job\n       * @param params.videoIds - Array of YouTube video IDs to fetch metadata for\n       * @param params.limit - Maximum number of videos to process (optional, default: 10, max: 5000)\n       * @returns A promise that resolves to a YoutubeBatchJob object with the job ID\n       */\n      batch: async (\n        params: YoutubeVideoBatchRequest\n      ): Promise<YoutubeBatchJob> => {\n        this.validateLimit(params, 1, 5000, 'batch operation');\n        return this.fetch<YoutubeBatchJob>(\n          '/youtube/video/batch',\n          params,\n          'POST'\n        );\n      },\n    }\n  );\n\n  /**\n   * Handles YouTube channel operations.\n   */\n  channel = Object.assign(\n    /**\n     * Fetches YouTube channel information.\n     * @param params - The parameters required to fetch the YouTube channel information\n     * @param params.id - The YouTube channel ID\n     * @returns A promise that resolves to a YoutubeChannel object containing the channel information\n     */\n    async (params: ResourceParams): Promise<YoutubeChannel> => {\n      return this.fetch<YoutubeChannel>('/youtube/channel', params);\n    },\n    {\n      /**\n       * Fetches the videos of a YouTube channel.\n       * @param params - The parameters required to fetch the YouTube channel videos\n       * @param params.id - The YouTube channel ID\n       * @param params.limit - The maximum number of videos to fetch (default: 30, max: 5000)\n       * @param params.type - The type of videos to fetch ('video', 'short', 'live', or 'all', default: 'video')\n       * @returns A promise that resolves to an object containing arrays of video IDs, short IDs, and live IDs\n       * @throws {SupadataError} If the limit is invalid (less than 1 or greater than 5000)\n       */\n      videos: async (params: ChannelVideosParams): Promise<VideoIds> => {\n        this.validateLimit(params);\n        return this.fetch<VideoIds>('/youtube/channel/videos', params);\n      },\n    }\n  );\n\n  /**\n   * Handles YouTube playlist operations.\n   */\n  playlist = Object.assign(\n    /**\n     * Fetches a YouTube playlist.\n     * @param params - The parameters required to fetch the playlist\n     * @param params.id - The YouTube playlist ID\n     * @returns A promise that resolves to a YoutubePlaylist object\n     */\n    async (params: ResourceParams): Promise<YoutubePlaylist> => {\n      return this.fetch<YoutubePlaylist>('/youtube/playlist', params);\n    },\n    {\n      /**\n       * Fetches the videos of a YouTube playlist.\n       * @param params - The parameters required to fetch the playlist videos\n       * @param params.id - The YouTube playlist ID\n       * @param params.limit - The maximum number of videos to fetch (default: 30, max: 5000)\n       * @returns A promise that resolves to an object containing arrays of video IDs, short IDs, and live IDs\n       * @throws {SupadataError} If the limit is invalid (less than 1 or greater than 5000)\n       */\n      videos: async (params: PlaylistVideosParams): Promise<VideoIds> => {\n        this.validateLimit(params);\n        return this.fetch<VideoIds>('/youtube/playlist/videos', params);\n      },\n    }\n  );\n\n  /**\n   * Handles YouTube batch operations.\n   */\n  batch = {\n    /**\n     * Retrieves the status and results of a batch job.\n     * @param jobId - The ID of the batch job\n     * @returns A promise that resolves to the YoutubeBatchResults containing job status and results\n     * @throws {SupadataError} If jobId is not provided\n     */\n    getBatchResults: async (jobId: string): Promise<YoutubeBatchResults> => {\n      if (!jobId) {\n        throw new SupadataError({\n          error: 'invalid-request',\n          message: 'Missing jobId',\n          details: 'The jobId parameter is required to get batch results.',\n        });\n      }\n      return this.fetch<YoutubeBatchResults>(`/youtube/batch/${jobId}`);\n    },\n  };\n\n  /**\n   * Translates a YouTube video transcript to a specified language.\n   * @param params - Parameters for translating the transcript\n   * @param params.videoId - The YouTube video ID (mutually exclusive with url)\n   * @param params.url - The YouTube video URL (mutually exclusive with videoId)\n   * @param params.lang - The target language code for translation\n   * @param params.text - Whether to return only the text content (optional)\n   * @returns A promise that resolves to a TranslatedTranscript object\n   */\n  translate = async (\n    params: TranslateParams\n  ): Promise<TranslatedTranscript> => {\n    return this.fetch<TranslatedTranscript>(\n      '/youtube/transcript/translate',\n      params\n    );\n  };\n\n  /**\n   * Search YouTube for videos, channels, and playlists with advanced filters.\n   * @param params - Parameters for the search\n   * @param params.query - The search query string\n   * @param params.uploadDate - Filter by upload date (\"all\", \"hour\", \"today\", \"week\", \"month\", \"year\")\n   * @param params.type - Filter by content type (\"all\", \"video\", \"channel\", \"playlist\", \"movie\")\n   * @param params.duration - Filter video length (\"all\", \"short\", \"medium\", \"long\")\n   * @param params.sortBy - Sort results by (\"relevance\", \"rating\", \"date\", \"views\")\n   * @param params.features - Array of special video features\n   * @param params.limit - Maximum results to return\n   * @param params.nextPageToken - Token for pagination\n   * @returns A promise that resolves to a YoutubeSearchResponse object\n   */\n  search = async (\n    params: YoutubeSearchParams\n  ): Promise<YoutubeSearchResponse> => {\n    if (!params.query) {\n      throw new SupadataError({\n        error: 'invalid-request',\n        message: 'Missing query parameter',\n        details: 'The query parameter is required for search.',\n      });\n    }\n\n    this.validateLimit(params, 1, 5000, 'search');\n\n    return this.fetch<YoutubeSearchResponse>('/youtube/search', params);\n  };\n\n  private validateLimit(\n    params: { limit?: number },\n    min: number = 1,\n    max: number = 5000,\n    operation: string = 'operation'\n  ) {\n    if (\n      params.limit != undefined &&\n      params.limit != null &&\n      (params.limit < min || params.limit > max)\n    ) {\n      throw new SupadataError({\n        error: 'invalid-request',\n        message: `Invalid limit for ${operation}.`,\n        details: `The limit must be between ${min} and ${max}.`,\n      });\n    }\n  }\n}\n","import { BaseClient } from '../client.js';\nimport { CrawlJob, CrawlRequest, JobId, Scrape, SiteMap } from '../types.js';\n\nexport class WebService extends BaseClient {\n  /**\n   * Extract content from any web page to Markdown format.\n   *\n   * @param url - URL of the webpage to scrape\n   * @returns A promise that resolves to the scraped content\n   */\n  async scrape(url: string): Promise<Scrape> {\n    return this.fetch<Scrape>('/web/scrape', { url });\n  }\n\n  /**\n   * Extract all links found on a webpage.\n   *\n   * @param url - URL of the webpage to map\n   * @returns A promise that resolves to a map of URLs found on the page\n   */\n  async map(url: string): Promise<SiteMap> {\n    return this.fetch<SiteMap>('/web/map', { url });\n  }\n\n  /**\n   * Create a crawl job to extract content from all pages on a website.\n   *\n   * @param request - Crawl request parameters\n   * @param request.url - URL of the website to crawl\n   * @param request.limit - Maximum number of pages to crawl (default: 100, max: 5000)\n   * @returns A promise that resolves to the crawl job id\n   */\n  async crawl(request: CrawlRequest): Promise<JobId> {\n    return this.fetch<JobId>('/web/crawl', request, 'POST');\n  }\n\n  /**\n   * Get the status and results of a crawl job.\n   * Automatically handles pagination to retrieve all pages from the crawl.\n   *\n   * @param jobId - The ID of the crawl job to retrieve\n   * @returns A promise that resolves to the complete crawl job results\n   */\n  async getCrawlResults(jobId: string): Promise<CrawlJob> {\n    let response: CrawlJob;\n    let pages: Scrape[] = [];\n    let nextUrl: string | undefined;\n\n    do {\n      response = await (nextUrl\n        ? this.fetchUrl<CrawlJob>(nextUrl)\n        : this.fetch<CrawlJob>(`/web/crawl/${jobId}`));\n\n      if (response.pages) {\n        pages = [...pages, ...response.pages];\n      }\n      nextUrl = response.next;\n    } while (nextUrl);\n\n    return response;\n  }\n}\n","import { BaseClient } from '../client.js';\nimport {\n  JobId,\n  JobResult,\n  SupadataError,\n  Transcript,\n  TranscriptOrJobId,\n} from '../types.js';\n\nexport interface GeneralTranscriptParams {\n  url: string;\n  lang?: string;\n  text?: boolean;\n  chunkSize?: number;\n  mode?: 'native' | 'auto' | 'generate';\n}\n\nexport class TranscriptService extends BaseClient {\n  /**\n   * Get transcript from a supported video platform or file URL.\n   * @param params - Parameters for fetching the transcript\n   * @returns A promise that resolves to either a Transcript or JobId for async processing\n   */\n  get = async (params: GeneralTranscriptParams): Promise<TranscriptOrJobId> => {\n    return this.fetch<TranscriptOrJobId>('/transcript', params);\n  };\n\n  /**\n   * Get results for a transcript job by job ID.\n   * @param jobId - The ID of the transcript job\n   * @returns A promise that resolves to the job result containing status and transcript if completed\n   * @throws {SupadataError} If jobId is not provided\n   */\n  getJobStatus = async (jobId: string): Promise<JobResult<Transcript>> => {\n    if (!jobId) {\n      throw new SupadataError({\n        error: 'invalid-request',\n        message: 'Missing jobId',\n        details:\n          'The jobId parameter is required to get transcript job status.',\n      });\n    }\n    return this.fetch<JobResult<Transcript>>(`/transcript/${jobId}`);\n  };\n}\n","import {\n  JobResult,\n  SupadataConfig,\n  Transcript,\n  TranscriptOrJobId,\n} from './types.js';\nimport { YouTubeService } from './services/youtube.js';\nimport { WebService } from './services/web.js';\nimport {\n  TranscriptService,\n  GeneralTranscriptParams,\n} from './services/transcript.js';\n\nexport * from './types.js';\nexport * from './client.js';\nexport * from './services/youtube.js';\nexport * from './services/web.js';\nexport {\n  TranscriptService,\n  GeneralTranscriptParams,\n} from './services/transcript.js';\n\nexport class Supadata {\n  readonly youtube: YouTubeService;\n  readonly web: WebService;\n  private _transcriptService: TranscriptService;\n\n  constructor(config: SupadataConfig) {\n    this.youtube = new YouTubeService(config);\n    this.web = new WebService(config);\n    this._transcriptService = new TranscriptService(config);\n  }\n\n  /**\n   * Get transcript from a supported video platform (YouTube, TikTok, Instagram, Twitter) or file URL.\n   * If the video is too large to return transcript immediately, request returns a job ID.\n   */\n  transcript = Object.assign(\n    async (params: GeneralTranscriptParams): Promise<TranscriptOrJobId> => {\n      return this._transcriptService.get(params);\n    },\n    {\n      getJobStatus: (jobId: string): Promise<JobResult<Transcript>> => {\n        return this._transcriptService.getJobStatus(jobId);\n      },\n    }\n  );\n}\n"]}