{"version":3,"sources":["jsdelivr-header.js","/npm/@the-convocation/twitter-scraper@0.21.0/src/errors.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/rate-limit.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/requests.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/xpff.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/auth.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/platform/platform-interface.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/platform/index.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/xctxid.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/api.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/auth-user.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/api-data.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/profile.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/timeline-async.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/type-util.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/timeline-tweet-util.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/timeline-v2.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/timeline-search.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/search.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/timeline-relationship.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/relationships.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/trends.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/timeline-list.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/tweets.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/direct-messages-async.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/direct-messages.ts","/npm/@the-convocation/twitter-scraper@0.21.0/src/scraper.ts"],"names":[],"mappings":"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","file":"/npm/@the-convocation/twitter-scraper@0.21.0/dist/default/cjs/index.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/@the-convocation/twitter-scraper@0.21.0/dist/default/cjs/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","export class ApiError extends Error {\n  constructor(readonly response: Response, readonly data: any) {\n    super(\n      `Response status: ${response.status} | headers: ${JSON.stringify(\n        headersToString(response.headers),\n      )} | data: ${typeof data === 'string' ? data : JSON.stringify(data)}`,\n    );\n  }\n\n  static async fromResponse(response: Response) {\n    // Try our best to parse the result, but don't bother if we can't\n    let data: string | object | undefined = undefined;\n    try {\n      if (response.headers.get('content-type')?.includes('application/json')) {\n        data = await response.json();\n      } else {\n        data = await response.text();\n      }\n    } catch {\n      try {\n        data = await response.text();\n      } catch {}\n    }\n\n    return new ApiError(response, data);\n  }\n}\n\nfunction headersToString(headers: Headers): string {\n  const result: string[] = [];\n  headers.forEach((value, key) => {\n    result.push(`${key}: ${value}`);\n  });\n  return result.join('\\n');\n}\n\nexport class AuthenticationError extends Error {\n  constructor(message?: string) {\n    super(message || 'Authentication failed');\n    this.name = 'AuthenticationError';\n  }\n}\n\nexport interface TwitterApiErrorPosition {\n  line: number;\n  column: number;\n}\n\nexport interface TwitterApiErrorTraceInfo {\n  trace_id: string;\n}\n\nexport interface TwitterApiErrorExtensions {\n  code?: number;\n  kind?: string;\n  name?: string;\n  source?: string;\n  tracing?: TwitterApiErrorTraceInfo;\n}\n\nexport interface TwitterApiErrorRaw extends TwitterApiErrorExtensions {\n  message?: string;\n  locations?: TwitterApiErrorPosition[];\n  path?: string[];\n  extensions?: TwitterApiErrorExtensions;\n}\n","import { FetchParameters } from './api-types';\nimport { ApiError } from './errors';\nimport debug from 'debug';\n\nconst log = debug('twitter-scraper:rate-limit');\n\n/**\n * Information about a rate-limiting event. Both the request and response\n * information are provided.\n */\nexport interface RateLimitEvent {\n  /** The complete arguments that were passed to the fetch function. */\n  fetchParameters: FetchParameters;\n  /** The failing HTTP response. */\n  response: Response;\n}\n\n/**\n * The public interface for all rate-limiting strategies. Library consumers are\n * welcome to provide their own implementations of this interface in the Scraper\n * constructor options.\n *\n * The {@link RateLimitEvent} object contains both the request and response\n * information associated with the event.\n *\n * @example\n * import { Scraper, RateLimitStrategy } from \"@the-convocation/twitter-scraper\";\n *\n * // A custom rate-limiting implementation that just logs request/response information.\n * class ConsoleLogRateLimitStrategy implements RateLimitStrategy {\n *   async onRateLimit(event: RateLimitEvent): Promise<void> {\n *     console.log(event.fetchParameters, event.response);\n *   }\n * }\n *\n * const scraper = new Scraper({\n *   rateLimitStrategy: new ConsoleLogRateLimitStrategy(),\n * });\n */\nexport interface RateLimitStrategy {\n  /**\n   * Called when the scraper is rate limited.\n   * @param event The event information, including the request and response info.\n   */\n  onRateLimit(event: RateLimitEvent): Promise<void>;\n}\n\n/**\n * A rate-limiting strategy that simply waits for the current rate limit period to expire.\n * This has been known to take up to 13 minutes, in some cases.\n */\nexport class WaitingRateLimitStrategy implements RateLimitStrategy {\n  async onRateLimit({ response: res }: RateLimitEvent): Promise<void> {\n    /*\n      Known headers at this point:\n      - x-rate-limit-limit: Maximum number of requests per time period?\n      - x-rate-limit-reset: UNIX timestamp when the current rate limit will be reset.\n      - x-rate-limit-remaining: Number of requests remaining in current time period?\n      */\n    const xRateLimitLimit = res.headers.get('x-rate-limit-limit');\n    const xRateLimitRemaining = res.headers.get('x-rate-limit-remaining');\n    const xRateLimitReset = res.headers.get('x-rate-limit-reset');\n\n    log(\n      `Rate limit event: limit=${xRateLimitLimit}, remaining=${xRateLimitRemaining}, reset=${xRateLimitReset}`,\n    );\n\n    if (xRateLimitRemaining == '0' && xRateLimitReset) {\n      const currentTime = new Date().valueOf() / 1000;\n      const timeDeltaMs = 1000 * (parseInt(xRateLimitReset) - currentTime);\n\n      // I have seen this block for 800s (~13 *minutes*)\n      await new Promise((resolve) => setTimeout(resolve, timeDeltaMs));\n    }\n  }\n}\n\n/**\n * A rate-limiting strategy that throws an {@link ApiError} when a rate limiting event occurs.\n */\nexport class ErrorRateLimitStrategy implements RateLimitStrategy {\n  async onRateLimit({ response: res }: RateLimitEvent): Promise<void> {\n    throw await ApiError.fromResponse(res);\n  }\n}\n","import { Cookie, CookieJar } from 'tough-cookie';\nimport setCookie from 'set-cookie-parser';\nimport type { Headers as HeadersPolyfill } from 'headers-polyfill';\nimport debug from 'debug';\n\nconst log = debug('twitter-scraper:requests');\n\n/**\n * Updates a cookie jar with the Set-Cookie headers from the provided Headers instance.\n * @param cookieJar The cookie jar to update.\n * @param headers The response headers to populate the cookie jar with.\n */\nexport async function updateCookieJar(\n  cookieJar: CookieJar,\n  headers: Headers | HeadersPolyfill,\n) {\n  // Try to use getSetCookie() if available (proper way to get all set-cookie headers)\n  let setCookieHeaders: string[] = [];\n\n  if (typeof headers.getSetCookie === 'function') {\n    setCookieHeaders = headers.getSetCookie();\n  } else {\n    // Fallback: get the single set-cookie header\n    const setCookieHeader = headers.get('set-cookie');\n    if (setCookieHeader) {\n      // Split combined set-cookie headers\n      setCookieHeaders = setCookie.splitCookiesString(setCookieHeader);\n    }\n  }\n\n  if (setCookieHeaders.length > 0) {\n    for (const cookieStr of setCookieHeaders) {\n      const cookie = Cookie.parse(cookieStr);\n      if (!cookie) {\n        log(`Failed to parse cookie: ${cookieStr.substring(0, 100)}`);\n        continue;\n      }\n\n      // Skip cookies that are being explicitly deleted (Max-Age=0 or expired)\n      // This prevents twitter from clearing important cookies like ct0\n      if (\n        cookie.maxAge === 0 ||\n        (cookie.expires && cookie.expires < new Date())\n      ) {\n        if (cookie.key === 'ct0') {\n          log(`Skipping deletion of ct0 cookie (Max-Age=0)`);\n        }\n        continue;\n      }\n\n      try {\n        const url = `${cookie.secure ? 'https' : 'http'}://${cookie.domain}${\n          cookie.path\n        }`;\n        await cookieJar.setCookie(cookie, url);\n        if (cookie.key === 'ct0') {\n          log(\n            `Successfully set ct0 cookie with value: ${cookie.value.substring(\n              0,\n              20,\n            )}...`,\n          );\n        }\n      } catch (err) {\n        // Log cookie setting errors\n        log(`Failed to set cookie ${cookie.key}: ${err}`);\n        if (cookie.key === 'ct0') {\n          log(`FAILED to set ct0 cookie! Error: ${err}`);\n        }\n      }\n    }\n  } else if (typeof document !== 'undefined') {\n    for (const cookie of document.cookie.split(';')) {\n      const hardCookie = Cookie.parse(cookie);\n      if (hardCookie) {\n        await cookieJar.setCookie(hardCookie, document.location.toString());\n      }\n    }\n  }\n}\n","import debug from 'debug';\n\nconst log = debug('twitter-scraper:xpff');\n\nlet isoCrypto: Crypto | null = null;\n\nasync function getCrypto(): Promise<Crypto> {\n  if (isoCrypto != null) {\n    return isoCrypto;\n  }\n\n  // In Node.js, the global `crypto` object is only available from v19.0.0 onwards.\n  // For earlier versions, we need to import the 'crypto' module.\n  if (typeof crypto === 'undefined') {\n    log('Global crypto is undefined, importing from crypto module...');\n    const { webcrypto } = await import('crypto');\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    isoCrypto = webcrypto as any;\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    return webcrypto as any;\n  }\n  isoCrypto = crypto;\n  return crypto;\n}\n\nasync function sha256(message: string): Promise<Uint8Array> {\n  const msgBuffer = new TextEncoder().encode(message);\n  const crypto = await getCrypto();\n  const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);\n  return new Uint8Array(hashBuffer);\n}\n\n// https://stackoverflow.com/a/40031979\nfunction buf2hex(buffer: ArrayBuffer): string {\n  return [...new Uint8Array(buffer)]\n    .map((x) => x.toString(16).padStart(2, '0'))\n    .join('');\n}\n\n// Adapted from https://github.com/dsekz/twitter-x-xp-forwarded-for-header\nexport class XPFFHeaderGenerator {\n  constructor(private readonly seed: string) {}\n\n  private async deriveKey(guestId: string): Promise<Uint8Array> {\n    const combined = `${this.seed}${guestId}`;\n    const result = await sha256(combined);\n    return result;\n  }\n\n  async generateHeader(plaintext: string, guestId: string): Promise<string> {\n    log(`Generating XPFF key for guest ID: ${guestId}`);\n    const key = await this.deriveKey(guestId);\n    const crypto = await getCrypto();\n    const nonce = crypto.getRandomValues(new Uint8Array(12));\n    const cipher = await crypto.subtle.importKey(\n      'raw',\n      key as BufferSource,\n      { name: 'AES-GCM' },\n      false,\n      ['encrypt'],\n    );\n    const encrypted = await crypto.subtle.encrypt(\n      {\n        name: 'AES-GCM',\n        iv: nonce,\n      },\n      cipher,\n      new TextEncoder().encode(plaintext),\n    );\n\n    // Combine nonce and encrypted data\n    const combined = new Uint8Array(nonce.length + encrypted.byteLength);\n    combined.set(nonce);\n    combined.set(new Uint8Array(encrypted), nonce.length);\n    const result = buf2hex(combined.buffer);\n\n    log(`XPFF header generated for guest ID ${guestId}: ${result}`);\n\n    return result;\n  }\n}\n\nconst xpffBaseKey =\n  '0e6be1f1e21ffc33590b888fd4dc81b19713e570e805d4e5df80a493c9571a05';\n\nfunction xpffPlain(): string {\n  const timestamp = Date.now();\n  return JSON.stringify({\n    navigator_properties: {\n      hasBeenActive: 'true',\n      userAgent:\n        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36',\n      webdriver: 'false',\n    },\n    created_at: timestamp,\n  });\n}\n\nexport async function generateXPFFHeader(guestId: string): Promise<string> {\n  const generator = new XPFFHeaderGenerator(xpffBaseKey);\n  const plaintext = xpffPlain();\n  return generator.generateHeader(plaintext, guestId);\n}\n","import { Cookie, CookieJar, MemoryCookieStore } from 'tough-cookie';\nimport { updateCookieJar } from './requests';\nimport { Headers } from 'headers-polyfill';\nimport fetch from 'cross-fetch';\nimport { FetchTransformOptions, flexParseJson } from './api';\nimport {\n  RateLimitEvent,\n  RateLimitStrategy,\n  WaitingRateLimitStrategy,\n} from './rate-limit';\nimport { AuthenticationError } from './errors';\nimport debug from 'debug';\nimport { generateXPFFHeader } from './xpff';\n\nconst log = debug('twitter-scraper:auth');\n\nexport interface TwitterAuthOptions {\n  fetch: typeof fetch;\n  transform: Partial<FetchTransformOptions>;\n  rateLimitStrategy: RateLimitStrategy;\n  experimental: {\n    xClientTransactionId?: boolean;\n    xpff?: boolean;\n  };\n}\n\nexport interface TwitterAuth {\n  fetch: typeof fetch;\n\n  /**\n   * How to behave when being rate-limited.\n   * @param event The event information.\n   */\n  onRateLimit(event: RateLimitEvent): Promise<void>;\n\n  /**\n   * Returns the current cookie jar.\n   */\n  cookieJar(): CookieJar;\n\n  /**\n   * Returns the current cookies.\n   */\n  getCookies(): Promise<Cookie[]>;\n\n  /**\n   * Returns if a user is logged-in to Twitter through this instance.\n   * @returns `true` if a user is logged-in; otherwise `false`.\n   */\n  isLoggedIn(): Promise<boolean>;\n\n  /**\n   * Logs into a Twitter account.\n   * @param username The username to log in with.\n   * @param password The password to log in with.\n   * @param email The email to log in with, if you have email confirmation enabled.\n   * @param twoFactorSecret The secret to generate two factor authentication tokens with, if you have two factor authentication enabled.\n   */\n  login(\n    username: string,\n    password: string,\n    email?: string,\n    twoFactorSecret?: string,\n  ): Promise<void>;\n\n  /**\n   * Logs out of the current session.\n   */\n  logout(): Promise<void>;\n\n  /**\n   * Deletes the current guest token token.\n   */\n  deleteToken(): void;\n\n  /**\n   * Returns if the authentication state has a token.\n   * @returns `true` if the authentication state has a token; `false` otherwise.\n   */\n  hasToken(): boolean;\n\n  /**\n   * Returns the time that authentication was performed.\n   * @returns The time at which the authentication token was created, or `null` if it hasn't been created yet.\n   */\n  authenticatedAt(): Date | null;\n\n  /**\n   * Installs the authentication information into a headers-like object. If needed, the\n   * authentication token will be updated from the API automatically.\n   * @param headers A Headers instance representing a request's headers.\n   * @param _url The URL being requested (currently unused, reserved for future use).\n   * @param bearerTokenOverride Optional bearer token to use instead of the default one.\n   */\n  installTo(\n    headers: Headers,\n    _url: string,\n    bearerTokenOverride?: string,\n  ): Promise<void>;\n}\n\n/**\n * Wraps the provided fetch function with transforms.\n * @param fetchFn The fetch function.\n * @param transform The transform options.\n * @returns The input fetch function, wrapped with the provided transforms.\n */\nfunction withTransform(\n  fetchFn: typeof fetch,\n  transform?: Partial<FetchTransformOptions>,\n): typeof fetch {\n  return async (input, init) => {\n    const fetchArgs = (await transform?.request?.(input, init)) ?? [\n      input,\n      init,\n    ];\n    const res = await fetchFn(...fetchArgs);\n    return (await transform?.response?.(res)) ?? res;\n  };\n}\n\n/**\n * A guest authentication token manager. Automatically handles token refreshes.\n */\nexport class TwitterGuestAuth implements TwitterAuth {\n  protected bearerToken: string;\n  protected jar: CookieJar;\n  protected guestToken?: string;\n  protected guestCreatedAt?: Date;\n  protected rateLimitStrategy: RateLimitStrategy;\n\n  fetch: typeof fetch;\n\n  constructor(\n    bearerToken: string,\n    readonly options?: Partial<TwitterAuthOptions>,\n  ) {\n    this.fetch = withTransform(options?.fetch ?? fetch, options?.transform);\n    this.rateLimitStrategy =\n      options?.rateLimitStrategy ?? new WaitingRateLimitStrategy();\n    this.bearerToken = bearerToken;\n    this.jar = new CookieJar();\n  }\n\n  async onRateLimit(event: RateLimitEvent): Promise<void> {\n    await this.rateLimitStrategy.onRateLimit(event);\n  }\n\n  cookieJar(): CookieJar {\n    return this.jar;\n  }\n\n  isLoggedIn(): Promise<boolean> {\n    return Promise.resolve(false);\n  }\n\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  login(_username: string, _password: string, _email?: string): Promise<void> {\n    return this.updateGuestToken();\n  }\n\n  logout(): Promise<void> {\n    this.deleteToken();\n    this.jar = new CookieJar();\n    return Promise.resolve();\n  }\n\n  deleteToken() {\n    delete this.guestToken;\n    delete this.guestCreatedAt;\n  }\n\n  hasToken(): boolean {\n    return this.guestToken != null;\n  }\n\n  authenticatedAt(): Date | null {\n    if (this.guestCreatedAt == null) {\n      return null;\n    }\n\n    return new Date(this.guestCreatedAt);\n  }\n\n  async installTo(\n    headers: Headers,\n    _url: string,\n    bearerTokenOverride?: string,\n  ): Promise<void> {\n    // Use the override token if provided, otherwise use the instance's bearer token\n    const tokenToUse = bearerTokenOverride ?? this.bearerToken;\n\n    // Only use guest tokens when not overriding the bearer token\n    // Guest tokens are tied to the bearer token they were generated with\n    if (!bearerTokenOverride) {\n      if (this.shouldUpdate()) {\n        await this.updateGuestToken();\n      }\n\n      if (this.guestToken) {\n        headers.set('x-guest-token', this.guestToken);\n      }\n    }\n\n    headers.set('authorization', `Bearer ${tokenToUse}`);\n    headers.set(\n      'user-agent',\n      'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36',\n    );\n\n    await this.installCsrfToken(headers);\n\n    if (this.options?.experimental?.xpff) {\n      const guestId = await this.guestId();\n      if (guestId != null) {\n        const xpffHeader = await generateXPFFHeader(guestId);\n        headers.set('x-xp-forwarded-for', xpffHeader);\n      }\n    }\n\n    headers.set('cookie', await this.getCookieString());\n  }\n\n  async installCsrfToken(headers: Headers): Promise<void> {\n    const cookies = await this.getCookies();\n    const xCsrfToken = cookies.find((cookie) => cookie.key === 'ct0');\n    if (xCsrfToken) {\n      headers.set('x-csrf-token', xCsrfToken.value);\n    }\n  }\n\n  protected async setCookie(key: string, value: string): Promise<void> {\n    const cookie = Cookie.parse(`${key}=${value}`);\n    if (!cookie) {\n      throw new Error('Failed to parse cookie.');\n    }\n\n    await this.jar.setCookie(cookie, this.getCookieJarUrl());\n\n    if (typeof document !== 'undefined') {\n      document.cookie = cookie.toString();\n    }\n  }\n\n  public async getCookies(): Promise<Cookie[]> {\n    return this.jar.getCookies(this.getCookieJarUrl());\n  }\n\n  protected async getCookieString(): Promise<string> {\n    const cookies = await this.getCookies();\n    return cookies.map((cookie) => `${cookie.key}=${cookie.value}`).join('; ');\n  }\n\n  protected async removeCookie(key: string): Promise<void> {\n    //@ts-expect-error don't care\n    const store: MemoryCookieStore = this.jar.store;\n    const cookies = await this.jar.getCookies(this.getCookieJarUrl());\n    for (const cookie of cookies) {\n      if (!cookie.domain || !cookie.path) continue;\n      store.removeCookie(cookie.domain, cookie.path, key);\n\n      if (typeof document !== 'undefined') {\n        document.cookie = `${cookie.key}=; Max-Age=0; path=${cookie.path}; domain=${cookie.domain}`;\n      }\n    }\n  }\n\n  private getCookieJarUrl(): string {\n    return typeof document !== 'undefined'\n      ? document.location.toString()\n      : 'https://x.com';\n  }\n\n  protected async guestId(): Promise<string | null> {\n    const cookies = await this.getCookies();\n    const guestIdCookie = cookies.find((cookie) => cookie.key === 'guest_id');\n    return guestIdCookie ? guestIdCookie.value : null;\n  }\n\n  /**\n   * Updates the authentication state with a new guest token from the Twitter API.\n   */\n  protected async updateGuestToken() {\n    try {\n      await this.updateGuestTokenCore();\n    } catch (err) {\n      log('Failed to update guest token; this may cause issues:', err);\n    }\n  }\n\n  private async updateGuestTokenCore() {\n    const guestActivateUrl = 'https://api.x.com/1.1/guest/activate.json';\n\n    const headers = new Headers({\n      Authorization: `Bearer ${this.bearerToken}`,\n      Cookie: await this.getCookieString(),\n    });\n\n    log(`Making POST request to ${guestActivateUrl}`);\n\n    const res = await this.fetch(guestActivateUrl, {\n      method: 'POST',\n      headers: headers,\n      referrerPolicy: 'no-referrer',\n    });\n\n    await updateCookieJar(this.jar, res.headers);\n\n    if (!res.ok) {\n      throw new AuthenticationError(await res.text());\n    }\n\n    const o = await flexParseJson<any>(res);\n    if (o == null || o['guest_token'] == null) {\n      throw new AuthenticationError('guest_token not found.');\n    }\n\n    const newGuestToken = o['guest_token'];\n    if (typeof newGuestToken !== 'string') {\n      throw new AuthenticationError('guest_token was not a string.');\n    }\n\n    this.guestToken = newGuestToken;\n    this.guestCreatedAt = new Date();\n\n    await this.setCookie('gt', newGuestToken);\n\n    log(`Updated guest token: ${newGuestToken}`);\n  }\n\n  /**\n   * Returns if the authentication token needs to be updated or not.\n   * @returns `true` if the token needs to be updated; `false` otherwise.\n   */\n  private shouldUpdate(): boolean {\n    return (\n      !this.hasToken() ||\n      (this.guestCreatedAt != null &&\n        this.guestCreatedAt <\n          new Date(new Date().valueOf() - 3 * 60 * 60 * 1000))\n    );\n  }\n}\n","export interface PlatformExtensions {\n  /**\n   * Randomizes the runtime's TLS ciphers to bypass TLS client fingerprinting, which\n   * hopefully avoids random 404s on some requests.\n   *\n   * **References:**\n   * - https://github.com/imputnet/cobalt/pull/574\n   */\n  randomizeCiphers(): Promise<void>;\n}\n\nexport const genericPlatform = new (class implements PlatformExtensions {\n  randomizeCiphers(): Promise<void> {\n    return Promise.resolve();\n  }\n})();\n","import { PlatformExtensions, genericPlatform } from './platform-interface';\n\nexport * from './platform-interface';\n\ndeclare const PLATFORM_NODE: boolean;\n\nexport class Platform implements PlatformExtensions {\n  async randomizeCiphers() {\n    const platform = await Platform.importPlatform();\n    await platform?.randomizeCiphers();\n  }\n\n  private static async importPlatform(): Promise<null | PlatformExtensions> {\n    if (PLATFORM_NODE) {\n      const { platform } = await import('./node/index.js');\n      return platform as PlatformExtensions;\n    }\n\n    return genericPlatform;\n  }\n}\n","import fetch from 'cross-fetch';\nimport debug from 'debug';\n\nconst log = debug('twitter-scraper:xctxid');\n\n// @ts-expect-error import type annotation (\"the current file is a CommonJS module\")\ntype LinkeDOM = typeof import('linkedom');\n\nlet linkedom: LinkeDOM | null = null;\nasync function linkedomImport(): Promise<LinkeDOM> {\n  if (!linkedom) {\n    const mod = await import('linkedom');\n    linkedom = mod;\n    return mod;\n  }\n  return linkedom;\n}\n\nasync function parseHTML(html: string): Promise<Window & typeof globalThis> {\n  if (typeof window !== 'undefined') {\n    const { defaultView } = new DOMParser().parseFromString(html, 'text/html');\n    if (!defaultView) {\n      throw new Error('Failed to get defaultView from parsed HTML.');\n    }\n    return defaultView;\n  } else {\n    const { DOMParser } = await linkedomImport();\n    return new DOMParser().parseFromString(html, 'text/html').defaultView;\n  }\n}\n\n// Copied from https://github.com/Lqm1/x-client-transaction-id/blob/main/utils.ts with minor tweaks to support us passing a custom fetch function\nasync function handleXMigration(fetchFn: typeof fetch): Promise<Document> {\n  // Set headers to mimic a browser request\n  const headers = {\n    accept:\n      'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',\n    'accept-language': 'ja',\n    'cache-control': 'no-cache',\n    pragma: 'no-cache',\n    priority: 'u=0, i',\n    'sec-ch-ua':\n      '\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\"',\n    'sec-ch-ua-mobile': '?0',\n    'sec-ch-ua-platform': '\"Windows\"',\n    'sec-fetch-dest': 'document',\n    'sec-fetch-mode': 'navigate',\n    'sec-fetch-site': 'none',\n    'sec-fetch-user': '?1',\n    'upgrade-insecure-requests': '1',\n    'user-agent':\n      'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36',\n  };\n\n  // Fetch X.com homepage\n  const response = await fetchFn('https://x.com', {\n    headers,\n  });\n\n  if (!response.ok) {\n    throw new Error(`Failed to fetch X homepage: ${response.statusText}`);\n  }\n\n  const htmlText = await response.text();\n\n  // Parse HTML using linkedom\n  let dom = await parseHTML(htmlText);\n  let document = dom.window.document;\n\n  // Check for migration redirection links\n  const migrationRedirectionRegex = new RegExp(\n    '(http(?:s)?://(?:www\\\\.)?(twitter|x){1}\\\\.com(/x)?/migrate([/?])?tok=[a-zA-Z0-9%\\\\-_]+)+',\n    'i',\n  );\n\n  const metaRefresh = document.querySelector(\"meta[http-equiv='refresh']\");\n  const metaContent = metaRefresh\n    ? metaRefresh.getAttribute('content') || ''\n    : '';\n\n  const migrationRedirectionUrl =\n    migrationRedirectionRegex.exec(metaContent) ||\n    migrationRedirectionRegex.exec(htmlText);\n\n  if (migrationRedirectionUrl) {\n    // Follow redirection URL\n    const redirectResponse = await fetch(migrationRedirectionUrl[0]);\n\n    if (!redirectResponse.ok) {\n      throw new Error(\n        `Failed to follow migration redirection: ${redirectResponse.statusText}`,\n      );\n    }\n\n    const redirectHtml = await redirectResponse.text();\n    dom = await parseHTML(redirectHtml);\n    document = dom.window.document;\n  }\n\n  // Handle migration form if present\n  const migrationForm =\n    document.querySelector(\"form[name='f']\") ||\n    document.querySelector(\"form[action='https://x.com/x/migrate']\");\n\n  if (migrationForm) {\n    const url =\n      migrationForm.getAttribute('action') || 'https://x.com/x/migrate';\n    const method = migrationForm.getAttribute('method') || 'POST';\n\n    // Collect form input fields\n    const requestPayload = new FormData();\n\n    const inputFields = migrationForm.querySelectorAll('input');\n    for (const element of Array.from(inputFields)) {\n      const name = element.getAttribute('name');\n      const value = element.getAttribute('value');\n      if (name && value) {\n        requestPayload.append(name, value);\n      }\n    }\n\n    // Submit form using POST request\n    const formResponse = await fetch(url, {\n      method: method,\n      body: requestPayload,\n      headers,\n    });\n\n    if (!formResponse.ok) {\n      throw new Error(\n        `Failed to submit migration form: ${formResponse.statusText}`,\n      );\n    }\n\n    const formHtml = await formResponse.text();\n    dom = await parseHTML(formHtml);\n    document = dom.window.document;\n  }\n\n  // Return final DOM document\n  return document;\n}\n\nlet ClientTransaction:\n  | typeof import('x-client-transaction-id')['ClientTransaction']\n  | null = null;\nasync function clientTransaction(): Promise<\n  typeof import('x-client-transaction-id')['ClientTransaction']\n> {\n  if (!ClientTransaction) {\n    const mod = await import('x-client-transaction-id');\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    ClientTransaction = mod.ClientTransaction as any;\n    // eslint-disable-next-line @typescript-eslint/no-explicit-any\n    return mod.ClientTransaction as any;\n  }\n  return ClientTransaction;\n}\n\nexport async function generateTransactionId(\n  url: string,\n  fetchFn: typeof fetch,\n  method: 'GET' | 'POST',\n) {\n  const parsedUrl = new URL(url);\n  const path = parsedUrl.pathname;\n\n  log(`Generating transaction ID for ${method} ${path}`);\n  const document = await handleXMigration(fetchFn);\n  const ClientTransactionClass = await clientTransaction();\n  const transaction = await ClientTransactionClass.create(document);\n  const transactionId = await transaction.generateTransactionId(method, path);\n  log(`Transaction ID: ${transactionId}`);\n\n  return transactionId;\n}\n","import { FetchParameters } from './api-types';\nimport { TwitterAuth, TwitterGuestAuth } from './auth';\nimport { ApiError } from './errors';\nimport { Platform, PlatformExtensions } from './platform';\nimport { updateCookieJar } from './requests';\nimport { Headers } from 'headers-polyfill';\nimport debug from 'debug';\nimport { generateTransactionId } from './xctxid';\n\nconst log = debug('twitter-scraper:api');\n\nexport interface FetchTransformOptions {\n  /**\n   * Transforms the request options before a request is made. This executes after all of the default\n   * parameters have been configured, and is stateless. It is safe to return new request options\n   * objects.\n   * @param args The request options.\n   * @returns The transformed request options.\n   */\n  request: (\n    ...args: FetchParameters\n  ) => FetchParameters | Promise<FetchParameters>;\n\n  /**\n   * Transforms the response after a request completes. This executes immediately after the request\n   * completes, and is stateless. It is safe to return a new response object.\n   * @param response The response object.\n   * @returns The transformed response object.\n   */\n  response: (response: Response) => Response | Promise<Response>;\n}\n\nexport const bearerToken =\n  'AAAAAAAAAAAAAAAAAAAAAFQODgEAAAAAVHTp76lzh3rFzcHbmHVvQxYYpTw%3DckAlMINMjmCwxUcaXbAN4XqJVdgMJaHqNOFgPMK0zN1qLqLQCF';\n\nexport const bearerToken2 =\n  'AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA';\n\nexport async function jitter(maxMs: number): Promise<void> {\n  const jitter = Math.random() * maxMs;\n  await new Promise((resolve) => setTimeout(resolve, jitter));\n}\n\n/**\n * An API result container.\n */\nexport type RequestApiResult<T> =\n  | { success: true; value: T }\n  | { success: false; err: Error };\n\n/**\n * Used internally to send HTTP requests to the Twitter API.\n * @internal\n * @param url - The URL to send the request to.\n * @param auth - The instance of {@link TwitterAuth} that will be used to authorize this request.\n * @param method - The HTTP method used when sending this request.\n * @param platform - The platform extensions to use.\n * @param headers - The headers to include in the request.\n * @param bearerTokenOverride - Optional bearer token to use instead of the default one.\n */\nexport async function requestApi<T>(\n  url: string,\n  auth: TwitterAuth,\n  method: 'GET' | 'POST' = 'GET',\n  platform: PlatformExtensions = new Platform(),\n  headers: Headers = new Headers(),\n  bearerTokenOverride?: string,\n): Promise<RequestApiResult<T>> {\n  log(`Making ${method} request to ${url}`);\n\n  await auth.installTo(headers, url, bearerTokenOverride);\n  await platform.randomizeCiphers();\n\n  if (\n    auth instanceof TwitterGuestAuth &&\n    auth.options?.experimental?.xClientTransactionId\n  ) {\n    const transactionId = await generateTransactionId(\n      url,\n      auth.fetch.bind(auth),\n      method,\n    );\n    headers.set('x-client-transaction-id', transactionId);\n  }\n\n  let res: Response;\n  do {\n    const fetchParameters: FetchParameters = [\n      url,\n      {\n        method,\n        headers,\n        credentials: 'include',\n      },\n    ];\n\n    try {\n      res = await auth.fetch(...fetchParameters);\n    } catch (err) {\n      if (!(err instanceof Error)) {\n        throw err;\n      }\n\n      return {\n        success: false,\n        err: new Error('Failed to perform request.'),\n      };\n    }\n\n    await updateCookieJar(auth.cookieJar(), res.headers);\n\n    if (res.status === 429) {\n      log('Rate limit hit, waiting for retry...');\n      await auth.onRateLimit({\n        fetchParameters: fetchParameters,\n        response: res,\n      });\n    }\n  } while (res.status === 429);\n\n  if (!res.ok) {\n    return {\n      success: false,\n      err: await ApiError.fromResponse(res),\n    };\n  }\n\n  const value: T = await flexParseJson(res);\n  if (res.headers.get('x-rate-limit-incoming') == '0') {\n    auth.deleteToken();\n    return { success: true, value };\n  } else {\n    return { success: true, value };\n  }\n}\n\nexport async function flexParseJson<T>(res: Response): Promise<T> {\n  try {\n    return await res.json();\n  } catch {\n    log('Failed to parse response as JSON, trying text parse...');\n    const text = await res.text();\n    log('Response text:', text);\n    return JSON.parse(text);\n  }\n}\n\n/** @internal */\nexport function addApiFeatures(o: object) {\n  return {\n    ...o,\n    rweb_lists_timeline_redesign_enabled: true,\n    responsive_web_graphql_exclude_directive_enabled: true,\n    verified_phone_label_enabled: false,\n    creator_subscriptions_tweet_preview_api_enabled: true,\n    responsive_web_graphql_timeline_navigation_enabled: true,\n    responsive_web_graphql_skip_user_profile_image_extensions_enabled: false,\n    tweetypie_unmention_optimization_enabled: true,\n    responsive_web_edit_tweet_api_enabled: true,\n    graphql_is_translatable_rweb_tweet_is_translatable_enabled: true,\n    view_counts_everywhere_api_enabled: true,\n    longform_notetweets_consumption_enabled: true,\n    tweet_awards_web_tipping_enabled: false,\n    freedom_of_speech_not_reach_fetch_enabled: true,\n    standardized_nudges_misinfo: true,\n    longform_notetweets_rich_text_read_enabled: true,\n    responsive_web_enhance_cards_enabled: false,\n    subscriptions_verification_info_enabled: true,\n    subscriptions_verification_info_reason_enabled: true,\n    subscriptions_verification_info_verified_since_enabled: true,\n    super_follow_badge_privacy_enabled: false,\n    super_follow_exclusive_tweet_notifications_enabled: false,\n    super_follow_tweet_api_enabled: false,\n    super_follow_user_api_enabled: false,\n    android_graphql_skip_api_media_color_palette: false,\n    creator_subscriptions_subscription_count_enabled: false,\n    blue_business_profile_image_shape_enabled: false,\n    unified_cards_ad_metadata_container_dynamic_card_content_query_enabled:\n      false,\n  };\n}\n\nexport function addApiParams(\n  params: URLSearchParams,\n  includeTweetReplies: boolean,\n): URLSearchParams {\n  params.set('include_profile_interstitial_type', '1');\n  params.set('include_blocking', '1');\n  params.set('include_blocked_by', '1');\n  params.set('include_followed_by', '1');\n  params.set('include_want_retweets', '1');\n  params.set('include_mute_edge', '1');\n  params.set('include_can_dm', '1');\n  params.set('include_can_media_tag', '1');\n  params.set('include_ext_has_nft_avatar', '1');\n  params.set('include_ext_is_blue_verified', '1');\n  params.set('include_ext_verified_type', '1');\n  params.set('skip_status', '1');\n  params.set('cards_platform', 'Web-12');\n  params.set('include_cards', '1');\n  params.set('include_ext_alt_text', 'true');\n  params.set('include_ext_limited_action_results', 'false');\n  params.set('include_quote_count', 'true');\n  params.set('include_reply_count', '1');\n  params.set('tweet_mode', 'extended');\n  params.set('include_ext_collab_control', 'true');\n  params.set('include_ext_views', 'true');\n  params.set('include_entities', 'true');\n  params.set('include_user_entities', 'true');\n  params.set('include_ext_media_color', 'true');\n  params.set('include_ext_media_availability', 'true');\n  params.set('include_ext_sensitive_media_warning', 'true');\n  params.set('include_ext_trusted_friends_metadata', 'true');\n  params.set('send_error_codes', 'true');\n  params.set('simple_quoted_tweet', 'true');\n  params.set('include_tweet_replies', `${includeTweetReplies}`);\n  params.set(\n    'ext',\n    'mediaStats,highlightedLabel,hasNftAvatar,voiceInfo,birdwatchPivot,enrichments,superFollowMetadata,unmentionInfo,editControl,collab_control,vibe',\n  );\n  return params;\n}\n","import { TwitterAuthOptions, TwitterGuestAuth } from './auth';\nimport { flexParseJson, requestApi } from './api';\nimport { CookieJar } from 'tough-cookie';\nimport { updateCookieJar } from './requests';\nimport { Headers } from 'headers-polyfill';\nimport { TwitterApiErrorRaw, AuthenticationError, ApiError } from './errors';\nimport { Type, type Static } from '@sinclair/typebox';\nimport { Check } from '@sinclair/typebox/value';\nimport * as OTPAuth from 'otpauth';\nimport { FetchParameters } from './api-types';\nimport debug from 'debug';\nimport { generateXPFFHeader } from './xpff';\nimport { generateTransactionId } from './xctxid';\n\nconst log = debug('twitter-scraper:auth-user');\n\nexport interface TwitterUserAuthFlowInitRequest {\n  flow_name: string;\n  input_flow_data: Record<string, unknown>;\n  subtask_versions: Record<string, number>;\n}\n\nexport interface TwitterUserAuthFlowSubtaskRequest {\n  flow_token: string;\n  subtask_inputs: ({\n    subtask_id: string;\n  } & Record<string, unknown>)[];\n}\n\nexport type TwitterUserAuthFlowRequest =\n  | TwitterUserAuthFlowInitRequest\n  | TwitterUserAuthFlowSubtaskRequest;\n\nexport interface TwitterUserAuthFlowResponse {\n  errors?: TwitterApiErrorRaw[];\n  flow_token?: string;\n  status?: string;\n  subtasks?: TwitterUserAuthSubtask[];\n}\n\ninterface TwitterUserAuthVerifyCredentials {\n  errors?: TwitterApiErrorRaw[];\n}\n\nconst TwitterUserAuthSubtask = Type.Object({\n  subtask_id: Type.String(),\n  enter_text: Type.Optional(Type.Object({})),\n});\ntype TwitterUserAuthSubtask = Static<typeof TwitterUserAuthSubtask>;\n\nexport type FlowTokenResultSuccess = {\n  status: 'success';\n  response: TwitterUserAuthFlowResponse;\n};\n\nexport type FlowTokenResultError = {\n  status: 'error';\n  err: Error;\n};\n\nexport type FlowTokenResult = FlowTokenResultSuccess | FlowTokenResultError;\n\nexport interface TwitterUserAuthCredentials {\n  username: string;\n  password: string;\n  email?: string;\n  twoFactorSecret?: string;\n}\n\n/**\n * The API interface provided to custom subtask handlers for interacting with the Twitter authentication flow.\n * This interface allows handlers to send flow requests and access the current flow token.\n *\n * The API is passed to each subtask handler and provides methods necessary for implementing\n * custom authentication subtasks. It abstracts away the low-level details of communicating\n * with Twitter's authentication API.\n *\n * @example\n * ```typescript\n * import { Scraper, FlowSubtaskHandler } from \"@the-convocation/twitter-scraper\";\n *\n * // A custom subtask handler that implements a hypothetical example subtask\n * const exampleHandler: FlowSubtaskHandler = async (subtaskId, response, credentials, api) => {\n *   // Process the example subtask somehow\n *   const data = await processExampleTask();\n *\n *   // Submit the processed data using the provided API\n *   return await api.sendFlowRequest({\n *     flow_token: api.getFlowToken(),\n *     subtask_inputs: [{\n *       subtask_id: subtaskId,\n *       example_data: {\n *         value: data,\n *         link: \"next_link\"\n *       }\n *     }]\n *   });\n * };\n *\n * const scraper = new Scraper();\n * scraper.registerAuthSubtaskHandler(\"ExampleSubtask\", exampleHandler);\n * ```\n */\nexport interface FlowSubtaskHandlerApi {\n  /**\n   * Send a flow request to the Twitter API.\n   * @param request The request object containing flow token and subtask inputs\n   * @returns The result of the flow task\n   */\n  sendFlowRequest: (\n    request: TwitterUserAuthFlowRequest,\n  ) => Promise<FlowTokenResult>;\n  /**\n   * Gets the current flow token.\n   * @returns The current flow token\n   */\n  getFlowToken: () => string;\n}\n\n/**\n * A handler function for processing Twitter authentication flow subtasks.\n * Library consumers can implement and register custom handlers for new or\n * existing subtask types using the Scraper.registerAuthSubtaskHandler method.\n *\n * Each subtask handler is called when its corresponding subtask ID is encountered\n * during the authentication flow. The handler receives the subtask ID, the previous\n * response data, the user's credentials, and an API interface for interacting with\n * the authentication flow.\n *\n * Handlers should process their specific subtask and return either a successful response\n * or an error. Success responses typically lead to the next subtask in the flow, while\n * errors will halt the authentication process.\n *\n * @param subtaskId - The identifier of the subtask being handled\n * @param previousResponse - The complete response from the previous authentication flow step\n * @param credentials - The user's authentication credentials including username, password, etc.\n * @param api - An interface providing methods to interact with the authentication flow\n * @returns A promise resolving to either a successful flow response or an error\n *\n * @example\n * ```typescript\n * import { Scraper, FlowSubtaskHandler } from \"@the-convocation/twitter-scraper\";\n *\n * // Custom handler for a hypothetical verification subtask\n * const verificationHandler: FlowSubtaskHandler = async (\n *   subtaskId,\n *   response,\n *   credentials,\n *   api\n * ) => {\n *   // Extract the verification data from the response\n *   const verificationData = response.subtasks?.[0].exampleData?.value;\n *   if (!verificationData) {\n *     return {\n *       status: 'error',\n *       err: new Error('No verification data found in response')\n *     };\n *   }\n *\n *   // Process the verification data somehow\n *   const result = await processVerification(verificationData);\n *\n *   // Submit the result using the flow API\n *   return await api.sendFlowRequest({\n *     flow_token: api.getFlowToken(),\n *     subtask_inputs: [{\n *       subtask_id: subtaskId,\n *       example_verification: {\n *         value: result,\n *         link: \"next_link\"\n *       }\n *     }]\n *   });\n * };\n *\n * const scraper = new Scraper();\n * scraper.registerAuthSubtaskHandler(\"ExampleVerificationSubtask\", verificationHandler);\n *\n * // Later, when logging in...\n * await scraper.login(\"username\", \"password\");\n * ```\n */\nexport type FlowSubtaskHandler = (\n  subtaskId: string,\n  previousResponse: TwitterUserAuthFlowResponse,\n  credentials: TwitterUserAuthCredentials,\n  api: FlowSubtaskHandlerApi,\n) => Promise<FlowTokenResult>;\n\n/**\n * A user authentication token manager.\n */\nexport class TwitterUserAuth extends TwitterGuestAuth {\n  private readonly subtaskHandlers: Map<string, FlowSubtaskHandler> = new Map();\n\n  constructor(bearerToken: string, options?: Partial<TwitterAuthOptions>) {\n    super(bearerToken, options);\n    this.initializeDefaultHandlers();\n  }\n\n  /**\n   * Register a custom subtask handler or override an existing one\n   * @param subtaskId The ID of the subtask to handle\n   * @param handler The handler function that processes the subtask\n   */\n  registerSubtaskHandler(subtaskId: string, handler: FlowSubtaskHandler): void {\n    this.subtaskHandlers.set(subtaskId, handler);\n  }\n\n  private initializeDefaultHandlers(): void {\n    this.subtaskHandlers.set(\n      'LoginJsInstrumentationSubtask',\n      this.handleJsInstrumentationSubtask.bind(this),\n    );\n    this.subtaskHandlers.set(\n      'LoginEnterUserIdentifierSSO',\n      this.handleEnterUserIdentifierSSO.bind(this),\n    );\n    this.subtaskHandlers.set(\n      'LoginEnterAlternateIdentifierSubtask',\n      this.handleEnterAlternateIdentifierSubtask.bind(this),\n    );\n    this.subtaskHandlers.set(\n      'LoginEnterPassword',\n      this.handleEnterPassword.bind(this),\n    );\n    this.subtaskHandlers.set(\n      'AccountDuplicationCheck',\n      this.handleAccountDuplicationCheck.bind(this),\n    );\n    this.subtaskHandlers.set(\n      'LoginTwoFactorAuthChallenge',\n      this.handleTwoFactorAuthChallenge.bind(this),\n    );\n    this.subtaskHandlers.set('LoginAcid', this.handleAcid.bind(this));\n    this.subtaskHandlers.set(\n      'LoginSuccessSubtask',\n      this.handleSuccessSubtask.bind(this),\n    );\n  }\n\n  async isLoggedIn(): Promise<boolean> {\n    const cookie = await this.getCookieString();\n    return cookie.includes('ct0=');\n  }\n\n  async login(\n    username: string,\n    password: string,\n    email?: string,\n    twoFactorSecret?: string,\n  ): Promise<void> {\n    await this.updateGuestToken();\n\n    const credentials: TwitterUserAuthCredentials = {\n      username,\n      password,\n      email,\n      twoFactorSecret,\n    };\n\n    let next: FlowTokenResult = await this.initLogin();\n    while (next.status === 'success' && next.response.subtasks?.length) {\n      const flowToken = next.response.flow_token;\n      if (flowToken == null) {\n        // Should never happen\n        throw new Error('flow_token not found.');\n      }\n\n      const subtaskId = next.response.subtasks[0].subtask_id;\n      const handler = this.subtaskHandlers.get(subtaskId);\n\n      if (handler) {\n        next = await handler(subtaskId, next.response, credentials, {\n          sendFlowRequest: this.executeFlowTask.bind(this),\n          getFlowToken: () => flowToken,\n        });\n      } else {\n        throw new Error(`Unknown subtask ${subtaskId}`);\n      }\n    }\n    if (next.status === 'error') {\n      throw next.err;\n    }\n  }\n\n  async logout(): Promise<void> {\n    if (!this.hasToken()) {\n      return;\n    }\n\n    try {\n      await requestApi<void>(\n        'https://api.x.com/1.1/account/logout.json',\n        this,\n        'POST',\n      );\n    } catch (error) {\n      // Ignore errors during logout but still clean up state\n      console.warn('Error during logout:', error);\n    } finally {\n      this.deleteToken();\n      this.jar = new CookieJar();\n    }\n  }\n\n  async installTo(\n    headers: Headers,\n    _url: string,\n    bearerTokenOverride?: string,\n  ): Promise<void> {\n    // Use the override token if provided, otherwise use the instance's bearer token\n    const tokenToUse = bearerTokenOverride ?? this.bearerToken;\n    headers.set('authorization', `Bearer ${tokenToUse}`);\n    headers.set(\n      'user-agent',\n      'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36',\n    );\n\n    if (this.guestToken) {\n      // Guest token is optional for authenticated users\n      headers.set('x-guest-token', this.guestToken);\n    }\n\n    await this.installCsrfToken(headers);\n\n    if (this.options?.experimental?.xpff) {\n      const guestId = await this.guestId();\n      if (guestId != null) {\n        const xpffHeader = await generateXPFFHeader(guestId);\n        headers.set('x-xp-forwarded-for', xpffHeader);\n      }\n    }\n\n    const cookie = await this.getCookieString();\n    headers.set('cookie', cookie);\n  }\n\n  private async initLogin(): Promise<FlowTokenResult> {\n    // Reset certain session-related cookies because Twitter complains sometimes if we don't\n    this.removeCookie('twitter_ads_id=');\n    this.removeCookie('ads_prefs=');\n    this.removeCookie('_twitter_sess=');\n    this.removeCookie('zipbox_forms_auth_token=');\n    this.removeCookie('lang=');\n    this.removeCookie('bouncer_reset_cookie=');\n    this.removeCookie('twid=');\n    this.removeCookie('twitter_ads_idb=');\n    this.removeCookie('email_uid=');\n    this.removeCookie('external_referer=');\n    this.removeCookie('ct0=');\n    this.removeCookie('aa_u=');\n    this.removeCookie('__cf_bm=');\n\n    return await this.executeFlowTask({\n      flow_name: 'login',\n      input_flow_data: {\n        flow_context: {\n          debug_overrides: {},\n          start_location: {\n            location: 'unknown',\n          },\n        },\n      },\n      subtask_versions: {\n        action_list: 2,\n        alert_dialog: 1,\n        app_download_cta: 1,\n        check_logged_in_account: 1,\n        choice_selection: 3,\n        contacts_live_sync_permission_prompt: 0,\n        cta: 7,\n        email_verification: 2,\n        end_flow: 1,\n        enter_date: 1,\n        enter_email: 2,\n        enter_password: 5,\n        enter_phone: 2,\n        enter_recaptcha: 1,\n        enter_text: 5,\n        enter_username: 2,\n        generic_urt: 3,\n        in_app_notification: 1,\n        interest_picker: 3,\n        js_instrumentation: 1,\n        menu_dialog: 1,\n        notifications_permission_prompt: 2,\n        open_account: 2,\n        open_home_timeline: 1,\n        open_link: 1,\n        phone_verification: 4,\n        privacy_options: 1,\n        security_key: 3,\n        select_avatar: 4,\n        select_banner: 2,\n        settings_list: 7,\n        show_code: 1,\n        sign_up: 2,\n        sign_up_review: 4,\n        tweet_selection_urt: 1,\n        update_users: 1,\n        upload_media: 1,\n        user_recommendations_list: 4,\n        user_recommendations_urt: 1,\n        wait_spinner: 3,\n        web_modal: 1,\n      },\n    });\n  }\n\n  private async handleJsInstrumentationSubtask(\n    subtaskId: string,\n    _prev: TwitterUserAuthFlowResponse,\n    _credentials: TwitterUserAuthCredentials,\n    api: FlowSubtaskHandlerApi,\n  ): Promise<FlowTokenResult> {\n    return await api.sendFlowRequest({\n      flow_token: api.getFlowToken(),\n      subtask_inputs: [\n        {\n          subtask_id: subtaskId,\n          js_instrumentation: {\n            response: '{}',\n            link: 'next_link',\n          },\n        },\n      ],\n    });\n  }\n\n  private async handleEnterAlternateIdentifierSubtask(\n    subtaskId: string,\n    _prev: TwitterUserAuthFlowResponse,\n    credentials: TwitterUserAuthCredentials,\n    api: FlowSubtaskHandlerApi,\n  ): Promise<FlowTokenResult> {\n    return await this.executeFlowTask({\n      flow_token: api.getFlowToken(),\n      subtask_inputs: [\n        {\n          subtask_id: subtaskId,\n          enter_text: {\n            text: credentials.email,\n            link: 'next_link',\n          },\n        },\n      ],\n    });\n  }\n\n  private async handleEnterUserIdentifierSSO(\n    subtaskId: string,\n    _prev: TwitterUserAuthFlowResponse,\n    credentials: TwitterUserAuthCredentials,\n    api: FlowSubtaskHandlerApi,\n  ): Promise<FlowTokenResult> {\n    return await this.executeFlowTask({\n      flow_token: api.getFlowToken(),\n      subtask_inputs: [\n        {\n          subtask_id: subtaskId,\n          settings_list: {\n            setting_responses: [\n              {\n                key: 'user_identifier',\n                response_data: {\n                  text_data: { result: credentials.username },\n                },\n              },\n            ],\n            link: 'next_link',\n          },\n        },\n      ],\n    });\n  }\n\n  private async handleEnterPassword(\n    subtaskId: string,\n    _prev: TwitterUserAuthFlowResponse,\n    credentials: TwitterUserAuthCredentials,\n    api: FlowSubtaskHandlerApi,\n  ): Promise<FlowTokenResult> {\n    return await this.executeFlowTask({\n      flow_token: api.getFlowToken(),\n      subtask_inputs: [\n        {\n          subtask_id: subtaskId,\n          enter_password: {\n            password: credentials.password,\n            link: 'next_link',\n          },\n        },\n      ],\n    });\n  }\n\n  private async handleAccountDuplicationCheck(\n    subtaskId: string,\n    _prev: TwitterUserAuthFlowResponse,\n    _credentials: TwitterUserAuthCredentials,\n    api: FlowSubtaskHandlerApi,\n  ): Promise<FlowTokenResult> {\n    return await this.executeFlowTask({\n      flow_token: api.getFlowToken(),\n      subtask_inputs: [\n        {\n          subtask_id: subtaskId,\n          check_logged_in_account: {\n            link: 'AccountDuplicationCheck_false',\n          },\n        },\n      ],\n    });\n  }\n\n  private async handleTwoFactorAuthChallenge(\n    subtaskId: string,\n    _prev: TwitterUserAuthFlowResponse,\n    credentials: TwitterUserAuthCredentials,\n    api: FlowSubtaskHandlerApi,\n  ): Promise<FlowTokenResult> {\n    if (!credentials.twoFactorSecret) {\n      return {\n        status: 'error',\n        err: new AuthenticationError(\n          'Two-factor authentication is required but no secret was provided',\n        ),\n      };\n    }\n\n    const totp = new OTPAuth.TOTP({ secret: credentials.twoFactorSecret });\n    let error;\n    for (let attempts = 1; attempts < 4; attempts += 1) {\n      try {\n        return await api.sendFlowRequest({\n          flow_token: api.getFlowToken(),\n          subtask_inputs: [\n            {\n              subtask_id: subtaskId,\n              enter_text: {\n                link: 'next_link',\n                text: totp.generate(),\n              },\n            },\n          ],\n        });\n      } catch (err) {\n        error = err;\n        await new Promise((resolve) => setTimeout(resolve, 2000 * attempts));\n      }\n    }\n    throw error;\n  }\n\n  private async handleAcid(\n    subtaskId: string,\n    _prev: TwitterUserAuthFlowResponse,\n    credentials: TwitterUserAuthCredentials,\n    api: FlowSubtaskHandlerApi,\n  ): Promise<FlowTokenResult> {\n    return await this.executeFlowTask({\n      flow_token: api.getFlowToken(),\n      subtask_inputs: [\n        {\n          subtask_id: subtaskId,\n          enter_text: {\n            text: credentials.email,\n            link: 'next_link',\n          },\n        },\n      ],\n    });\n  }\n\n  private async handleSuccessSubtask(): Promise<FlowTokenResult> {\n    // Login completed successfully, nothing more to do\n    log('Successfully logged in with user credentials.');\n    return {\n      status: 'success',\n      response: {},\n    };\n  }\n\n  private async executeFlowTask(\n    data: TwitterUserAuthFlowRequest,\n  ): Promise<FlowTokenResult> {\n    let onboardingTaskUrl = 'https://api.x.com/1.1/onboarding/task.json';\n    if ('flow_name' in data) {\n      onboardingTaskUrl = `https://api.x.com/1.1/onboarding/task.json?flow_name=${data.flow_name}`;\n    }\n\n    log(`Making POST request to ${onboardingTaskUrl}`);\n    const headers = new Headers({\n      accept: '*/*',\n      'accept-language': 'en-US,en;q=0.9',\n      'content-type': 'application/json',\n      'cache-control': 'no-cache',\n      origin: 'https://x.com',\n      pragma: 'no-cache',\n      priority: 'u=1, i',\n      referer: 'https://x.com/',\n      'sec-ch-ua':\n        '\"Google Chrome\";v=\"135\", \"Not-A.Brand\";v=\"8\", \"Chromium\";v=\"135\"',\n      'sec-ch-ua-mobile': '?0',\n      'sec-ch-ua-platform': '\"Windows\"',\n      'sec-fetch-dest': 'empty',\n      'sec-fetch-mode': 'cors',\n      'sec-fetch-site': 'same-origin',\n      'user-agent':\n        'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36',\n      'x-twitter-auth-type': 'OAuth2Client',\n      'x-twitter-active-user': 'yes',\n      'x-twitter-client-language': 'en',\n    });\n    await this.installTo(headers, onboardingTaskUrl);\n\n    if (this.options?.experimental?.xClientTransactionId) {\n      const transactionId = await generateTransactionId(\n        onboardingTaskUrl,\n        this.fetch.bind(this),\n        'POST',\n      );\n      headers.set('x-client-transaction-id', transactionId);\n    }\n\n    let res: Response;\n    do {\n      const fetchParameters: FetchParameters = [\n        onboardingTaskUrl,\n        {\n          credentials: 'include',\n          method: 'POST',\n          headers: headers,\n          body: JSON.stringify(data),\n        },\n      ];\n\n      try {\n        res = await this.fetch(...fetchParameters);\n      } catch (err) {\n        if (!(err instanceof Error)) {\n          throw err;\n        }\n\n        return {\n          status: 'error',\n          err: err,\n        };\n      }\n\n      await updateCookieJar(this.jar, res.headers);\n\n      if (res.status === 429) {\n        log('Rate limit hit, waiting before retrying...');\n        await this.onRateLimit({\n          fetchParameters: fetchParameters,\n          response: res,\n        });\n      }\n    } while (res.status === 429);\n\n    if (!res.ok) {\n      return { status: 'error', err: await ApiError.fromResponse(res) };\n    }\n\n    const flow: TwitterUserAuthFlowResponse = await flexParseJson(res);\n    if (flow?.flow_token == null) {\n      return {\n        status: 'error',\n        err: new AuthenticationError('flow_token not found.'),\n      };\n    }\n\n    if (flow.errors?.length) {\n      return {\n        status: 'error',\n        err: new AuthenticationError(\n          `Authentication error (${flow.errors[0].code}): ${flow.errors[0].message}`,\n        ),\n      };\n    }\n\n    if (typeof flow.flow_token !== 'string') {\n      return {\n        status: 'error',\n        err: new AuthenticationError('flow_token was not a string.'),\n      };\n    }\n\n    const subtask = flow.subtasks?.length ? flow.subtasks[0] : undefined;\n    Check(TwitterUserAuthSubtask, subtask);\n\n    if (subtask && subtask.subtask_id === 'DenyLoginSubtask') {\n      return {\n        status: 'error',\n        err: new AuthenticationError('Authentication error: DenyLoginSubtask'),\n      };\n    }\n\n    return {\n      status: 'success',\n      response: flow,\n    };\n  }\n}\n","import stringify from 'json-stable-stringify';\n\n/**\n * Examples of requests to API endpoints. These are parsed at runtime and used\n * as templates for requests to a particular endpoint. Please ensure these do\n * not contain any information that you do not want published to NPM.\n */\nconst endpoints = {\n  UserTweets:\n    'https://x.com/i/api/graphql/oRJs8SLCRNRbQzuZG93_oA/UserTweets?variables=%7B%22userId%22%3A%221806359170830172162%22%2C%22count%22%3A20%2C%22includePromotedContent%22%3Atrue%2C%22withQuickPromoteEligibilityTweetFields%22%3Atrue%2C%22withVoice%22%3Atrue%7D&features=%7B%22rweb_video_screen_enabled%22%3Afalse%2C%22payments_enabled%22%3Afalse%2C%22profile_label_improvements_pcf_label_in_post_enabled%22%3Atrue%2C%22responsive_web_profile_redirect_enabled%22%3Afalse%2C%22rweb_tipjar_consumption_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22premium_content_api_read_enabled%22%3Afalse%2C%22communities_web_enable_tweet_community_results_fetch%22%3Atrue%2C%22c9s_tweet_anatomy_moderator_badge_enabled%22%3Atrue%2C%22responsive_web_grok_analyze_button_fetch_trends_enabled%22%3Afalse%2C%22responsive_web_grok_analyze_post_followups_enabled%22%3Atrue%2C%22responsive_web_jetfuel_frame%22%3Atrue%2C%22responsive_web_grok_share_attachment_enabled%22%3Atrue%2C%22articles_preview_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Atrue%2C%22responsive_web_twitter_article_tweet_consumption_enabled%22%3Atrue%2C%22tweet_awards_web_tipping_enabled%22%3Afalse%2C%22responsive_web_grok_show_grok_translated_post%22%3Atrue%2C%22responsive_web_grok_analysis_button_from_backend%22%3Atrue%2C%22creator_subscriptions_quote_tweet_preview_enabled%22%3Afalse%2C%22freedom_of_speech_not_reach_fetch_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Atrue%2C%22longform_notetweets_rich_text_read_enabled%22%3Atrue%2C%22longform_notetweets_inline_media_enabled%22%3Atrue%2C%22responsive_web_grok_image_annotation_enabled%22%3Atrue%2C%22responsive_web_grok_imagine_annotation_enabled%22%3Atrue%2C%22responsive_web_grok_community_note_auto_translation_is_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D&fieldToggles=%7B%22withArticlePlainText%22%3Afalse%7D',\n  UserTweetsAndReplies:\n    'https://x.com/i/api/graphql/Hk4KlJ-ONjlJsucqR55P7g/UserTweetsAndReplies?variables=%7B%22userId%22%3A%221806359170830172162%22%2C%22count%22%3A20%2C%22includePromotedContent%22%3Atrue%2C%22withCommunity%22%3Atrue%2C%22withVoice%22%3Atrue%7D&features=%7B%22rweb_video_screen_enabled%22%3Afalse%2C%22profile_label_improvements_pcf_label_in_post_enabled%22%3Atrue%2C%22rweb_tipjar_consumption_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22premium_content_api_read_enabled%22%3Afalse%2C%22communities_web_enable_tweet_community_results_fetch%22%3Atrue%2C%22c9s_tweet_anatomy_moderator_badge_enabled%22%3Atrue%2C%22responsive_web_grok_analyze_button_fetch_trends_enabled%22%3Afalse%2C%22responsive_web_grok_analyze_post_followups_enabled%22%3Atrue%2C%22responsive_web_jetfuel_frame%22%3Afalse%2C%22responsive_web_grok_share_attachment_enabled%22%3Atrue%2C%22articles_preview_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Atrue%2C%22responsive_web_twitter_article_tweet_consumption_enabled%22%3Atrue%2C%22tweet_awards_web_tipping_enabled%22%3Afalse%2C%22responsive_web_grok_show_grok_translated_post%22%3Afalse%2C%22responsive_web_grok_analysis_button_from_backend%22%3Atrue%2C%22creator_subscriptions_quote_tweet_preview_enabled%22%3Afalse%2C%22freedom_of_speech_not_reach_fetch_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Atrue%2C%22longform_notetweets_rich_text_read_enabled%22%3Atrue%2C%22longform_notetweets_inline_media_enabled%22%3Atrue%2C%22responsive_web_grok_image_annotation_enabled%22%3Atrue%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D&fieldToggles=%7B%22withArticlePlainText%22%3Afalse%7D',\n  UserLikedTweets:\n    'https://x.com/i/api/graphql/XHTMjDbiTGLQ9cP1em-aqQ/Likes?variables=%7B%22userId%22%3A%222244196397%22%2C%22count%22%3A20%2C%22includePromotedContent%22%3Afalse%2C%22withClientEventToken%22%3Afalse%2C%22withBirdwatchNotes%22%3Afalse%2C%22withVoice%22%3Atrue%7D&features=%7B%22rweb_video_screen_enabled%22%3Afalse%2C%22profile_label_improvements_pcf_label_in_post_enabled%22%3Atrue%2C%22rweb_tipjar_consumption_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22premium_content_api_read_enabled%22%3Afalse%2C%22communities_web_enable_tweet_community_results_fetch%22%3Atrue%2C%22c9s_tweet_anatomy_moderator_badge_enabled%22%3Atrue%2C%22responsive_web_grok_analyze_button_fetch_trends_enabled%22%3Afalse%2C%22responsive_web_grok_analyze_post_followups_enabled%22%3Atrue%2C%22responsive_web_jetfuel_frame%22%3Afalse%2C%22responsive_web_grok_share_attachment_enabled%22%3Atrue%2C%22articles_preview_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Atrue%2C%22responsive_web_twitter_article_tweet_consumption_enabled%22%3Atrue%2C%22tweet_awards_web_tipping_enabled%22%3Afalse%2C%22responsive_web_grok_show_grok_translated_post%22%3Afalse%2C%22responsive_web_grok_analysis_button_from_backend%22%3Atrue%2C%22creator_subscriptions_quote_tweet_preview_enabled%22%3Afalse%2C%22freedom_of_speech_not_reach_fetch_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Atrue%2C%22longform_notetweets_rich_text_read_enabled%22%3Atrue%2C%22longform_notetweets_inline_media_enabled%22%3Atrue%2C%22responsive_web_grok_image_annotation_enabled%22%3Atrue%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D&fieldToggles=%7B%22withArticlePlainText%22%3Afalse%7D',\n  UserByScreenName:\n    'https://api.x.com/graphql/-oaLodhGbbnzJBACb1kk2Q/UserByScreenName?variables=%7B%22screen_name%22%3A%22geminiapp%22%2C%22withGrokTranslatedBio%22%3Afalse%7D&features=%7B%22hidden_profile_subscriptions_enabled%22%3Atrue%2C%22profile_label_improvements_pcf_label_in_post_enabled%22%3Atrue%2C%22responsive_web_profile_redirect_enabled%22%3Afalse%2C%22rweb_tipjar_consumption_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22subscriptions_verification_info_is_identity_verified_enabled%22%3Atrue%2C%22subscriptions_verification_info_verified_since_enabled%22%3Atrue%2C%22highlights_tweets_tab_ui_enabled%22%3Atrue%2C%22responsive_web_twitter_article_notes_tab_enabled%22%3Atrue%2C%22subscriptions_feature_can_gift_premium%22%3Atrue%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%7D&fieldToggles=%7B%22withPayments%22%3Afalse%2C%22withAuxiliaryUserLabels%22%3Atrue%7D',\n  TweetDetail:\n    'https://x.com/i/api/graphql/YVyS4SfwYW7Uw5qwy0mQCA/TweetDetail?variables=%7B%22focalTweetId%22%3A%221985465713096794294%22%2C%22referrer%22%3A%22profile%22%2C%22with_rux_injections%22%3Afalse%2C%22rankingMode%22%3A%22Relevance%22%2C%22includePromotedContent%22%3Atrue%2C%22withCommunity%22%3Atrue%2C%22withQuickPromoteEligibilityTweetFields%22%3Atrue%2C%22withBirdwatchNotes%22%3Atrue%2C%22withVoice%22%3Atrue%7D&features=%7B%22rweb_video_screen_enabled%22%3Afalse%2C%22payments_enabled%22%3Afalse%2C%22profile_label_improvements_pcf_label_in_post_enabled%22%3Atrue%2C%22responsive_web_profile_redirect_enabled%22%3Afalse%2C%22rweb_tipjar_consumption_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22premium_content_api_read_enabled%22%3Afalse%2C%22communities_web_enable_tweet_community_results_fetch%22%3Atrue%2C%22c9s_tweet_anatomy_moderator_badge_enabled%22%3Atrue%2C%22responsive_web_grok_analyze_button_fetch_trends_enabled%22%3Afalse%2C%22responsive_web_grok_analyze_post_followups_enabled%22%3Atrue%2C%22responsive_web_jetfuel_frame%22%3Atrue%2C%22responsive_web_grok_share_attachment_enabled%22%3Atrue%2C%22articles_preview_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Atrue%2C%22responsive_web_twitter_article_tweet_consumption_enabled%22%3Atrue%2C%22tweet_awards_web_tipping_enabled%22%3Afalse%2C%22responsive_web_grok_show_grok_translated_post%22%3Atrue%2C%22responsive_web_grok_analysis_button_from_backend%22%3Atrue%2C%22creator_subscriptions_quote_tweet_preview_enabled%22%3Afalse%2C%22freedom_of_speech_not_reach_fetch_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Atrue%2C%22longform_notetweets_rich_text_read_enabled%22%3Atrue%2C%22longform_notetweets_inline_media_enabled%22%3Atrue%2C%22responsive_web_grok_image_annotation_enabled%22%3Atrue%2C%22responsive_web_grok_imagine_annotation_enabled%22%3Atrue%2C%22responsive_web_grok_community_note_auto_translation_is_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D&fieldToggles=%7B%22withArticleRichContentState%22%3Atrue%2C%22withArticlePlainText%22%3Afalse%2C%22withGrokAnalyze%22%3Afalse%2C%22withDisallowedReplyControls%22%3Afalse%7D',\n  TweetResultByRestId:\n    'https://api.x.com/graphql/tCVRZ3WCvoj0BVO7BKnL-Q/TweetResultByRestId?variables=%7B%22tweetId%22%3A%221985465713096794294%22%2C%22withCommunity%22%3Afalse%2C%22includePromotedContent%22%3Afalse%2C%22withVoice%22%3Afalse%7D&features=%7B%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22premium_content_api_read_enabled%22%3Afalse%2C%22communities_web_enable_tweet_community_results_fetch%22%3Atrue%2C%22c9s_tweet_anatomy_moderator_badge_enabled%22%3Atrue%2C%22responsive_web_grok_analyze_button_fetch_trends_enabled%22%3Afalse%2C%22responsive_web_grok_analyze_post_followups_enabled%22%3Afalse%2C%22responsive_web_jetfuel_frame%22%3Atrue%2C%22responsive_web_grok_share_attachment_enabled%22%3Atrue%2C%22articles_preview_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Atrue%2C%22responsive_web_twitter_article_tweet_consumption_enabled%22%3Atrue%2C%22tweet_awards_web_tipping_enabled%22%3Afalse%2C%22responsive_web_grok_show_grok_translated_post%22%3Afalse%2C%22responsive_web_grok_analysis_button_from_backend%22%3Atrue%2C%22creator_subscriptions_quote_tweet_preview_enabled%22%3Afalse%2C%22freedom_of_speech_not_reach_fetch_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Atrue%2C%22longform_notetweets_rich_text_read_enabled%22%3Atrue%2C%22longform_notetweets_inline_media_enabled%22%3Atrue%2C%22payments_enabled%22%3Afalse%2C%22profile_label_improvements_pcf_label_in_post_enabled%22%3Atrue%2C%22responsive_web_profile_redirect_enabled%22%3Afalse%2C%22rweb_tipjar_consumption_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22responsive_web_grok_image_annotation_enabled%22%3Atrue%2C%22responsive_web_grok_imagine_annotation_enabled%22%3Atrue%2C%22responsive_web_grok_community_note_auto_translation_is_enabled%22%3Afalse%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D&fieldToggles=%7B%22withArticleRichContentState%22%3Atrue%2C%22withArticlePlainText%22%3Afalse%2C%22withGrokAnalyze%22%3Afalse%2C%22withDisallowedReplyControls%22%3Afalse%7D',\n  ListTweets:\n    'https://x.com/i/api/graphql/S1Sm3_mNJwa-fnY9htcaAQ/ListLatestTweetsTimeline?variables=%7B%22listId%22%3A%221736495155002106192%22%2C%22count%22%3A20%7D&features=%7B%22rweb_video_screen_enabled%22%3Afalse%2C%22profile_label_improvements_pcf_label_in_post_enabled%22%3Atrue%2C%22rweb_tipjar_consumption_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22premium_content_api_read_enabled%22%3Afalse%2C%22communities_web_enable_tweet_community_results_fetch%22%3Atrue%2C%22c9s_tweet_anatomy_moderator_badge_enabled%22%3Atrue%2C%22responsive_web_grok_analyze_button_fetch_trends_enabled%22%3Afalse%2C%22responsive_web_grok_analyze_post_followups_enabled%22%3Atrue%2C%22responsive_web_jetfuel_frame%22%3Afalse%2C%22responsive_web_grok_share_attachment_enabled%22%3Atrue%2C%22articles_preview_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Atrue%2C%22responsive_web_twitter_article_tweet_consumption_enabled%22%3Atrue%2C%22tweet_awards_web_tipping_enabled%22%3Afalse%2C%22responsive_web_grok_show_grok_translated_post%22%3Afalse%2C%22responsive_web_grok_analysis_button_from_backend%22%3Atrue%2C%22creator_subscriptions_quote_tweet_preview_enabled%22%3Afalse%2C%22freedom_of_speech_not_reach_fetch_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Atrue%2C%22longform_notetweets_rich_text_read_enabled%22%3Atrue%2C%22longform_notetweets_inline_media_enabled%22%3Atrue%2C%22responsive_web_grok_image_annotation_enabled%22%3Atrue%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D',\n  SearchTimeline:\n    'https://x.com/i/api/graphql/bshMIjqDk8LTXTq4w91WKw/SearchTimeline?variables=%7B%22rawQuery%22%3A%22twitter%22%2C%22count%22%3A20%2C%22querySource%22%3A%22typed_query%22%2C%22product%22%3A%22Top%22%2C%22withGrokTranslatedBio%22%3Afalse%7D&features=%7B%22rweb_video_screen_enabled%22%3Afalse%2C%22profile_label_improvements_pcf_label_in_post_enabled%22%3Atrue%2C%22responsive_web_profile_redirect_enabled%22%3Afalse%2C%22rweb_tipjar_consumption_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22premium_content_api_read_enabled%22%3Afalse%2C%22communities_web_enable_tweet_community_results_fetch%22%3Atrue%2C%22c9s_tweet_anatomy_moderator_badge_enabled%22%3Atrue%2C%22responsive_web_grok_analyze_button_fetch_trends_enabled%22%3Afalse%2C%22responsive_web_grok_analyze_post_followups_enabled%22%3Atrue%2C%22responsive_web_jetfuel_frame%22%3Atrue%2C%22responsive_web_grok_share_attachment_enabled%22%3Atrue%2C%22articles_preview_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Atrue%2C%22responsive_web_twitter_article_tweet_consumption_enabled%22%3Atrue%2C%22tweet_awards_web_tipping_enabled%22%3Afalse%2C%22responsive_web_grok_show_grok_translated_post%22%3Atrue%2C%22responsive_web_grok_analysis_button_from_backend%22%3Atrue%2C%22creator_subscriptions_quote_tweet_preview_enabled%22%3Afalse%2C%22freedom_of_speech_not_reach_fetch_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Atrue%2C%22longform_notetweets_rich_text_read_enabled%22%3Atrue%2C%22longform_notetweets_inline_media_enabled%22%3Atrue%2C%22responsive_web_grok_image_annotation_enabled%22%3Atrue%2C%22responsive_web_grok_imagine_annotation_enabled%22%3Atrue%2C%22responsive_web_grok_community_note_auto_translation_is_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D',\n  Followers:\n    'https://x.com/i/api/graphql/SCu9fVIlCUm-BM8-tL5pkQ/Followers?variables=%7B%22userId%22%3A%221806359170830172162%22%2C%22count%22%3A20%2C%22includePromotedContent%22%3Afalse%2C%22withGrokTranslatedBio%22%3Afalse%7D&features=%7B%22rweb_video_screen_enabled%22%3Afalse%2C%22profile_label_improvements_pcf_label_in_post_enabled%22%3Atrue%2C%22responsive_web_profile_redirect_enabled%22%3Afalse%2C%22rweb_tipjar_consumption_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22premium_content_api_read_enabled%22%3Afalse%2C%22communities_web_enable_tweet_community_results_fetch%22%3Atrue%2C%22c9s_tweet_anatomy_moderator_badge_enabled%22%3Atrue%2C%22responsive_web_grok_analyze_button_fetch_trends_enabled%22%3Afalse%2C%22responsive_web_grok_analyze_post_followups_enabled%22%3Atrue%2C%22responsive_web_jetfuel_frame%22%3Atrue%2C%22responsive_web_grok_share_attachment_enabled%22%3Atrue%2C%22articles_preview_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Atrue%2C%22responsive_web_twitter_article_tweet_consumption_enabled%22%3Atrue%2C%22tweet_awards_web_tipping_enabled%22%3Afalse%2C%22responsive_web_grok_show_grok_translated_post%22%3Atrue%2C%22responsive_web_grok_analysis_button_from_backend%22%3Atrue%2C%22creator_subscriptions_quote_tweet_preview_enabled%22%3Afalse%2C%22freedom_of_speech_not_reach_fetch_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Atrue%2C%22longform_notetweets_rich_text_read_enabled%22%3Atrue%2C%22longform_notetweets_inline_media_enabled%22%3Atrue%2C%22responsive_web_grok_image_annotation_enabled%22%3Atrue%2C%22responsive_web_grok_imagine_annotation_enabled%22%3Atrue%2C%22responsive_web_grok_community_note_auto_translation_is_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D',\n  Following:\n    'https://x.com/i/api/graphql/S5xUN9s2v4xk50KWGGvyvQ/Following?variables=%7B%22userId%22%3A%221806359170830172162%22%2C%22count%22%3A20%2C%22includePromotedContent%22%3Afalse%2C%22withGrokTranslatedBio%22%3Afalse%7D&features=%7B%22rweb_video_screen_enabled%22%3Afalse%2C%22profile_label_improvements_pcf_label_in_post_enabled%22%3Atrue%2C%22responsive_web_profile_redirect_enabled%22%3Afalse%2C%22rweb_tipjar_consumption_enabled%22%3Atrue%2C%22verified_phone_label_enabled%22%3Afalse%2C%22creator_subscriptions_tweet_preview_api_enabled%22%3Atrue%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Atrue%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22premium_content_api_read_enabled%22%3Afalse%2C%22communities_web_enable_tweet_community_results_fetch%22%3Atrue%2C%22c9s_tweet_anatomy_moderator_badge_enabled%22%3Atrue%2C%22responsive_web_grok_analyze_button_fetch_trends_enabled%22%3Afalse%2C%22responsive_web_grok_analyze_post_followups_enabled%22%3Atrue%2C%22responsive_web_jetfuel_frame%22%3Atrue%2C%22responsive_web_grok_share_attachment_enabled%22%3Atrue%2C%22articles_preview_enabled%22%3Atrue%2C%22responsive_web_edit_tweet_api_enabled%22%3Atrue%2C%22graphql_is_translatable_rweb_tweet_is_translatable_enabled%22%3Atrue%2C%22view_counts_everywhere_api_enabled%22%3Atrue%2C%22longform_notetweets_consumption_enabled%22%3Atrue%2C%22responsive_web_twitter_article_tweet_consumption_enabled%22%3Atrue%2C%22tweet_awards_web_tipping_enabled%22%3Afalse%2C%22responsive_web_grok_show_grok_translated_post%22%3Atrue%2C%22responsive_web_grok_analysis_button_from_backend%22%3Atrue%2C%22creator_subscriptions_quote_tweet_preview_enabled%22%3Afalse%2C%22freedom_of_speech_not_reach_fetch_enabled%22%3Atrue%2C%22standardized_nudges_misinfo%22%3Atrue%2C%22tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled%22%3Atrue%2C%22longform_notetweets_rich_text_read_enabled%22%3Atrue%2C%22longform_notetweets_inline_media_enabled%22%3Atrue%2C%22responsive_web_grok_image_annotation_enabled%22%3Atrue%2C%22responsive_web_grok_imagine_annotation_enabled%22%3Atrue%2C%22responsive_web_grok_community_note_auto_translation_is_enabled%22%3Afalse%2C%22responsive_web_enhance_cards_enabled%22%3Afalse%7D',\n} as const;\n\nexport interface EndpointFieldInfo {\n  /**\n   * Request variables, used for providing arguments such as user IDs or result counts.\n   */\n  variables: Record<string, unknown>;\n\n  /**\n   * Request features, used for encoding feature flags into the request. These may either be\n   * boolean values or numerically-encoded booleans (1 or 0). It is possible this may change\n   * to include other representations of booleans as Twitter's backend evolves.\n   */\n  features: Record<string, unknown>;\n\n  /**\n   * Request field toggles, used for limiting how returned fields are represented. This is\n   * rarely used.\n   */\n  fieldToggles: Record<string, unknown>;\n}\n\ntype SomePartial<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;\n\ntype EndpointVersion = string;\ntype EndpointName = string;\ntype EncodedVariables = string;\ntype EncodedFeatures = string;\ntype EncodedFieldToggles = string;\n\n// TODO: Set up field-level Intellisense for the QraphQL parameters in these?\ntype BaseUrl =\n  | 'https://twitter.com/i/api/graphql'\n  | 'https://x.com/i/api/graphql'\n  | 'https://api.x.com/graphql';\ntype EndpointFields<EndpointUrl> =\n  EndpointUrl extends `${BaseUrl}/${EndpointVersion}/${EndpointName}?variables=${EncodedVariables}&features=${EncodedFeatures}&fieldToggles=${EncodedFieldToggles}`\n    ? EndpointFieldInfo\n    : EndpointUrl extends `${BaseUrl}/${EndpointVersion}/${EndpointName}?variables=${EncodedVariables}&features=${EncodedFeatures}`\n    ? SomePartial<EndpointFieldInfo, 'fieldToggles'>\n    : EndpointUrl extends `${BaseUrl}/${EndpointVersion}/${EndpointName}?variables=${EncodedVariables}`\n    ? SomePartial<EndpointFieldInfo, 'features' | 'fieldToggles'>\n    : Partial<EndpointFieldInfo>;\n\nexport type ApiRequestInfo<EndpointUrl> = EndpointFields<EndpointUrl> & {\n  /**\n   * The URL, without any GraphQL query parameters.\n   */\n  url: string;\n\n  /**\n   * Converts the request back into a URL to be sent to the Twitter API.\n   */\n  toRequestUrl(): string;\n};\n\n/** Wrapper class for API request information. */\nclass ApiRequest<EndpointUrl> {\n  url: string;\n  variables?: Record<string, unknown> | undefined;\n  features?: Record<string, unknown> | undefined;\n  fieldToggles?: Record<string, unknown> | undefined;\n\n  constructor(info: Omit<ApiRequestInfo<EndpointUrl>, 'toRequestUrl'>) {\n    this.url = info.url;\n    this.variables = info.variables;\n    this.features = info.features;\n    this.fieldToggles = info.fieldToggles;\n  }\n\n  toRequestUrl(): string {\n    const params = new URLSearchParams();\n\n    // Only include query parameters with values\n    if (this.variables) {\n      // Stringify with the query keys in sorted order like the Go package\n      const variablesStr = stringify(this.variables);\n      if (variablesStr) params.set('variables', variablesStr);\n    }\n\n    if (this.features) {\n      const featuresStr = stringify(this.features);\n      if (featuresStr) params.set('features', featuresStr);\n    }\n\n    if (this.fieldToggles) {\n      const fieldTogglesStr = stringify(this.fieldToggles);\n      if (fieldTogglesStr) params.set('fieldToggles', fieldTogglesStr);\n    }\n\n    return `${this.url}?${params.toString()}`;\n  }\n}\n\n/**\n * Parses information from a Twitter API endpoint using an example request\n * URL against that endpoint. This can be used to extract GraphQL parameters\n * in order to easily reuse and/or override them later.\n * @param example An example of the endpoint to analyze.\n * @returns The parsed endpoint information.\n */\nfunction parseEndpointExample<\n  Endpoints,\n  Endpoint extends string & keyof Endpoints,\n>(example: Endpoint): ApiRequestInfo<Endpoints[Endpoint]> {\n  const { protocol, host, pathname, searchParams: query } = new URL(example);\n\n  const base = `${protocol}//${host}${pathname}`;\n  const variables = query.get('variables');\n  const features = query.get('features');\n  const fieldToggles = query.get('fieldToggles');\n\n  return new ApiRequest<Endpoints[Endpoint]>({\n    url: base,\n    variables: variables ? JSON.parse(variables) : undefined,\n    features: features ? JSON.parse(features) : undefined,\n    fieldToggles: fieldToggles ? JSON.parse(fieldToggles) : undefined,\n  } as Omit<ApiRequestInfo<Endpoints[Endpoint]>, 'toRequestUrl'>) as ApiRequestInfo<\n    Endpoints[Endpoint]\n  >;\n}\n\ntype ApiRequestFactory<Endpoints> = {\n  [Endpoint in keyof Endpoints as `create${string &\n    Endpoint}Request`]: () => ApiRequestInfo<Endpoints[Endpoint]>;\n};\n\nfunction createApiRequestFactory<Endpoints extends Record<string, string>>(\n  endpoints: Endpoints,\n): ApiRequestFactory<Endpoints> {\n  type UntypedApiRequestFactory = ApiRequestFactory<Record<string, string>>;\n\n  return Object.entries(endpoints)\n    .map<UntypedApiRequestFactory>(([endpointName, endpointExample]) => {\n      // Create a partial factory for only one endpoint\n      return {\n        [`create${endpointName}Request`]: () => {\n          // Create a new instance on each invocation so that we can safely\n          // mutate requests before sending them off\n          return parseEndpointExample<Endpoints, any>(endpointExample);\n        },\n      };\n    })\n    .reduce((agg, next) => {\n      // Merge all of our factories into one that includes every endpoint\n      return Object.assign(agg, next);\n    }) as ApiRequestFactory<Endpoints>;\n}\n\nexport const apiRequestFactory = createApiRequestFactory(endpoints);\n","import { requestApi, RequestApiResult, bearerToken2 } from './api';\nimport { TwitterAuth } from './auth';\nimport { TwitterApiErrorRaw } from './errors';\nimport { apiRequestFactory } from './api-data';\n\nexport interface CoreUserRaw {\n  created_at?: string;\n  name?: string;\n  screen_name?: string;\n}\n\nexport interface LegacyUserRaw {\n  created_at?: string;\n  description?: string;\n  entities?: {\n    url?: {\n      urls?: {\n        url?: string;\n        expanded_url?: string;\n        display_url?: string;\n        indices?: [number, number];\n      }[];\n    };\n    description?: {\n      // TODO: Get the proper type of this.\n      urls?: any[];\n    };\n  };\n  favourites_count?: number;\n  followers_count?: number;\n  friends_count?: number;\n  media_count?: number;\n  statuses_count?: number;\n  id_str?: string;\n  listed_count?: number;\n  name?: string;\n  location?: string;\n  geo_enabled?: boolean;\n  pinned_tweet_ids_str?: string[];\n  profile_background_color?: string;\n  profile_banner_url?: string;\n  profile_image_url_https?: string;\n  protected?: boolean;\n  screen_name?: string;\n  verified?: boolean;\n  has_custom_timelines?: boolean;\n  has_extended_profile?: boolean;\n  url?: string;\n  can_dm?: boolean;\n  id?: number;\n  // TODO: Get the proper type of this.\n  utc_offset?: any;\n  // TODO: Get the proper type of this.\n  time_zone?: any;\n  // TODO: Get the proper type of this.\n  lang?: any;\n  contributors_enabled?: boolean;\n  is_translator?: boolean;\n  is_translation_enabled?: boolean;\n  profile_background_image_url?: string;\n  profile_background_image_url_https?: string;\n  profile_background_tile?: boolean;\n  profile_image_url?: string;\n  profile_link_color?: string;\n  profile_sidebar_border_color?: string;\n  profile_sidebar_fill_color?: string;\n  profile_text_color?: string;\n  profile_use_background_image?: boolean;\n  default_profile?: boolean;\n  default_profile_image?: boolean;\n  can_secret_dm?: boolean;\n  can_media_tag?: boolean;\n  following?: boolean;\n  follow_request_sent?: boolean;\n  notifications?: boolean;\n  blocking?: boolean;\n  subscribed_by?: boolean;\n  blocked_by?: boolean;\n  want_retweets?: boolean;\n  dm_blocked_by?: boolean;\n  dm_blocking?: boolean;\n  business_profile_state?: string;\n  translator_type?: string;\n  // TODO: Get the proper type of this.\n  withheld_in_countries?: any[];\n  followed_by?: boolean;\n}\n\n/**\n * A parsed profile object.\n */\nexport interface Profile {\n  avatar?: string;\n  banner?: string;\n  biography?: string;\n  birthday?: string;\n  followersCount?: number;\n  followingCount?: number;\n  friendsCount?: number;\n  mediaCount?: number;\n  statusesCount?: number;\n  isPrivate?: boolean;\n  isVerified?: boolean;\n  isBlueVerified?: boolean;\n  joined?: Date;\n  likesCount?: number;\n  listedCount?: number;\n  location?: string;\n  name?: string;\n  pinnedTweetIds?: string[];\n  tweetsCount?: number;\n  url?: string;\n  userId?: string;\n  username?: string;\n  website?: string;\n  canDm?: boolean;\n}\n\nexport interface UserRaw {\n  data: {\n    user: {\n      result: {\n        __typename?: string;\n        message?: string;\n        reason?: string;\n        rest_id?: string;\n        is_blue_verified?: boolean;\n        legacy: LegacyUserRaw;\n        core?: CoreUserRaw;\n        avatar?: {\n          image_url?: string;\n        };\n        location?: {\n          location?: string;\n        };\n      };\n    };\n  };\n  errors?: TwitterApiErrorRaw[];\n}\n\nfunction getAvatarOriginalSizeUrl(avatarUrl: string | undefined) {\n  return avatarUrl ? avatarUrl.replace('_normal', '') : undefined;\n}\n\nexport function parseProfile(\n  legacy: LegacyUserRaw,\n  isBlueVerified?: boolean,\n): Profile {\n  const profile: Profile = {\n    avatar: getAvatarOriginalSizeUrl(legacy.profile_image_url_https),\n    banner: legacy.profile_banner_url,\n    biography: legacy.description,\n    followersCount: legacy.followers_count,\n    followingCount: legacy.friends_count,\n    friendsCount: legacy.friends_count,\n    mediaCount: legacy.media_count,\n    isPrivate: legacy.protected ?? false,\n    isVerified: legacy.verified,\n    likesCount: legacy.favourites_count,\n    listedCount: legacy.listed_count,\n    location: legacy.location,\n    name: legacy.name,\n    pinnedTweetIds: legacy.pinned_tweet_ids_str,\n    tweetsCount: legacy.statuses_count,\n    url: `https://x.com/${legacy.screen_name}`,\n    userId: legacy.id_str,\n    username: legacy.screen_name,\n    isBlueVerified: isBlueVerified ?? false,\n    canDm: legacy.can_dm,\n  };\n\n  if (legacy.created_at != null) {\n    profile.joined = new Date(Date.parse(legacy.created_at));\n  }\n\n  const urls = legacy.entities?.url?.urls;\n  if (urls?.length != null && urls?.length > 0) {\n    profile.website = urls[0].expanded_url;\n  }\n\n  return profile;\n}\n\nexport async function getProfile(\n  username: string,\n  auth: TwitterAuth,\n): Promise<RequestApiResult<Profile>> {\n  const request = apiRequestFactory.createUserByScreenNameRequest();\n  request.variables.screen_name = username;\n\n  // Use bearerToken2 for UserByScreenName endpoint\n  const res = await requestApi<UserRaw>(\n    request.toRequestUrl(),\n    auth,\n    'GET',\n    undefined,\n    undefined,\n    bearerToken2,\n  );\n  if (!res.success) {\n    return res;\n  }\n\n  const { value } = res;\n  const { errors } = value;\n  if (\n    (!value.data || !value.data.user || !value.data.user.result) &&\n    errors != null &&\n    errors.length > 0\n  ) {\n    return {\n      success: false,\n      err: new Error(errors.map((e) => e.message).join('\\n')),\n    };\n  }\n\n  if (!value.data || !value.data.user || !value.data.user.result) {\n    return {\n      success: false,\n      err: new Error('User not found.'),\n    };\n  }\n  const { result: user } = value.data.user;\n  const { legacy } = user;\n\n  if (user.__typename === 'UserUnavailable' && user?.reason === 'Suspended') {\n    return {\n      success: false,\n      err: new Error('User is suspended.'),\n    };\n  }\n\n  if (user.rest_id == null || user.rest_id.length === 0) {\n    return {\n      success: false,\n      err: new Error('rest_id not found.'),\n    };\n  }\n\n  legacy.id_str = user.rest_id;\n  legacy.screen_name ??= user.core?.screen_name;\n  legacy.profile_image_url_https ??= user.avatar?.image_url;\n  legacy.created_at ??= user.core?.created_at;\n  legacy.location ??= user.location?.location;\n  legacy.name ??= user.core?.name;\n\n  if (legacy.screen_name == null || legacy.screen_name.length === 0) {\n    return {\n      success: false,\n      err: new Error(`User ${username} does not exist or is private.`),\n    };\n  }\n\n  return {\n    success: true,\n    value: parseProfile(legacy, user.is_blue_verified),\n  };\n}\n\nconst idCache = new Map<string, string>();\n\nexport async function getUserIdByScreenName(\n  screenName: string,\n  auth: TwitterAuth,\n): Promise<RequestApiResult<string>> {\n  const cached = idCache.get(screenName);\n  if (cached != null) {\n    return { success: true, value: cached };\n  }\n\n  const profileRes = await getProfile(screenName, auth);\n  if (!profileRes.success) {\n    return profileRes;\n  }\n\n  const profile = profileRes.value;\n  if (profile.userId != null) {\n    idCache.set(screenName, profile.userId);\n\n    return {\n      success: true,\n      value: profile.userId,\n    };\n  }\n\n  return {\n    success: false,\n    err: new Error('User ID is undefined.'),\n  };\n}\n","import { jitter } from './api';\nimport { Profile } from './profile';\nimport { Tweet } from './tweets';\n\nexport interface FetchProfilesResponse {\n  profiles: Profile[];\n  next?: string;\n}\n\nexport type FetchProfiles = (\n  query: string,\n  maxProfiles: number,\n  cursor: string | undefined,\n) => Promise<FetchProfilesResponse>;\n\nexport interface FetchTweetsResponse {\n  tweets: Tweet[];\n  next?: string;\n}\n\nexport type FetchTweets = (\n  query: string,\n  maxTweets: number,\n  cursor: string | undefined,\n) => Promise<FetchTweetsResponse>;\n\nexport async function* getUserTimeline(\n  query: string,\n  maxProfiles: number,\n  fetchFunc: FetchProfiles,\n): AsyncGenerator<Profile, void> {\n  let nProfiles = 0;\n  let cursor: string | undefined = undefined;\n  let consecutiveEmptyBatches = 0;\n  while (nProfiles < maxProfiles) {\n    const batch: FetchProfilesResponse = await fetchFunc(\n      query,\n      maxProfiles,\n      cursor,\n    );\n\n    const { profiles, next } = batch;\n    cursor = next;\n\n    if (profiles.length === 0) {\n      consecutiveEmptyBatches++;\n      if (consecutiveEmptyBatches > 5) break;\n    } else consecutiveEmptyBatches = 0;\n\n    for (const profile of profiles) {\n      if (nProfiles < maxProfiles) yield profile;\n      else break;\n      nProfiles++;\n    }\n\n    if (!next) break;\n\n    await jitter(1000);\n  }\n}\n\nexport async function* getTweetTimeline(\n  query: string,\n  maxTweets: number,\n  fetchFunc: FetchTweets,\n): AsyncGenerator<Tweet, void> {\n  let nTweets = 0;\n  let cursor: string | undefined = undefined;\n  while (nTweets < maxTweets) {\n    const batch: FetchTweetsResponse = await fetchFunc(\n      query,\n      maxTweets,\n      cursor,\n    );\n\n    const { tweets, next } = batch;\n\n    if (tweets.length === 0) {\n      break;\n    }\n\n    for (const tweet of tweets) {\n      if (nTweets < maxTweets) {\n        cursor = next;\n        yield tweet;\n      } else {\n        break;\n      }\n\n      nTweets++;\n    }\n\n    await jitter(1000);\n  }\n}\n","export type NonNullableField<T, K extends keyof T> = {\n  [P in K]-?: T[P];\n} & T;\n\nexport function isFieldDefined<T, K extends keyof T>(key: K) {\n  return function (value: T): value is NonNullableField<T, K> {\n    return isDefined(value[key]);\n  };\n}\n\nexport function isDefined<T>(value: T | null | undefined): value is T {\n  return value != null;\n}\n","import { LegacyTweetRaw, TimelineMediaExtendedRaw } from './timeline-v1';\nimport { Photo, Video } from './tweets';\nimport { isFieldDefined, NonNullableField } from './type-util';\n\nconst reHashtag = /\\B(\\#\\S+\\b)/g;\nconst reCashtag = /\\B(\\$\\S+\\b)/g;\nconst reTwitterUrl = /https:(\\/\\/t\\.co\\/([A-Za-z0-9]|[A-Za-z]){10})/g;\nconst reUsername = /\\B(\\@\\S{1,15}\\b)/g;\n\nexport function parseMediaGroups(media: TimelineMediaExtendedRaw[]): {\n  sensitiveContent?: boolean;\n  photos: Photo[];\n  videos: Video[];\n} {\n  const photos: Photo[] = [];\n  const videos: Video[] = [];\n  let sensitiveContent: boolean | undefined = undefined;\n\n  for (const m of media\n    .filter(isFieldDefined('id_str'))\n    .filter(isFieldDefined('media_url_https'))) {\n    if (m.type === 'photo') {\n      photos.push({\n        id: m.id_str,\n        url: m.media_url_https,\n        alt_text: m.ext_alt_text,\n      });\n    } else if (m.type === 'video') {\n      videos.push(parseVideo(m));\n    } else if (m.type === 'animated_gif') {\n      videos.push(parseGif(m));\n    }\n\n    const sensitive = m.ext_sensitive_media_warning;\n    if (sensitive != null) {\n      sensitiveContent =\n        sensitive.adult_content ||\n        sensitive.graphic_violence ||\n        sensitive.other;\n    }\n  }\n\n  return { sensitiveContent, photos, videos };\n}\n\nfunction parseGif(\n  m: NonNullableField<TimelineMediaExtendedRaw, 'id_str' | 'media_url_https'>,\n): Video {\n  const gif: Video = {\n    id: m.id_str,\n    preview: m.media_url_https,\n  };\n\n  const variants = m.video_info?.variants ?? [];\n\n  const url = variants.find((v) => v.content_type === 'video/mp4')?.url;\n\n  if (url) {\n    gif.preview = url;\n    gif.url = url;\n  }\n\n  return gif;\n}\n\nfunction parseVideo(\n  m: NonNullableField<TimelineMediaExtendedRaw, 'id_str' | 'media_url_https'>,\n): Video {\n  const video: Video = {\n    id: m.id_str,\n    preview: m.media_url_https,\n  };\n\n  let maxBitrate = 0;\n  const variants = m.video_info?.variants ?? [];\n  for (const variant of variants) {\n    const bitrate = variant.bitrate;\n    if (bitrate != null && bitrate > maxBitrate && variant.url != null) {\n      let variantUrl = variant.url;\n      const stringStart = 0;\n      const tagSuffixIdx = variantUrl.indexOf('?tag=10');\n      if (tagSuffixIdx !== -1) {\n        variantUrl = variantUrl.substring(stringStart, tagSuffixIdx + 1);\n      }\n\n      video.url = variantUrl;\n      maxBitrate = bitrate;\n    }\n  }\n\n  return video;\n}\n\nexport function reconstructTweetHtml(\n  tweet: LegacyTweetRaw,\n  photos: Photo[],\n  videos: Video[],\n): string {\n  const media: string[] = [];\n\n  // HTML parsing with regex :)\n  let html = tweet.full_text ?? '';\n\n  html = html.replace(reHashtag, linkHashtagHtml);\n  html = html.replace(reCashtag, linkCashtagHtml);\n  html = html.replace(reUsername, linkUsernameHtml);\n  html = html.replace(reTwitterUrl, unwrapTcoUrlHtml(tweet, media));\n\n  for (const { url } of photos) {\n    if (media.indexOf(url) !== -1) {\n      continue;\n    }\n\n    html += `<br><img src=\"${url}\"/>`;\n  }\n\n  for (const { preview: url } of videos) {\n    if (media.indexOf(url) !== -1) {\n      continue;\n    }\n\n    html += `<br><img src=\"${url}\"/>`;\n  }\n\n  html = html.replace(/\\n/g, '<br>');\n\n  return html;\n}\n\nfunction linkHashtagHtml(hashtag: string) {\n  return `<a href=\"https://x.com/hashtag/${hashtag.replace(\n    '#',\n    '',\n  )}\">${hashtag}</a>`;\n}\n\nfunction linkCashtagHtml(cashtag: string) {\n  return `<a href=\"https://x.com/search?q=%24${cashtag.replace(\n    '$',\n    '',\n  )}\">${cashtag}</a>`;\n}\n\nfunction linkUsernameHtml(username: string) {\n  return `<a href=\"https://x.com/${username.replace('@', '')}\">${username}</a>`;\n}\n\nfunction unwrapTcoUrlHtml(tweet: LegacyTweetRaw, foundedMedia: string[]) {\n  return function (tco: string) {\n    for (const entity of tweet.entities?.urls ?? []) {\n      if (tco === entity.url && entity.expanded_url != null) {\n        return `<a href=\"${entity.expanded_url}\">${tco}</a>`;\n      }\n    }\n\n    for (const entity of tweet.extended_entities?.media ?? []) {\n      if (tco === entity.url && entity.media_url_https != null) {\n        foundedMedia.push(entity.media_url_https);\n        return `<br><a href=\"${tco}\"><img src=\"${entity.media_url_https}\"/></a>`;\n      }\n    }\n\n    return tco;\n  };\n}\n","import { CoreUserRaw, LegacyUserRaw } from './profile';\nimport { parseMediaGroups, reconstructTweetHtml } from './timeline-tweet-util';\nimport {\n  EditControlInitialRaw,\n  LegacyTweetRaw,\n  ParseTweetResult,\n  QueryTweetsResponse,\n  SearchResultRaw,\n  TimelineResultRaw,\n} from './timeline-v1';\nimport { Tweet } from './tweets';\nimport { isFieldDefined } from './type-util';\n\nexport interface TimelineUserResultRaw {\n  rest_id?: string;\n  legacy?: LegacyUserRaw;\n  is_blue_verified?: boolean;\n}\n\nexport interface TimelineEntryItemContentRaw {\n  itemType?: string;\n  __typename?: string;\n  tweetDisplayType?: string;\n  tweetResult?: {\n    result?: TimelineResultRaw;\n  };\n  tweet_results?: {\n    result?: TimelineResultRaw;\n  };\n  userDisplayType?: string;\n  user_results?: {\n    result?: TimelineUserResultRaw;\n  };\n}\n\nexport interface TimelineEntryRaw {\n  entryId: string;\n  content?: {\n    entryType?: string;\n    __typename?: string;\n    cursorType?: string;\n    value?: string;\n    items?: {\n      entryId?: string;\n      item?: {\n        content?: TimelineEntryItemContentRaw;\n        itemContent?: SearchEntryItemContentRaw;\n      };\n    }[];\n    itemContent?: TimelineEntryItemContentRaw;\n  };\n}\n\nexport interface SearchEntryItemContentRaw {\n  tweetDisplayType?: string;\n  tweet_results?: {\n    result?: SearchResultRaw;\n  };\n  userDisplayType?: string;\n  user_results?: {\n    result?: TimelineUserResultRaw;\n  };\n}\n\nexport interface SearchEntryRaw {\n  entryId: string;\n  sortIndex: string;\n  content?: {\n    cursorType?: string;\n    entryType?: string;\n    __typename?: string;\n    value?: string;\n    items?: {\n      item?: {\n        content?: SearchEntryItemContentRaw;\n      };\n    }[];\n    itemContent?: SearchEntryItemContentRaw;\n  };\n}\n\nexport interface TimelineInstruction {\n  entries?: TimelineEntryRaw[];\n  entry?: TimelineEntryRaw;\n  type?: string;\n}\n\nexport interface TimelineV2 {\n  data?: {\n    user?: {\n      result?: {\n        __typename?: string;\n        timeline?: {\n          timeline?: {\n            instructions?: TimelineInstruction[];\n          };\n        };\n      };\n    };\n  };\n}\n\nexport interface ThreadedConversation {\n  data?: {\n    threaded_conversation_with_injections_v2?: {\n      instructions?: TimelineInstruction[];\n    };\n  };\n}\n\nfunction getLegacyTweetId(tweet: Readonly<LegacyTweetRaw>): string | undefined {\n  if (tweet.id_str) {\n    return tweet.id_str;\n  }\n\n  return tweet.conversation_id_str;\n}\n\nexport function parseLegacyTweet(\n  coreUser?: Readonly<CoreUserRaw>,\n  user?: Readonly<LegacyUserRaw>,\n  tweet?: Readonly<LegacyTweetRaw>,\n  editControl?: Readonly<EditControlInitialRaw>,\n): ParseTweetResult {\n  if (tweet == null) {\n    return {\n      success: false,\n      err: new Error('Tweet was not found in the timeline object.'),\n    };\n  }\n\n  if (user == null) {\n    return {\n      success: false,\n      err: new Error('User was not found in the timeline object.'),\n    };\n  }\n\n  const tweetId = getLegacyTweetId(tweet);\n  if (!tweetId) {\n    return {\n      success: false,\n      err: new Error('Tweet ID was not found in object.'),\n    };\n  }\n\n  const hashtags = tweet.entities?.hashtags ?? [];\n  const mentions = tweet.entities?.user_mentions ?? [];\n  const media = tweet.extended_entities?.media ?? [];\n  const pinnedTweets = new Set<string | undefined>(\n    user.pinned_tweet_ids_str ?? [],\n  );\n  const urls = tweet.entities?.urls ?? [];\n  const { photos, videos, sensitiveContent } = parseMediaGroups(media);\n\n  // The edit tweets array always contains the original tweet, even if it has not been edited\n  const tweetVersions = editControl?.edit_tweet_ids ?? [tweetId];\n\n  const name = user.name ?? coreUser?.name;\n  const username = user.screen_name ?? coreUser?.screen_name;\n  const tw: Tweet = {\n    __raw_UNSTABLE: tweet,\n    bookmarkCount: tweet.bookmark_count,\n    conversationId: tweet.conversation_id_str,\n    id: tweetId,\n    hashtags: hashtags\n      .filter(isFieldDefined('text'))\n      .map((hashtag) => hashtag.text),\n    likes: tweet.favorite_count,\n    mentions: mentions.filter(isFieldDefined('id_str')).map((mention) => ({\n      id: mention.id_str,\n      username: mention.screen_name,\n      name: mention.name,\n    })),\n    name: name,\n    permanentUrl: `https://x.com/${username}/status/${tweetId}`,\n    photos,\n    replies: tweet.reply_count,\n    retweets: tweet.retweet_count,\n    text: tweet.full_text,\n    thread: [],\n    urls: urls\n      .filter(isFieldDefined('expanded_url'))\n      .map((url) => url.expanded_url),\n    userId: tweet.user_id_str,\n    username: username,\n    videos,\n    isQuoted: false,\n    isReply: false,\n    isEdited: tweetVersions.length > 1,\n    versions: tweetVersions,\n    isRetweet: false,\n    isPin: false,\n    sensitiveContent: false,\n  };\n\n  if (tweet.created_at) {\n    tw.timeParsed = new Date(Date.parse(tweet.created_at));\n    tw.timestamp = Math.floor(tw.timeParsed.valueOf() / 1000);\n  }\n\n  if (tweet.place?.id) {\n    tw.place = tweet.place;\n  }\n\n  const quotedStatusIdStr = tweet.quoted_status_id_str;\n  const inReplyToStatusIdStr = tweet.in_reply_to_status_id_str;\n  const retweetedStatusIdStr = tweet.retweeted_status_id_str;\n  const retweetedStatusResult = tweet.retweeted_status_result?.result;\n\n  if (quotedStatusIdStr) {\n    tw.isQuoted = true;\n    tw.quotedStatusId = quotedStatusIdStr;\n  }\n\n  if (inReplyToStatusIdStr) {\n    tw.isReply = true;\n    tw.inReplyToStatusId = inReplyToStatusIdStr;\n  }\n\n  if (retweetedStatusIdStr || retweetedStatusResult) {\n    tw.isRetweet = true;\n    tw.retweetedStatusId = retweetedStatusIdStr;\n\n    if (retweetedStatusResult) {\n      const parsedResult = parseLegacyTweet(\n        retweetedStatusResult?.core?.user_results?.result?.core,\n        retweetedStatusResult?.core?.user_results?.result?.legacy,\n        retweetedStatusResult?.legacy,\n        retweetedStatusResult?.edit_control?.edit_control_initial,\n      );\n\n      if (parsedResult.success) {\n        tw.retweetedStatus = parsedResult.tweet;\n      }\n    }\n  }\n\n  const views = parseInt(tweet.ext_views?.count ?? '');\n  if (!isNaN(views)) {\n    tw.views = views;\n  }\n\n  if (pinnedTweets.has(tweetId)) {\n    // TODO: Update tests so this can be assigned at the tweet declaration\n    tw.isPin = true;\n  }\n\n  if (sensitiveContent) {\n    // TODO: Update tests so this can be assigned at the tweet declaration\n    tw.sensitiveContent = true;\n  }\n\n  tw.html = reconstructTweetHtml(tweet, tw.photos, tw.videos);\n\n  return { success: true, tweet: tw };\n}\n\nfunction parseResult(result?: TimelineResultRaw): ParseTweetResult {\n  const noteTweetResultText =\n    result?.note_tweet?.note_tweet_results?.result?.text;\n\n  if (result?.legacy && noteTweetResultText) {\n    result.legacy.full_text = noteTweetResultText;\n  }\n\n  const tweetResult = parseLegacyTweet(\n    result?.core?.user_results?.result?.core,\n    result?.core?.user_results?.result?.legacy,\n    result?.legacy,\n    result?.edit_control?.edit_control_initial,\n  );\n  if (!tweetResult.success) {\n    return tweetResult;\n  }\n\n  if (!tweetResult.tweet.views && result?.views?.count) {\n    const views = parseInt(result.views.count);\n    if (!isNaN(views)) {\n      tweetResult.tweet.views = views;\n    }\n  }\n\n  const quotedResult = result?.quoted_status_result?.result;\n  if (quotedResult) {\n    if (quotedResult.legacy && quotedResult.rest_id) {\n      quotedResult.legacy.id_str = quotedResult.rest_id;\n    }\n\n    const quotedTweetResult = parseResult(quotedResult);\n    if (quotedTweetResult.success) {\n      tweetResult.tweet.quotedStatus = quotedTweetResult.tweet;\n    }\n  }\n\n  return tweetResult;\n}\n\nconst expectedEntryTypes = ['tweet', 'profile-conversation'];\n\nfunction getTimelineInstructionEntries(\n  instruction: TimelineInstruction,\n): TimelineEntryRaw[] {\n  const entries = instruction.entries ?? [];\n  if (instruction.entry) {\n    entries.push(instruction.entry);\n  }\n  return entries;\n}\n\nexport function parseTimelineTweetsV2(\n  timeline: TimelineV2,\n): QueryTweetsResponse {\n  let bottomCursor: string | undefined;\n  let topCursor: string | undefined;\n  const tweets: Tweet[] = [];\n  const instructions =\n    timeline.data?.user?.result?.timeline?.timeline?.instructions ?? [];\n  for (const instruction of instructions) {\n    const entries = getTimelineInstructionEntries(instruction);\n    for (const entry of entries) {\n      const entryContent = entry.content;\n      if (!entryContent) continue;\n\n      // Handle pagination\n      if (entryContent.cursorType === 'Bottom') {\n        bottomCursor = entryContent.value;\n        continue;\n      } else if (entryContent.cursorType === 'Top') {\n        topCursor = entryContent.value;\n        continue;\n      }\n\n      const idStr = entry.entryId;\n      if (\n        !expectedEntryTypes.some((entryType) => idStr.startsWith(entryType))\n      ) {\n        continue;\n      }\n\n      if (entryContent.itemContent) {\n        // Typically TimelineTimelineTweet entries\n        parseAndPush(tweets, entryContent.itemContent, idStr);\n      } else if (entryContent.items) {\n        // Typically TimelineTimelineModule entries\n        for (const item of entryContent.items) {\n          if (item.item?.itemContent) {\n            parseAndPush(tweets, item.item.itemContent, idStr);\n          }\n        }\n      }\n    }\n  }\n\n  return { tweets, next: bottomCursor, previous: topCursor };\n}\n\nexport function parseTimelineEntryItemContentRaw(\n  content: TimelineEntryItemContentRaw,\n  entryId: string,\n  isConversation = false,\n) {\n  let result = content.tweet_results?.result ?? content.tweetResult?.result;\n  if (\n    result?.__typename === 'Tweet' ||\n    (result?.__typename === 'TweetWithVisibilityResults' && result?.tweet)\n  ) {\n    if (result?.__typename === 'TweetWithVisibilityResults')\n      result = result.tweet;\n\n    if (result?.legacy) {\n      result.legacy.id_str =\n        result.rest_id ??\n        entryId.replace('conversation-', '').replace('tweet-', '');\n    }\n\n    const tweetResult = parseResult(result);\n    if (tweetResult.success) {\n      if (isConversation) {\n        if (content?.tweetDisplayType === 'SelfThread') {\n          tweetResult.tweet.isSelfThread = true;\n        }\n      }\n\n      return tweetResult.tweet;\n    }\n  }\n\n  return null;\n}\n\nexport function parseAndPush(\n  tweets: Tweet[],\n  content: TimelineEntryItemContentRaw,\n  entryId: string,\n  isConversation = false,\n) {\n  const tweet = parseTimelineEntryItemContentRaw(\n    content,\n    entryId,\n    isConversation,\n  );\n\n  if (tweet) {\n    tweets.push(tweet);\n  }\n}\n\nexport function parseThreadedConversation(\n  conversation: ThreadedConversation,\n): Tweet[] {\n  const tweets: Tweet[] = [];\n  const instructions =\n    conversation.data?.threaded_conversation_with_injections_v2?.instructions ??\n    [];\n\n  for (const instruction of instructions) {\n    const entries = getTimelineInstructionEntries(instruction);\n    for (const entry of entries) {\n      const entryContent = entry.content?.itemContent;\n      if (entryContent) {\n        parseAndPush(tweets, entryContent, entry.entryId, true);\n      }\n\n      for (const item of entry.content?.items ?? []) {\n        const itemContent = item.item?.itemContent;\n        if (itemContent) {\n          parseAndPush(tweets, itemContent, entry.entryId, true);\n        }\n      }\n    }\n  }\n\n  for (const tweet of tweets) {\n    if (tweet.inReplyToStatusId) {\n      for (const parentTweet of tweets) {\n        if (parentTweet.id === tweet.inReplyToStatusId) {\n          tweet.inReplyToStatus = parentTweet;\n          break;\n        }\n      }\n    }\n\n    if (tweet.isSelfThread && tweet.conversationId === tweet.id) {\n      for (const childTweet of tweets) {\n        if (childTweet.isSelfThread && childTweet.id !== tweet.id) {\n          tweet.thread.push(childTweet);\n        }\n      }\n\n      if (tweet.thread.length === 0) {\n        tweet.isSelfThread = false;\n      }\n    }\n  }\n\n  return tweets;\n}\n","import { Profile, parseProfile } from './profile';\nimport { QueryProfilesResponse, QueryTweetsResponse } from './timeline-v1';\nimport { SearchEntryRaw, parseLegacyTweet } from './timeline-v2';\nimport { Tweet } from './tweets';\n\nexport interface SearchTimeline {\n  data?: {\n    search_by_raw_query?: {\n      search_timeline?: {\n        timeline?: {\n          instructions?: {\n            entries?: SearchEntryRaw[];\n            entry?: SearchEntryRaw;\n            type?: string;\n          }[];\n        };\n      };\n    };\n  };\n}\n\nexport function parseSearchTimelineTweets(\n  timeline: SearchTimeline,\n): QueryTweetsResponse {\n  let bottomCursor: string | undefined;\n  let topCursor: string | undefined;\n  const tweets: Tweet[] = [];\n  const instructions =\n    timeline.data?.search_by_raw_query?.search_timeline?.timeline\n      ?.instructions ?? [];\n  for (const instruction of instructions) {\n    if (\n      instruction.type === 'TimelineAddEntries' ||\n      instruction.type === 'TimelineReplaceEntry'\n    ) {\n      if (instruction.entry?.content?.cursorType === 'Bottom') {\n        bottomCursor = instruction.entry.content.value;\n        continue;\n      } else if (instruction.entry?.content?.cursorType === 'Top') {\n        topCursor = instruction.entry.content.value;\n        continue;\n      }\n\n      const entries = instruction.entries ?? [];\n      for (const entry of entries) {\n        const itemContent = entry.content?.itemContent;\n        if (itemContent?.tweetDisplayType === 'Tweet') {\n          const tweetResultRaw = itemContent.tweet_results?.result;\n          const tweetResult = parseLegacyTweet(\n            tweetResultRaw?.core?.user_results?.result?.core,\n            tweetResultRaw?.core?.user_results?.result?.legacy,\n            tweetResultRaw?.legacy,\n            tweetResultRaw?.edit_control?.edit_control_initial,\n          );\n\n          if (tweetResult.success) {\n            if (!tweetResult.tweet.views && tweetResultRaw?.views?.count) {\n              const views = parseInt(tweetResultRaw.views.count);\n              if (!isNaN(views)) {\n                tweetResult.tweet.views = views;\n              }\n            }\n\n            tweets.push(tweetResult.tweet);\n          }\n        } else if (entry.content?.cursorType === 'Bottom') {\n          bottomCursor = entry.content.value;\n        } else if (entry.content?.cursorType === 'Top') {\n          topCursor = entry.content.value;\n        }\n      }\n    }\n  }\n\n  return { tweets, next: bottomCursor, previous: topCursor };\n}\n\nexport function parseSearchTimelineUsers(\n  timeline: SearchTimeline,\n): QueryProfilesResponse {\n  let bottomCursor: string | undefined;\n  let topCursor: string | undefined;\n  const profiles: Profile[] = [];\n  const instructions =\n    timeline.data?.search_by_raw_query?.search_timeline?.timeline\n      ?.instructions ?? [];\n\n  for (const instruction of instructions) {\n    if (\n      instruction.type === 'TimelineAddEntries' ||\n      instruction.type === 'TimelineReplaceEntry'\n    ) {\n      if (instruction.entry?.content?.cursorType === 'Bottom') {\n        bottomCursor = instruction.entry.content.value;\n        continue;\n      } else if (instruction.entry?.content?.cursorType === 'Top') {\n        topCursor = instruction.entry.content.value;\n        continue;\n      }\n\n      const entries = instruction.entries ?? [];\n      for (const entry of entries) {\n        const itemContent = entry.content?.itemContent;\n        if (itemContent?.userDisplayType === 'User') {\n          const userResultRaw = itemContent.user_results?.result;\n\n          if (userResultRaw?.legacy) {\n            const profile = parseProfile(\n              userResultRaw.legacy,\n              userResultRaw.is_blue_verified,\n            );\n\n            if (!profile.userId) {\n              profile.userId = userResultRaw.rest_id;\n            }\n\n            profiles.push(profile);\n          }\n        } else if (entry.content?.cursorType === 'Bottom') {\n          bottomCursor = entry.content.value;\n        } else if (entry.content?.cursorType === 'Top') {\n          topCursor = entry.content.value;\n        }\n      }\n    }\n  }\n\n  return { profiles, next: bottomCursor, previous: topCursor };\n}\n","import { bearerToken2, requestApi } from './api';\nimport { TwitterAuth } from './auth';\nimport { Profile } from './profile';\nimport { QueryProfilesResponse, QueryTweetsResponse } from './timeline-v1';\nimport { getTweetTimeline, getUserTimeline } from './timeline-async';\nimport { Tweet } from './tweets';\nimport {\n  SearchTimeline,\n  parseSearchTimelineTweets,\n  parseSearchTimelineUsers,\n} from './timeline-search';\nimport { AuthenticationError } from './errors';\nimport { apiRequestFactory } from './api-data';\n\n/**\n * The categories that can be used in Twitter searches.\n */\nexport enum SearchMode {\n  Top,\n  Latest,\n  Photos,\n  Videos,\n  Users,\n}\n\nexport function searchTweets(\n  query: string,\n  maxTweets: number,\n  searchMode: SearchMode,\n  auth: TwitterAuth,\n): AsyncGenerator<Tweet, void> {\n  return getTweetTimeline(query, maxTweets, (q, mt, c) => {\n    return fetchSearchTweets(q, mt, searchMode, auth, c);\n  });\n}\n\nexport function searchProfiles(\n  query: string,\n  maxProfiles: number,\n  auth: TwitterAuth,\n): AsyncGenerator<Profile, void> {\n  return getUserTimeline(query, maxProfiles, (q, mt, c) => {\n    return fetchSearchProfiles(q, mt, auth, c);\n  });\n}\n\nexport async function fetchSearchTweets(\n  query: string,\n  maxTweets: number,\n  searchMode: SearchMode,\n  auth: TwitterAuth,\n  cursor?: string,\n): Promise<QueryTweetsResponse> {\n  const timeline = await getSearchTimeline(\n    query,\n    maxTweets,\n    searchMode,\n    auth,\n    cursor,\n  );\n\n  return parseSearchTimelineTweets(timeline);\n}\n\nexport async function fetchSearchProfiles(\n  query: string,\n  maxProfiles: number,\n  auth: TwitterAuth,\n  cursor?: string,\n): Promise<QueryProfilesResponse> {\n  const timeline = await getSearchTimeline(\n    query,\n    maxProfiles,\n    SearchMode.Users,\n    auth,\n    cursor,\n  );\n\n  return parseSearchTimelineUsers(timeline);\n}\n\nasync function getSearchTimeline(\n  query: string,\n  maxItems: number,\n  searchMode: SearchMode,\n  auth: TwitterAuth,\n  cursor?: string,\n): Promise<SearchTimeline> {\n  if (!(await auth.isLoggedIn())) {\n    throw new AuthenticationError('Scraper is not logged-in for search.');\n  }\n\n  if (maxItems > 50) {\n    maxItems = 50;\n  }\n\n  const searchTimelineRequest = apiRequestFactory.createSearchTimelineRequest();\n  searchTimelineRequest.variables.rawQuery = query;\n  searchTimelineRequest.variables.count = maxItems;\n  searchTimelineRequest.variables.querySource = 'typed_query';\n  searchTimelineRequest.variables.product = 'Top';\n\n  if (cursor != null && cursor != '') {\n    searchTimelineRequest.variables['cursor'] = cursor;\n  }\n\n  switch (searchMode) {\n    case SearchMode.Latest:\n      searchTimelineRequest.variables.product = 'Latest';\n      break;\n    case SearchMode.Photos:\n      searchTimelineRequest.variables.product = 'Photos';\n      break;\n    case SearchMode.Videos:\n      searchTimelineRequest.variables.product = 'Videos';\n      break;\n    case SearchMode.Users:\n      searchTimelineRequest.variables.product = 'People';\n      break;\n    default:\n      break;\n  }\n\n  const res = await requestApi<SearchTimeline>(\n    searchTimelineRequest.toRequestUrl(),\n    auth,\n    'GET',\n    undefined,\n    undefined,\n    bearerToken2,\n  );\n\n  if (!res.success) {\n    throw res.err;\n  }\n\n  return res.value;\n}\n","import { Profile, parseProfile } from './profile';\nimport { QueryProfilesResponse } from './timeline-v1';\nimport { TimelineUserResultRaw } from './timeline-v2';\n\nexport interface RelationshipEntryItemContentRaw {\n  itemType?: string;\n  userDisplayType?: string;\n  user_results?: {\n    result?: TimelineUserResultRaw;\n  };\n}\n\nexport interface RelationshipEntryRaw {\n  entryId: string;\n  sortIndex: string;\n  content?: {\n    cursorType?: string;\n    entryType?: string;\n    __typename?: string;\n    value?: string;\n    itemContent?: RelationshipEntryItemContentRaw;\n  };\n}\n\nexport interface RelationshipTimeline {\n  data?: {\n    user?: {\n      result?: {\n        timeline?: {\n          timeline?: {\n            instructions?: {\n              entries?: RelationshipEntryRaw[];\n              entry?: RelationshipEntryRaw;\n              type?: string;\n            }[];\n          };\n        };\n      };\n    };\n  };\n}\n\nexport function parseRelationshipTimeline(\n  timeline: RelationshipTimeline,\n): QueryProfilesResponse {\n  let bottomCursor: string | undefined;\n  let topCursor: string | undefined;\n  const profiles: Profile[] = [];\n  const instructions =\n    timeline.data?.user?.result?.timeline?.timeline?.instructions ?? [];\n\n  for (const instruction of instructions) {\n    if (\n      instruction.type === 'TimelineAddEntries' ||\n      instruction.type === 'TimelineReplaceEntry'\n    ) {\n      if (instruction.entry?.content?.cursorType === 'Bottom') {\n        bottomCursor = instruction.entry.content.value;\n        continue;\n      }\n\n      if (instruction.entry?.content?.cursorType === 'Top') {\n        topCursor = instruction.entry.content.value;\n        continue;\n      }\n\n      const entries = instruction.entries ?? [];\n      for (const entry of entries) {\n        const itemContent = entry.content?.itemContent;\n        if (itemContent?.userDisplayType === 'User') {\n          const userResultRaw = itemContent.user_results?.result;\n\n          if (userResultRaw?.legacy) {\n            const profile = parseProfile(\n              userResultRaw.legacy,\n              userResultRaw.is_blue_verified,\n            );\n\n            if (!profile.userId) {\n              profile.userId = userResultRaw.rest_id;\n            }\n\n            profiles.push(profile);\n          }\n        } else if (entry.content?.cursorType === 'Bottom') {\n          bottomCursor = entry.content.value;\n        } else if (entry.content?.cursorType === 'Top') {\n          topCursor = entry.content.value;\n        }\n      }\n    }\n  }\n\n  return { profiles, next: bottomCursor, previous: topCursor };\n}\n","import { bearerToken2, requestApi } from './api';\nimport { TwitterAuth } from './auth';\nimport { Profile } from './profile';\nimport { QueryProfilesResponse } from './timeline-v1';\nimport { getUserTimeline } from './timeline-async';\nimport {\n  RelationshipTimeline,\n  parseRelationshipTimeline,\n} from './timeline-relationship';\nimport { AuthenticationError } from './errors';\nimport { apiRequestFactory } from './api-data';\n\nexport function getFollowing(\n  userId: string,\n  maxProfiles: number,\n  auth: TwitterAuth,\n): AsyncGenerator<Profile, void> {\n  return getUserTimeline(userId, maxProfiles, (q, mt, c) => {\n    return fetchProfileFollowing(q, mt, auth, c);\n  });\n}\n\nexport function getFollowers(\n  userId: string,\n  maxProfiles: number,\n  auth: TwitterAuth,\n): AsyncGenerator<Profile, void> {\n  return getUserTimeline(userId, maxProfiles, (q, mt, c) => {\n    return fetchProfileFollowers(q, mt, auth, c);\n  });\n}\n\nexport async function fetchProfileFollowing(\n  userId: string,\n  maxProfiles: number,\n  auth: TwitterAuth,\n  cursor?: string,\n): Promise<QueryProfilesResponse> {\n  if (!(await auth.isLoggedIn())) {\n    throw new AuthenticationError(\n      'Scraper is not logged-in for profile following.',\n    );\n  }\n\n  const timeline = await getFollowingTimeline(\n    userId,\n    maxProfiles,\n    auth,\n    cursor,\n  );\n\n  return parseRelationshipTimeline(timeline);\n}\n\nexport async function fetchProfileFollowers(\n  userId: string,\n  maxProfiles: number,\n  auth: TwitterAuth,\n  cursor?: string,\n): Promise<QueryProfilesResponse> {\n  if (!(await auth.isLoggedIn())) {\n    throw new AuthenticationError(\n      'Scraper is not logged-in for profile followers.',\n    );\n  }\n\n  const timeline = await getFollowersTimeline(\n    userId,\n    maxProfiles,\n    auth,\n    cursor,\n  );\n\n  return parseRelationshipTimeline(timeline);\n}\n\nasync function getFollowingTimeline(\n  userId: string,\n  maxItems: number,\n  auth: TwitterAuth,\n  cursor?: string,\n): Promise<RelationshipTimeline> {\n  if (!auth.isLoggedIn()) {\n    throw new AuthenticationError(\n      'Scraper is not logged-in for profile following.',\n    );\n  }\n\n  if (maxItems > 50) {\n    maxItems = 50;\n  }\n\n  const followingRequest = apiRequestFactory.createFollowingRequest();\n  followingRequest.variables.userId = userId;\n  followingRequest.variables.count = maxItems;\n  followingRequest.variables.includePromotedContent = false;\n\n  if (cursor != null && cursor != '') {\n    followingRequest.variables.cursor = cursor;\n  }\n\n  const res = await requestApi<RelationshipTimeline>(\n    followingRequest.toRequestUrl(),\n    auth,\n    'GET',\n    undefined,\n    undefined,\n    bearerToken2,\n  );\n\n  if (!res.success) {\n    throw res.err;\n  }\n\n  return res.value;\n}\n\nasync function getFollowersTimeline(\n  userId: string,\n  maxItems: number,\n  auth: TwitterAuth,\n  cursor?: string,\n): Promise<RelationshipTimeline> {\n  if (!auth.isLoggedIn()) {\n    throw new AuthenticationError(\n      'Scraper is not logged-in for profile followers.',\n    );\n  }\n\n  if (maxItems > 50) {\n    maxItems = 50;\n  }\n\n  const followersRequest = apiRequestFactory.createFollowersRequest();\n  followersRequest.variables.userId = userId;\n  followersRequest.variables.count = maxItems;\n  followersRequest.variables.includePromotedContent = false;\n\n  if (cursor != null && cursor != '') {\n    followersRequest.variables.cursor = cursor;\n  }\n\n  const res = await requestApi<RelationshipTimeline>(\n    followersRequest.toRequestUrl(),\n    auth,\n    'GET',\n    undefined,\n    undefined,\n    bearerToken2,\n  );\n\n  if (!res.success) {\n    throw res.err;\n  }\n\n  return res.value;\n}\n","import { addApiParams, requestApi, bearerToken2 } from './api';\nimport { TwitterAuth } from './auth';\nimport { TimelineV1 } from './timeline-v1';\n\nexport async function getTrends(auth: TwitterAuth): Promise<string[]> {\n  const params = new URLSearchParams();\n  addApiParams(params, false);\n\n  params.set('count', '20');\n  params.set('candidate_source', 'trends');\n  params.set('include_page_configuration', 'false');\n  params.set('entity_tokens', 'false');\n\n  // Use bearerToken2 for trends endpoint\n  const res = await requestApi<TimelineV1>(\n    `https://api.x.com/2/guide.json?${params.toString()}`,\n    auth,\n    'GET',\n    undefined,\n    undefined,\n    bearerToken2,\n  );\n  if (!res.success) {\n    throw res.err;\n  }\n\n  const instructions = res.value.timeline?.instructions ?? [];\n  if (instructions.length < 2) {\n    throw new Error('No trend entries found.');\n  }\n\n  // Some of this is silly, but for now we're assuming we know nothing about the\n  // data, and that anything can be missing. Go has non-nilable strings and empty\n  // slices are nil, so it largely doesn't need to worry about this.\n  const entries = instructions[1].addEntries?.entries ?? [];\n  if (entries.length < 2) {\n    throw new Error('No trend entries found.');\n  }\n\n  const items = entries[1].content?.timelineModule?.items ?? [];\n  const trends: string[] = [];\n  for (const item of items) {\n    const trend =\n      item.item?.clientEventInfo?.details?.guideDetails?.transparentGuideDetails\n        ?.trendMetadata?.trendName;\n    if (trend != null) {\n      trends.push(trend);\n    }\n  }\n\n  return trends;\n}\n","import { QueryTweetsResponse } from './timeline-v1';\nimport { parseAndPush, TimelineEntryRaw } from './timeline-v2';\nimport { Tweet } from './tweets';\n\nexport interface ListTimeline {\n  data?: {\n    list?: {\n      tweets_timeline?: {\n        timeline?: {\n          instructions?: {\n            entries?: TimelineEntryRaw[];\n            entry?: TimelineEntryRaw;\n            type?: string;\n          }[];\n        };\n      };\n    };\n  };\n}\n\nexport function parseListTimelineTweets(\n  timeline: ListTimeline,\n): QueryTweetsResponse {\n  let bottomCursor: string | undefined;\n  let topCursor: string | undefined;\n  const tweets: Tweet[] = [];\n  const instructions =\n    timeline.data?.list?.tweets_timeline?.timeline?.instructions ?? [];\n  for (const instruction of instructions) {\n    const entries = instruction.entries ?? [];\n\n    for (const entry of entries) {\n      const entryContent = entry.content;\n      if (!entryContent) continue;\n\n      if (entryContent.cursorType === 'Bottom') {\n        bottomCursor = entryContent.value;\n        continue;\n      } else if (entryContent.cursorType === 'Top') {\n        topCursor = entryContent.value;\n        continue;\n      }\n\n      const idStr = entry.entryId;\n      if (\n        !idStr.startsWith('tweet') &&\n        !idStr.startsWith('list-conversation')\n      ) {\n        continue;\n      }\n\n      if (entryContent.itemContent) {\n        parseAndPush(tweets, entryContent.itemContent, idStr);\n      } else if (entryContent.items) {\n        for (const contentItem of entryContent.items) {\n          if (\n            contentItem.item &&\n            contentItem.item.itemContent &&\n            contentItem.entryId\n          ) {\n            parseAndPush(\n              tweets,\n              contentItem.item.itemContent,\n              contentItem.entryId.split('tweet-')[1],\n            );\n          }\n        }\n      }\n    }\n  }\n\n  return { tweets, next: bottomCursor, previous: topCursor };\n}\n","import { addApiFeatures, requestApi, bearerToken2 } from './api';\nimport { TwitterAuth } from './auth';\nimport { getUserIdByScreenName } from './profile';\nimport { LegacyTweetRaw, QueryTweetsResponse } from './timeline-v1';\nimport {\n  parseTimelineTweetsV2,\n  TimelineV2,\n  TimelineEntryItemContentRaw,\n  parseTimelineEntryItemContentRaw,\n  ThreadedConversation,\n  parseThreadedConversation,\n} from './timeline-v2';\nimport { getTweetTimeline } from './timeline-async';\nimport { apiRequestFactory } from './api-data';\nimport { ListTimeline, parseListTimelineTweets } from './timeline-list';\nimport { AuthenticationError } from './errors';\n\nexport interface Mention {\n  id: string;\n  username?: string;\n  name?: string;\n}\n\nexport interface Photo {\n  id: string;\n  url: string;\n  alt_text: string | undefined;\n}\n\nexport interface Video {\n  id: string;\n  preview: string;\n  url?: string;\n}\n\nexport interface PlaceRaw {\n  id?: string;\n  place_type?: string;\n  name?: string;\n  full_name?: string;\n  country_code?: string;\n  country?: string;\n  bounding_box?: {\n    type?: string;\n    coordinates?: number[][][];\n  };\n}\n\n/**\n * A parsed Tweet object.\n */\nexport interface Tweet {\n  __raw_UNSTABLE?: LegacyTweetRaw;\n  bookmarkCount?: number;\n  conversationId?: string;\n  hashtags: string[];\n  html?: string;\n  id?: string;\n  inReplyToStatus?: Tweet;\n  inReplyToStatusId?: string;\n  isEdited?: boolean;\n  versions?: string[];\n  isQuoted?: boolean;\n  isPin?: boolean;\n  isReply?: boolean;\n  isRetweet?: boolean;\n  isSelfThread?: boolean;\n  likes?: number;\n  name?: string;\n  mentions: Mention[];\n  permanentUrl?: string;\n  photos: Photo[];\n  place?: PlaceRaw;\n  quotedStatus?: Tweet;\n  quotedStatusId?: string;\n  replies?: number;\n  retweets?: number;\n  retweetedStatus?: Tweet;\n  retweetedStatusId?: string;\n  text?: string;\n  thread: Tweet[];\n  timeParsed?: Date;\n  timestamp?: number;\n  urls: string[];\n  userId?: string;\n  username?: string;\n  videos: Video[];\n  views?: number;\n  sensitiveContent?: boolean;\n}\n\nexport type TweetQuery =\n  | Partial<Tweet>\n  | ((tweet: Tweet) => boolean | Promise<boolean>);\n\nexport const features = addApiFeatures({\n  interactive_text_enabled: true,\n  longform_notetweets_inline_media_enabled: false,\n  responsive_web_text_conversations_enabled: false,\n  tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled:\n    false,\n  vibe_api_enabled: false,\n});\n\nexport async function fetchTweets(\n  userId: string,\n  maxTweets: number,\n  cursor: string | undefined,\n  auth: TwitterAuth,\n): Promise<QueryTweetsResponse> {\n  if (maxTweets > 200) {\n    maxTweets = 200;\n  }\n\n  const userTweetsRequest = apiRequestFactory.createUserTweetsRequest();\n  userTweetsRequest.variables.userId = userId;\n  userTweetsRequest.variables.count = maxTweets;\n  userTweetsRequest.variables.includePromotedContent = false; // true on the website\n\n  if (cursor != null && cursor != '') {\n    userTweetsRequest.variables['cursor'] = cursor;\n  }\n\n  // Use bearerToken2 for UserTweets endpoint\n  const res = await requestApi<TimelineV2>(\n    userTweetsRequest.toRequestUrl(),\n    auth,\n    'GET',\n    undefined,\n    undefined,\n    bearerToken2,\n  );\n\n  if (!res.success) {\n    throw res.err;\n  }\n\n  return parseTimelineTweetsV2(res.value);\n}\n\nexport async function fetchTweetsAndReplies(\n  userId: string,\n  maxTweets: number,\n  cursor: string | undefined,\n  auth: TwitterAuth,\n): Promise<QueryTweetsResponse> {\n  if (maxTweets > 40) {\n    maxTweets = 40;\n  }\n\n  const userTweetsRequest =\n    apiRequestFactory.createUserTweetsAndRepliesRequest();\n  userTweetsRequest.variables.userId = userId;\n  userTweetsRequest.variables.count = maxTweets;\n  userTweetsRequest.variables.includePromotedContent = false; // true on the website\n\n  if (cursor != null && cursor != '') {\n    userTweetsRequest.variables['cursor'] = cursor;\n  }\n\n  // Use bearerToken2 for UserTweetsAndReplies endpoint\n  const res = await requestApi<TimelineV2>(\n    userTweetsRequest.toRequestUrl(),\n    auth,\n    'GET',\n    undefined,\n    undefined,\n    bearerToken2,\n  );\n\n  if (!res.success) {\n    throw res.err;\n  }\n\n  return parseTimelineTweetsV2(res.value);\n}\n\nexport async function fetchListTweets(\n  listId: string,\n  maxTweets: number,\n  cursor: string | undefined,\n  auth: TwitterAuth,\n): Promise<QueryTweetsResponse> {\n  if (maxTweets > 200) {\n    maxTweets = 200;\n  }\n\n  const listTweetsRequest = apiRequestFactory.createListTweetsRequest();\n  listTweetsRequest.variables.listId = listId;\n  listTweetsRequest.variables.count = maxTweets;\n\n  if (cursor != null && cursor != '') {\n    listTweetsRequest.variables['cursor'] = cursor;\n  }\n\n  // Use bearerToken2 for ListTweet endpoint\n  const res = await requestApi<ListTimeline>(\n    listTweetsRequest.toRequestUrl(),\n    auth,\n    'GET',\n    undefined,\n    undefined,\n    bearerToken2,\n  );\n\n  if (!res.success) {\n    throw res.err;\n  }\n\n  return parseListTimelineTweets(res.value);\n}\n\nexport function getTweets(\n  user: string,\n  maxTweets: number,\n  auth: TwitterAuth,\n): AsyncGenerator<Tweet, void> {\n  return getTweetTimeline(user, maxTweets, async (q, mt, c) => {\n    const userIdRes = await getUserIdByScreenName(q, auth);\n\n    if (!userIdRes.success) {\n      throw userIdRes.err;\n    }\n\n    const { value: userId } = userIdRes;\n\n    return fetchTweets(userId, mt, c, auth);\n  });\n}\n\nexport function getTweetsByUserId(\n  userId: string,\n  maxTweets: number,\n  auth: TwitterAuth,\n): AsyncGenerator<Tweet, void> {\n  return getTweetTimeline(userId, maxTweets, (q, mt, c) => {\n    return fetchTweets(q, mt, c, auth);\n  });\n}\n\nexport function getTweetsAndReplies(\n  user: string,\n  maxTweets: number,\n  auth: TwitterAuth,\n): AsyncGenerator<Tweet, void> {\n  return getTweetTimeline(user, maxTweets, async (q, mt, c) => {\n    const userIdRes = await getUserIdByScreenName(q, auth);\n\n    if (!userIdRes.success) {\n      throw userIdRes.err;\n    }\n\n    const { value: userId } = userIdRes;\n\n    return fetchTweetsAndReplies(userId, mt, c, auth);\n  });\n}\n\nexport function getTweetsAndRepliesByUserId(\n  userId: string,\n  maxTweets: number,\n  auth: TwitterAuth,\n): AsyncGenerator<Tweet, void> {\n  return getTweetTimeline(userId, maxTweets, (q, mt, c) => {\n    return fetchTweetsAndReplies(q, mt, c, auth);\n  });\n}\n\nexport async function fetchLikedTweets(\n  userId: string,\n  maxTweets: number,\n  cursor: string | undefined,\n  auth: TwitterAuth,\n): Promise<QueryTweetsResponse> {\n  if (!(await auth.isLoggedIn())) {\n    throw new AuthenticationError(\n      'Scraper is not logged-in for fetching liked tweets.',\n    );\n  }\n\n  if (maxTweets > 200) {\n    maxTweets = 200;\n  }\n\n  const userTweetsRequest = apiRequestFactory.createUserLikedTweetsRequest();\n  userTweetsRequest.variables.userId = userId;\n  userTweetsRequest.variables.count = maxTweets;\n  userTweetsRequest.variables.includePromotedContent = false; // true on the website\n\n  if (cursor != null && cursor != '') {\n    userTweetsRequest.variables['cursor'] = cursor;\n  }\n\n  // Use bearerToken2 for UserLikedTweets endpoint\n  const res = await requestApi<TimelineV2>(\n    userTweetsRequest.toRequestUrl(),\n    auth,\n    'GET',\n    undefined,\n    undefined,\n    bearerToken2,\n  );\n\n  if (!res.success) {\n    throw res.err;\n  }\n\n  return parseTimelineTweetsV2(res.value);\n}\n\nexport function getLikedTweets(\n  user: string,\n  maxTweets: number,\n  auth: TwitterAuth,\n): AsyncGenerator<Tweet, void> {\n  return getTweetTimeline(user, maxTweets, async (q, mt, c) => {\n    const userIdRes = await getUserIdByScreenName(q, auth);\n\n    if (!userIdRes.success) {\n      throw userIdRes.err;\n    }\n\n    const { value: userId } = userIdRes;\n\n    return fetchLikedTweets(userId, mt, c, auth);\n  });\n}\n\nexport async function getTweetWhere(\n  tweets: AsyncIterable<Tweet>,\n  query: TweetQuery,\n): Promise<Tweet | null> {\n  const isCallback = typeof query === 'function';\n\n  for await (const tweet of tweets) {\n    const matches = isCallback\n      ? await query(tweet)\n      : checkTweetMatches(tweet, query);\n\n    if (matches) {\n      return tweet;\n    }\n  }\n\n  return null;\n}\n\nexport async function getTweetsWhere(\n  tweets: AsyncIterable<Tweet>,\n  query: TweetQuery,\n): Promise<Tweet[]> {\n  const isCallback = typeof query === 'function';\n  const filtered = [];\n\n  for await (const tweet of tweets) {\n    const matches = isCallback ? query(tweet) : checkTweetMatches(tweet, query);\n\n    if (!matches) continue;\n    filtered.push(tweet);\n  }\n\n  return filtered;\n}\n\nfunction checkTweetMatches(tweet: Tweet, options: Partial<Tweet>): boolean {\n  return Object.keys(options).every((k) => {\n    const key = k as keyof Tweet;\n    return tweet[key] === options[key];\n  });\n}\n\nexport async function getLatestTweet(\n  user: string,\n  includeRetweets: boolean,\n  max: number,\n  auth: TwitterAuth,\n): Promise<Tweet | null | void> {\n  const timeline = getTweets(user, max, auth);\n\n  // No point looping if max is 1, just use first entry.\n  return max === 1\n    ? (await timeline.next()).value\n    : await getTweetWhere(timeline, { isRetweet: includeRetweets });\n}\n\nexport interface TweetResultByRestId {\n  data?: TimelineEntryItemContentRaw;\n}\n\nexport async function getTweet(\n  id: string,\n  auth: TwitterAuth,\n): Promise<Tweet | null> {\n  const tweetDetailRequest = apiRequestFactory.createTweetDetailRequest();\n  tweetDetailRequest.variables.focalTweetId = id;\n\n  // Use bearerToken2 for this specific endpoint (TweetDetail)\n  // This is required for animated GIFs to appear in tweets with mixed media\n  const res = await requestApi<ThreadedConversation>(\n    tweetDetailRequest.toRequestUrl(),\n    auth,\n    'GET',\n    undefined,\n    undefined,\n    bearerToken2,\n  );\n\n  if (!res.success) {\n    throw res.err;\n  }\n\n  if (!res.value) {\n    return null;\n  }\n\n  const tweets = parseThreadedConversation(res.value);\n  return tweets.find((tweet) => tweet.id === id) ?? null;\n}\n\nexport async function getTweetAnonymous(\n  id: string,\n  auth: TwitterAuth,\n): Promise<Tweet | null> {\n  const tweetResultByRestIdRequest =\n    apiRequestFactory.createTweetResultByRestIdRequest();\n  tweetResultByRestIdRequest.variables.tweetId = id;\n\n  // Use bearerToken2 for this specific endpoint (TweetResultByRestId)\n  // This matches the behavior observed in the Twitter web client and Go library\n  // We pass it as an override to avoid mutating shared state (concurrency-safe)\n  const res = await requestApi<TweetResultByRestId>(\n    tweetResultByRestIdRequest.toRequestUrl(),\n    auth,\n    'GET',\n    undefined,\n    undefined,\n    bearerToken2,\n  );\n\n  if (!res.success) {\n    throw res.err;\n  }\n\n  if (!res.value.data) {\n    return null;\n  }\n\n  return parseTimelineEntryItemContentRaw(res.value.data, id);\n}\n","import {\n  DmConversationTimeline,\n  DmMessageEntry,\n  DmCursorOptions,\n} from './direct-messages';\nimport { jitter } from './api';\n\nexport interface FetchDmConversationMessagesResponse {\n  conversation: DmConversationTimeline;\n  next?: DmCursorOptions;\n}\n\nexport type FetchDmConversationFn = (\n  conversationId: string,\n  maxMessages: number,\n  cursor: DmCursorOptions | undefined,\n) => Promise<FetchDmConversationMessagesResponse>;\n\nexport async function* getDmConversationMessagesGenerator(\n  conversationId: string,\n  maxMessages: number,\n  initialCursor: DmCursorOptions | undefined,\n  fetchFunc: FetchDmConversationFn,\n): AsyncGenerator<DmMessageEntry, void> {\n  let nMessages = 0;\n  let cursor = initialCursor;\n\n  while (nMessages < maxMessages) {\n    const batch: FetchDmConversationMessagesResponse = await fetchFunc(\n      conversationId,\n      maxMessages,\n      cursor,\n    );\n\n    const { conversation, next } = batch;\n\n    if (!conversation?.entries || conversation?.entries?.length === 0) {\n      break;\n    }\n\n    for (const entry of conversation.entries) {\n      if (nMessages < maxMessages) {\n        yield entry;\n        nMessages++;\n      } else {\n        break;\n      }\n    }\n\n    cursor = next;\n\n    if (conversation.status === 'AT_END' || !next) {\n      break;\n    }\n\n    await jitter(1000);\n  }\n}\n","import { AuthenticationError } from './errors';\nimport { TwitterAuth } from './auth';\nimport { LegacyUserRaw } from './profile';\nimport { requestApi, addApiParams } from './api';\n\nimport { getDmConversationMessagesGenerator } from './direct-messages-async';\n\nexport interface DmInboxResponse {\n  inbox_initial_state: DmInbox;\n}\n\nexport interface DmInbox {\n  last_seen_event_id: string;\n  trusted_last_seen_event_id: string;\n  untrusted_last_seen_event_id: string;\n  cursor: string;\n  inbox_timelines: DmInboxTimelines;\n  entries: DmMessageEntry[];\n  users: { [key: string]: LegacyUserRaw };\n  conversations: { [key: string]: DmConversation };\n}\n\nexport interface DmConversationResponse {\n  conversation_timeline: DmConversationTimeline;\n}\n\nexport interface DmConversationTimeline {\n  status: DmStatus;\n  min_entry_id: string;\n  max_entry_id: string;\n  entries: DmMessageEntry[];\n  users: { [key: string]: LegacyUserRaw };\n  conversations: { [key: string]: DmConversation };\n}\n\nexport interface DmConversation {\n  conversation_id: string;\n  type: string;\n  sort_event_id: string;\n  sort_timestamp: string;\n  participants: DmParticipant[];\n  nsfw: boolean;\n  notifications_disabled: boolean;\n  mention_notifications_disabled: boolean;\n  last_read_event_id: string;\n  read_only: boolean;\n  trusted: boolean;\n  muted: boolean;\n  status: DmStatus;\n  min_entry_id: string;\n  max_entry_id: string;\n}\n\nexport type DmStatus = 'AT_END' | 'HAS_MORE';\n\nexport interface DmParticipant {\n  user_id: string;\n  last_read_event_id?: string;\n}\n\nexport interface DmMessageEntry {\n  welcome_message_create?: DmWelcomeMessage;\n  message?: DmMessage;\n}\n\nexport interface DmMessage {\n  id: string;\n  time: string;\n  affects_sort: boolean;\n  request_id: string;\n  conversation_id: string;\n  message_data: DmMessageData;\n  message_reactions: DmReaction[];\n}\n\nexport interface DmMessageData {\n  id: string;\n  time: string;\n  recipient_id: string;\n  sender_id: string;\n  text: string;\n  edit_count?: number;\n  entities?: DmMessageEntities;\n}\n\nexport interface DmReaction {\n  id: string;\n  time: string;\n  conversation_id: string;\n  message_id: string;\n  reaction_key: string;\n  emoji_reaction: string;\n  sender_id: string;\n}\n\nexport interface DmMessageEntities {\n  // TODO: Not sure what these types are.\n  hashtags: any[];\n  symbols: any[];\n  user_mentions: any[];\n  urls: DmMessageUrl[];\n}\n\nexport interface DmMessageUrl {\n  url: string;\n  expanded_url: string;\n  display_url: string;\n  indices: number[];\n}\n\nexport interface DmWelcomeMessage extends DmMessage {\n  welcome_message_id: string;\n}\n\nexport interface DmInboxTimelines {\n  trusted: DmTimelineState;\n  untrusted: DmTimelineState;\n  untrusted_low_quality: DmTimelineState;\n}\n\nexport interface DmTimelineState {\n  status: DmStatus;\n  min_entry_id: string;\n}\n\nexport interface DmCursorOptions {\n  maxId?: string;\n  minId?: string;\n}\n\nexport async function fetchDmInbox(auth: TwitterAuth) {\n  if (!(await auth.isLoggedIn())) {\n    throw new AuthenticationError(\n      'Scraper is not logged-in for fetching direct messages.',\n    );\n  }\n\n  // TODO: Not sure how the \"cursor\" works for this. I don't have enough DMs to test.\n  const params = new URLSearchParams();\n  addApiParams(params, false);\n\n  params.set('nsfw_filtering_enabled', 'false');\n  params.set('filter_low_quality', 'true');\n  params.set('include_quality', 'all');\n  params.set('include_ext_profile_image_shape', '1');\n  params.set('dm_secret_conversations_enabled', 'false');\n  params.set('krs_registration_enabled', 'false');\n  params.set('include_ext_limited_action_results', 'true');\n  params.set('dm_users', 'true');\n  params.set('include_groups', 'true');\n  params.set('include_inbox_timelines', 'true');\n  params.set('supports_reactions', 'true');\n  params.set('supports_edit', 'true');\n  params.set('include_ext_edit_control', 'true');\n  params.set('include_ext_business_affiliations_label', 'true');\n  params.set('include_ext_parody_commentary_fan_label', 'true');\n  params.set(\n    'ext',\n    'mediaColor,altText,mediaStats,highlightedLabel,parodyCommentaryFanLabel,voiceInfo,birdwatchPivot,superFollowMetadata,unmentionInfo,editControl,article',\n  );\n\n  const res = await requestApi<DmInboxResponse>(\n    `https://x.com/i/api/1.1/dm/inbox_initial_state.json?${params.toString()}`,\n    auth,\n  );\n\n  if (!res.success) {\n    throw res.err;\n  }\n\n  return parseDmInbox(res.value);\n}\n\nexport async function parseDmInbox(inbox: DmInboxResponse) {\n  return inbox.inbox_initial_state;\n}\n\n// This gets the current authenticated user's direct messages.\n// This requires the user to be authenticated.\nexport async function getDmInbox(auth: TwitterAuth) {\n  return await fetchDmInbox(auth);\n}\n\n// This gets the current authenticated user's direct conversations.\n// This requires the user to be authenticated.\nexport async function fetchDmConversation(\n  conversationId: string,\n  cursor: DmCursorOptions | undefined,\n  auth: TwitterAuth,\n) {\n  if (!(await auth.isLoggedIn())) {\n    throw new AuthenticationError(\n      'Scraper is not logged-in for fetching direct messages.',\n    );\n  }\n\n  const params = new URLSearchParams();\n  addApiParams(params, false);\n\n  params.set('context', 'FETCH_DM_CONVERSATION_HISTORY');\n  params.set('include_ext_profile_image_shape', '1');\n  params.set('dm_secret_conversations_enabled', 'false');\n  params.set('krs_registration_enabled', 'false');\n  params.set('include_ext_limited_action_results', 'true');\n  params.set('dm_users', 'true');\n  params.set('include_groups', 'true');\n  params.set('include_inbox_timelines', 'true');\n  params.set('supports_reactions', 'true');\n  params.set('supports_edit', 'true');\n  params.set('include_conversation_info', 'true');\n  params.set(\n    'ext',\n    'mediaColor,altText,mediaStats,highlightedLabel,parodyCommentaryFanLabel,voiceInfo,birdwatchPivot,superFollowMetadata,unmentionInfo,editControl,article',\n  );\n\n  // By default, passing no cursor means you get the latest results.\n  // `max_id` does backwards pagination using min_entry_id as the maxId to get older messages.\n  // `min_id` does forward pagination using max_entry_id as the minId to get newer messages.\n  // To know when there are no more pages, the response's \"status\" will return \"AT_END\".\n  if (cursor) {\n    if (cursor.maxId) {\n      params.set('max_id', cursor.maxId);\n    }\n    if (cursor.minId) {\n      params.set('min_id', cursor.minId);\n    }\n  }\n\n  const url = `https://x.com/i/api/1.1/dm/conversation/${conversationId}.json?${params.toString()}`;\n\n  const res = await requestApi<DmConversationResponse>(url, auth);\n\n  if (!res.success) {\n    throw res.err;\n  }\n\n  return parseDmConversation(res.value);\n}\n\nexport async function parseDmConversation(\n  conversation: DmConversationResponse,\n) {\n  return conversation.conversation_timeline;\n}\n\nexport async function getDmConversation(\n  conversationId: string,\n  cursor: DmCursorOptions | undefined,\n  auth: TwitterAuth,\n) {\n  return await fetchDmConversation(conversationId, cursor, auth);\n}\n\nexport function getDmMessages(\n  conversationId: string,\n  maxMessages: number,\n  cursor: DmCursorOptions | undefined,\n  auth: TwitterAuth,\n): AsyncGenerator<DmMessageEntry, void> {\n  return getDmConversationMessagesGenerator(\n    conversationId,\n    maxMessages,\n    cursor,\n    async (id, _max, cursor) => {\n      const conversation = await fetchDmConversation(id, cursor, auth);\n\n      let next: DmCursorOptions | undefined = undefined;\n\n      if (cursor?.minId && conversation.max_entry_id) {\n        next = { minId: conversation.max_entry_id };\n      } else if (conversation.min_entry_id) {\n        next = { maxId: conversation.min_entry_id };\n      }\n\n      return {\n        conversation,\n        next,\n      };\n    },\n  );\n}\n\nexport function findDmConversationsByUserId(\n  inbox: DmInbox,\n  userId: string,\n): DmConversation[] {\n  const conversations: DmConversation[] = [];\n\n  for (const conversationId in inbox.conversations) {\n    const conversation = inbox.conversations[conversationId];\n    const hasUser = conversation.participants.some(\n      (participant) => participant.user_id === userId,\n    );\n\n    if (hasUser) {\n      conversations.push(conversation);\n    }\n  }\n\n  return conversations;\n}\n","import { Cookie } from 'tough-cookie';\nimport { bearerToken, FetchTransformOptions, RequestApiResult } from './api';\nimport { TwitterAuth, TwitterAuthOptions, TwitterGuestAuth } from './auth';\nimport { FlowSubtaskHandler, TwitterUserAuth } from './auth-user';\nimport { getProfile, getUserIdByScreenName, Profile } from './profile';\nimport {\n  fetchSearchProfiles,\n  fetchSearchTweets,\n  SearchMode,\n  searchProfiles,\n  searchTweets,\n} from './search';\nimport {\n  fetchProfileFollowing,\n  fetchProfileFollowers,\n  getFollowing,\n  getFollowers,\n} from './relationships';\nimport { QueryProfilesResponse, QueryTweetsResponse } from './timeline-v1';\nimport { getTrends } from './trends';\nimport {\n  Tweet,\n  getTweetAnonymous,\n  getTweets,\n  getLatestTweet,\n  getLikedTweets,\n  getTweetWhere,\n  getTweetsWhere,\n  getTweetsByUserId,\n  TweetQuery,\n  getTweet,\n  fetchListTweets,\n  getTweetsAndRepliesByUserId,\n  getTweetsAndReplies,\n  fetchLikedTweets,\n} from './tweets';\nimport fetch from 'cross-fetch';\nimport { RateLimitStrategy } from './rate-limit';\nimport {\n  DmConversationTimeline,\n  DmInbox,\n  DmMessageEntry,\n  DmCursorOptions,\n  getDmConversation,\n  getDmMessages,\n  getDmInbox,\n  findDmConversationsByUserId,\n  DmConversation,\n} from './direct-messages';\n\nconst twUrl = 'https://x.com';\n\nexport interface ScraperOptions {\n  /**\n   * An alternative fetch function to use instead of the default fetch function. This may be useful\n   * in nonstandard runtime environments, such as edge workers.\n   */\n  fetch: typeof fetch;\n\n  /**\n   * Additional options that control how requests and responses are processed. This can be used to\n   * proxy requests through other hosts, for example.\n   */\n  transform: Partial<FetchTransformOptions>;\n\n  /**\n   * A handling strategy for rate limits (HTTP 429).\n   */\n  rateLimitStrategy: RateLimitStrategy;\n\n  /**\n   * Experimental features that may be added, changed, or removed at any time. Use with caution.\n   */\n  experimental: {\n    /**\n     * Enables the generation of the `x-client-transaction-id` header on requests. This may resolve some errors.\n     */\n    xClientTransactionId: boolean;\n    /**\n     * Enables the generation of the `x-xp-forwarded-for` header on requests. This may resolve some errors.\n     */\n    xpff: boolean;\n  };\n}\n\n/**\n * An interface to Twitter's undocumented API.\n * - Reusing Scraper objects is recommended to minimize the time spent authenticating unnecessarily.\n */\nexport class Scraper {\n  private auth!: TwitterAuth;\n  private authTrends!: TwitterAuth;\n  private token: string;\n\n  /**\n   * Creates a new Scraper object.\n   * - Scrapers maintain their own guest tokens for Twitter's internal API.\n   * - Reusing Scraper objects is recommended to minimize the time spent authenticating unnecessarily.\n   */\n  constructor(private readonly options?: Partial<ScraperOptions>) {\n    this.token = bearerToken;\n    this.useGuestAuth();\n  }\n\n  /**\n   * Registers a subtask handler for the given subtask ID. This\n   * will override any existing handler for the same subtask.\n   * @param subtaskId The ID of the subtask to register the handler for.\n   * @param subtaskHandler The handler function to register.\n   */\n  public registerAuthSubtaskHandler(\n    subtaskId: string,\n    subtaskHandler: FlowSubtaskHandler,\n  ): void {\n    if (this.auth instanceof TwitterUserAuth) {\n      this.auth.registerSubtaskHandler(subtaskId, subtaskHandler);\n    }\n\n    if (this.authTrends instanceof TwitterUserAuth) {\n      this.authTrends.registerSubtaskHandler(subtaskId, subtaskHandler);\n    }\n  }\n\n  /**\n   * Initializes auth properties using a guest token.\n   * Used when creating a new instance of this class, and when logging out.\n   * @internal\n   */\n  private useGuestAuth() {\n    this.auth = new TwitterGuestAuth(this.token, this.getAuthOptions());\n    this.authTrends = new TwitterGuestAuth(this.token, this.getAuthOptions());\n  }\n\n  /**\n   * Fetches a Twitter profile.\n   * @param username The Twitter username of the profile to fetch, without an `@` at the beginning.\n   * @returns The requested {@link Profile}.\n   */\n  public async getProfile(username: string): Promise<Profile> {\n    const res = await getProfile(username, this.auth);\n    return this.handleResponse(res);\n  }\n\n  /**\n   * Fetches the user ID corresponding to the provided screen name.\n   * @param screenName The Twitter screen name of the profile to fetch.\n   * @returns The ID of the corresponding account.\n   */\n  public async getUserIdByScreenName(screenName: string): Promise<string> {\n    const res = await getUserIdByScreenName(screenName, this.auth);\n    return this.handleResponse(res);\n  }\n\n  /**\n   * Fetches tweets from Twitter.\n   * @param query The search query. Any Twitter-compatible query format can be used.\n   * @param maxTweets The maximum number of tweets to return.\n   * @param includeReplies Whether or not replies should be included in the response.\n   * @param searchMode The category filter to apply to the search. Defaults to `Top`.\n   * @returns An {@link AsyncGenerator} of tweets matching the provided filters.\n   */\n  public searchTweets(\n    query: string,\n    maxTweets: number,\n    searchMode: SearchMode = SearchMode.Top,\n  ): AsyncGenerator<Tweet, void> {\n    return searchTweets(query, maxTweets, searchMode, this.auth);\n  }\n\n  /**\n   * Fetches profiles from Twitter.\n   * @param query The search query. Any Twitter-compatible query format can be used.\n   * @param maxProfiles The maximum number of profiles to return.\n   * @returns An {@link AsyncGenerator} of tweets matching the provided filter(s).\n   */\n  public searchProfiles(\n    query: string,\n    maxProfiles: number,\n  ): AsyncGenerator<Profile, void> {\n    return searchProfiles(query, maxProfiles, this.auth);\n  }\n\n  /**\n   * Fetches tweets from Twitter.\n   * @param query The search query. Any Twitter-compatible query format can be used.\n   * @param maxTweets The maximum number of tweets to return.\n   * @param includeReplies Whether or not replies should be included in the response.\n   * @param searchMode The category filter to apply to the search. Defaults to `Top`.\n   * @param cursor The search cursor, which can be passed into further requests for more results.\n   * @returns A page of results, containing a cursor that can be used in further requests.\n   */\n  public fetchSearchTweets(\n    query: string,\n    maxTweets: number,\n    searchMode: SearchMode,\n    cursor?: string,\n  ): Promise<QueryTweetsResponse> {\n    return fetchSearchTweets(query, maxTweets, searchMode, this.auth, cursor);\n  }\n\n  /**\n   * Fetches profiles from Twitter.\n   * @param query The search query. Any Twitter-compatible query format can be used.\n   * @param maxProfiles The maximum number of profiles to return.\n   * @param cursor The search cursor, which can be passed into further requests for more results.\n   * @returns A page of results, containing a cursor that can be used in further requests.\n   */\n  public fetchSearchProfiles(\n    query: string,\n    maxProfiles: number,\n    cursor?: string,\n  ): Promise<QueryProfilesResponse> {\n    return fetchSearchProfiles(query, maxProfiles, this.auth, cursor);\n  }\n\n  /**\n   * Fetches list tweets from Twitter.\n   * @param listId The list id\n   * @param maxTweets The maximum number of tweets to return.\n   * @param cursor The search cursor, which can be passed into further requests for more results.\n   * @returns A page of results, containing a cursor that can be used in further requests.\n   */\n  public fetchListTweets(\n    listId: string,\n    maxTweets: number,\n    cursor?: string,\n  ): Promise<QueryTweetsResponse> {\n    return fetchListTweets(listId, maxTweets, cursor, this.auth);\n  }\n\n  /**\n   * Fetch the tweets a user has liked\n   * @param userId The user whose liked tweets should be returned\n   * @param maxTweets The maximum number of tweets to return.\n   * @param cursor The search cursor, which can be passed into further requests for more results.\n   * @returns A page of results, containing a cursor that can be used in further requests.\n   */\n  public fetchLikedTweets(\n    userId: string,\n    maxTweets: number,\n    cursor?: string,\n  ): Promise<QueryTweetsResponse> {\n    return fetchLikedTweets(userId, maxTweets, cursor, this.auth);\n  }\n\n  /**\n   * Fetch the profiles a user is following\n   * @param userId The user whose following should be returned\n   * @param maxProfiles The maximum number of profiles to return.\n   * @returns An {@link AsyncGenerator} of following profiles for the provided user.\n   */\n  public getFollowing(\n    userId: string,\n    maxProfiles: number,\n  ): AsyncGenerator<Profile, void> {\n    return getFollowing(userId, maxProfiles, this.auth);\n  }\n\n  /**\n   * Fetch the profiles that follow a user\n   * @param userId The user whose followers should be returned\n   * @param maxProfiles The maximum number of profiles to return.\n   * @returns An {@link AsyncGenerator} of profiles following the provided user.\n   */\n  public getFollowers(\n    userId: string,\n    maxProfiles: number,\n  ): AsyncGenerator<Profile, void> {\n    return getFollowers(userId, maxProfiles, this.auth);\n  }\n\n  /**\n   * Fetches following profiles from Twitter.\n   * @param userId The user whose following should be returned\n   * @param maxProfiles The maximum number of profiles to return.\n   * @param cursor The search cursor, which can be passed into further requests for more results.\n   * @returns A page of results, containing a cursor that can be used in further requests.\n   */\n  public fetchProfileFollowing(\n    userId: string,\n    maxProfiles: number,\n    cursor?: string,\n  ): Promise<QueryProfilesResponse> {\n    return fetchProfileFollowing(userId, maxProfiles, this.auth, cursor);\n  }\n\n  /**\n   * Fetches profile followers from Twitter.\n   * @param userId The user whose following should be returned\n   * @param maxProfiles The maximum number of profiles to return.\n   * @param cursor The search cursor, which can be passed into further requests for more results.\n   * @returns A page of results, containing a cursor that can be used in further requests.\n   */\n  public fetchProfileFollowers(\n    userId: string,\n    maxProfiles: number,\n    cursor?: string,\n  ): Promise<QueryProfilesResponse> {\n    return fetchProfileFollowers(userId, maxProfiles, this.auth, cursor);\n  }\n\n  /**\n   * Fetches the current trends from Twitter.\n   * @returns The current list of trends.\n   */\n  public getTrends(): Promise<string[]> {\n    return getTrends(this.authTrends);\n  }\n\n  /**\n   * Fetches tweets from a Twitter user.\n   * @param user The user whose tweets should be returned.\n   * @param maxTweets The maximum number of tweets to return. Defaults to `200`.\n   * @returns An {@link AsyncGenerator} of tweets from the provided user.\n   */\n  public getTweets(user: string, maxTweets = 200): AsyncGenerator<Tweet> {\n    return getTweets(user, maxTweets, this.auth);\n  }\n\n  /**\n   * Fetches liked tweets from a Twitter user. Requires authentication.\n   * @param user The user whose likes should be returned.\n   * @param maxTweets The maximum number of tweets to return. Defaults to `200`.\n   * @returns An {@link AsyncGenerator} of liked tweets from the provided user.\n   */\n  public getLikedTweets(user: string, maxTweets = 200): AsyncGenerator<Tweet> {\n    return getLikedTweets(user, maxTweets, this.auth);\n  }\n\n  /**\n   * Fetches tweets from a Twitter user using their ID.\n   * @param userId The user whose tweets should be returned.\n   * @param maxTweets The maximum number of tweets to return. Defaults to `200`.\n   * @returns An {@link AsyncGenerator} of tweets from the provided user.\n   */\n  public getTweetsByUserId(\n    userId: string,\n    maxTweets = 200,\n  ): AsyncGenerator<Tweet, void> {\n    return getTweetsByUserId(userId, maxTweets, this.auth);\n  }\n\n  /**\n   * Fetches tweets and replies from a Twitter user.\n   * @param user The user whose tweets should be returned.\n   * @param maxTweets The maximum number of tweets to return. Defaults to `200`.\n   * @returns An {@link AsyncGenerator} of tweets from the provided user.\n   */\n  public getTweetsAndReplies(\n    user: string,\n    maxTweets = 200,\n  ): AsyncGenerator<Tweet> {\n    return getTweetsAndReplies(user, maxTweets, this.auth);\n  }\n\n  /**\n   * Fetches tweets and replies from a Twitter user using their ID.\n   * @param userId The user whose tweets should be returned.\n   * @param maxTweets The maximum number of tweets to return. Defaults to `200`.\n   * @returns An {@link AsyncGenerator} of tweets from the provided user.\n   */\n  public getTweetsAndRepliesByUserId(\n    userId: string,\n    maxTweets = 200,\n  ): AsyncGenerator<Tweet, void> {\n    return getTweetsAndRepliesByUserId(userId, maxTweets, this.auth);\n  }\n\n  /**\n   * Fetches the first tweet matching the given query.\n   *\n   * Example:\n   * ```js\n   * const timeline = scraper.getTweets('user', 200);\n   * const retweet = await scraper.getTweetWhere(timeline, { isRetweet: true });\n   * ```\n   * @param tweets The {@link AsyncIterable} of tweets to search through.\n   * @param query A query to test **all** tweets against. This may be either an\n   * object of key/value pairs or a predicate. If this query is an object, all\n   * key/value pairs must match a {@link Tweet} for it to be returned. If this query\n   * is a predicate, it must resolve to `true` for a {@link Tweet} to be returned.\n   * - All keys are optional.\n   * - If specified, the key must be implemented by that of {@link Tweet}.\n   */\n  public getTweetWhere(\n    tweets: AsyncIterable<Tweet>,\n    query: TweetQuery,\n  ): Promise<Tweet | null> {\n    return getTweetWhere(tweets, query);\n  }\n\n  /**\n   * Fetches all tweets matching the given query.\n   *\n   * Example:\n   * ```js\n   * const timeline = scraper.getTweets('user', 200);\n   * const retweets = await scraper.getTweetsWhere(timeline, { isRetweet: true });\n   * ```\n   * @param tweets The {@link AsyncIterable} of tweets to search through.\n   * @param query A query to test **all** tweets against. This may be either an\n   * object of key/value pairs or a predicate. If this query is an object, all\n   * key/value pairs must match a {@link Tweet} for it to be returned. If this query\n   * is a predicate, it must resolve to `true` for a {@link Tweet} to be returned.\n   * - All keys are optional.\n   * - If specified, the key must be implemented by that of {@link Tweet}.\n   */\n  public getTweetsWhere(\n    tweets: AsyncIterable<Tweet>,\n    query: TweetQuery,\n  ): Promise<Tweet[]> {\n    return getTweetsWhere(tweets, query);\n  }\n\n  /**\n   * Fetches the most recent tweet from a Twitter user.\n   * @param user The user whose latest tweet should be returned.\n   * @param includeRetweets Whether or not to include retweets. Defaults to `false`.\n   * @returns The {@link Tweet} object or `null`/`undefined` if it couldn't be fetched.\n   */\n  public getLatestTweet(\n    user: string,\n    includeRetweets = false,\n    max = 200,\n  ): Promise<Tweet | null | void> {\n    return getLatestTweet(user, includeRetweets, max, this.auth);\n  }\n\n  /**\n   * Fetches a single tweet.\n   * @param id The ID of the tweet to fetch.\n   * @returns The {@link Tweet} object, or `null` if it couldn't be fetched.\n   */\n  public getTweet(id: string): Promise<Tweet | null> {\n    if (this.auth instanceof TwitterUserAuth) {\n      return getTweet(id, this.auth);\n    } else {\n      return getTweetAnonymous(id, this.auth);\n    }\n  }\n\n  /**\n   * Retrieves the direct message inbox for the authenticated user.\n   *\n   * @return A promise that resolves to an object representing the direct message inbox.\n   */\n  public getDmInbox(): Promise<DmInbox> {\n    return getDmInbox(this.auth);\n  }\n\n  /**\n   * Retrieves the direct message conversation for the specified conversation ID.\n   *\n   * @param conversationId - The unique identifier of the DM conversation to retrieve.\n   * @param cursor - Use `maxId` to get messages before a message ID (older messages), or `minId` to get messages after a message ID (newer messages).\n   * @return A promise that resolves to the timeline of the DM conversation.\n   */\n  public getDmConversation(\n    conversationId: string,\n    cursor?: DmCursorOptions,\n  ): Promise<DmConversationTimeline> {\n    return getDmConversation(conversationId, cursor, this.auth);\n  }\n\n  /**\n   * Retrieves direct messages from a specific conversation.\n   *\n   * @param conversationId - The unique identifier of the conversation to fetch messages from.\n   * @param [maxMessages=20] - The maximum number of messages to retrieve per request.\n   * @param cursor - Use `maxId` to get messages before a message ID (older messages), or `minId` to get messages after a message ID (newer messages).\n   * @returns An {@link AsyncGenerator} of messages from the provided conversation.\n   */\n  public getDmMessages(\n    conversationId: string,\n    maxMessages = 20,\n    cursor?: DmCursorOptions,\n  ): AsyncGenerator<DmMessageEntry, void> {\n    return getDmMessages(conversationId, maxMessages, cursor, this.auth);\n  }\n\n  /**\n   * Retrieves a list of direct message conversations for a specific user based on their user ID.\n   *\n   * @param inbox - The DM inbox containing all available conversations.\n   * @param userId - The unique identifier of the user whose DM conversations are to be retrieved.\n   * @return An array of DM conversations associated with the specified user ID.\n   */\n  public findDmConversationsByUserId(\n    inbox: DmInbox,\n    userId: string,\n  ): DmConversation[] {\n    return findDmConversationsByUserId(inbox, userId);\n  }\n\n  /**\n   * Returns if the scraper has a guest token. The token may not be valid.\n   * @returns `true` if the scraper has a guest token; otherwise `false`.\n   */\n  public hasGuestToken(): boolean {\n    return this.auth.hasToken() || this.authTrends.hasToken();\n  }\n\n  /**\n   * Returns if the scraper is logged in as a real user.\n   * @returns `true` if the scraper is logged in with a real user account; otherwise `false`.\n   */\n  public async isLoggedIn(): Promise<boolean> {\n    return (\n      (await this.auth.isLoggedIn()) && (await this.authTrends.isLoggedIn())\n    );\n  }\n\n  /**\n   * Login to Twitter as a real Twitter account. This enables running\n   * searches.\n   * @param username The username of the Twitter account to login with.\n   * @param password The password of the Twitter account to login with.\n   * @param email The email to log in with, if you have email confirmation enabled.\n   * @param twoFactorSecret The secret to generate two factor authentication tokens with, if you have two factor authentication enabled.\n   */\n  public async login(\n    username: string,\n    password: string,\n    email?: string,\n    twoFactorSecret?: string,\n  ): Promise<void> {\n    // Swap in a real authorizer for all requests\n    const userAuth = new TwitterUserAuth(this.token, this.getAuthOptions());\n    await userAuth.login(username, password, email, twoFactorSecret);\n    this.auth = userAuth;\n    this.authTrends = userAuth;\n  }\n\n  /**\n   * Log out of Twitter.\n   */\n  public async logout(): Promise<void> {\n    await this.auth.logout();\n    await this.authTrends.logout();\n\n    // Swap in guest authorizers for all requests\n    this.useGuestAuth();\n  }\n\n  /**\n   * Retrieves all cookies for the current session.\n   * @returns All cookies for the current session.\n   */\n  public async getCookies(): Promise<Cookie[]> {\n    return await this.authTrends\n      .cookieJar()\n      .getCookies(\n        typeof document !== 'undefined' ? document.location.toString() : twUrl,\n      );\n  }\n\n  /**\n   * Set cookies for the current session.\n   * @param cookies The cookies to set for the current session.\n   */\n  public async setCookies(cookies: (string | Cookie)[]): Promise<void> {\n    const userAuth = new TwitterUserAuth(this.token, this.getAuthOptions());\n    for (const cookie of cookies) {\n      await userAuth.cookieJar().setCookie(cookie, twUrl);\n    }\n\n    this.auth = userAuth;\n    this.authTrends = userAuth;\n  }\n\n  /**\n   * Clear all cookies for the current session.\n   */\n  public async clearCookies(): Promise<void> {\n    await this.auth.cookieJar().removeAllCookies();\n    await this.authTrends.cookieJar().removeAllCookies();\n  }\n\n  /**\n   * Sets the optional cookie to be used in requests.\n   * @param _cookie The cookie to be used in requests.\n   * @deprecated This function no longer represents any part of Twitter's auth flow.\n   * @returns This scraper instance.\n   */\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  public withCookie(_cookie: string): Scraper {\n    console.warn(\n      'Warning: Scraper#withCookie is deprecated and will be removed in a later version. Use Scraper#login or Scraper#setCookies instead.',\n    );\n    return this;\n  }\n\n  /**\n   * Sets the optional CSRF token to be used in requests.\n   * @param _token The CSRF token to be used in requests.\n   * @deprecated This function no longer represents any part of Twitter's auth flow.\n   * @returns This scraper instance.\n   */\n  // eslint-disable-next-line @typescript-eslint/no-unused-vars\n  public withXCsrfToken(_token: string): Scraper {\n    console.warn(\n      'Warning: Scraper#withXCsrfToken is deprecated and will be removed in a later version.',\n    );\n    return this;\n  }\n\n  private getAuthOptions(): Partial<TwitterAuthOptions> {\n    return {\n      fetch: this.options?.fetch,\n      transform: this.options?.transform,\n      rateLimitStrategy: this.options?.rateLimitStrategy,\n      experimental: {\n        xClientTransactionId: this.options?.experimental?.xClientTransactionId,\n        xpff: this.options?.experimental?.xpff,\n      },\n    };\n  }\n\n  private handleResponse<T>(res: RequestApiResult<T>): T {\n    if (!res.success) {\n      throw res.err;\n    }\n\n    return res.value;\n  }\n}\n"]}